205 lines
6.1 KiB
Rust
205 lines
6.1 KiB
Rust
use iced::{
|
|
Alignment, Background, Border, Color, Font, Length, Point, Settings, Shadow, Size, Task,
|
|
Vector,
|
|
alignment::{Horizontal, Vertical},
|
|
application::Appearance,
|
|
border,
|
|
font::Family,
|
|
widget::{Column, Container, Row, Text, container},
|
|
window,
|
|
};
|
|
use owm_rs::free_api_v25::current::WeatherResponse;
|
|
use owm_widg_config::config::Config;
|
|
|
|
#[derive(Debug, Clone)]
|
|
enum Message {}
|
|
|
|
struct WeatherPopup {
|
|
resp: WeatherResponse,
|
|
conf: Config,
|
|
}
|
|
|
|
impl WeatherPopup {
|
|
fn new(resp: WeatherResponse, conf: Config) -> Self {
|
|
Self { resp, conf }
|
|
}
|
|
|
|
fn update(&mut self, _message: Message) -> Task<Message> {
|
|
Task::none()
|
|
}
|
|
|
|
fn view(&self) -> iced::Element<Message> {
|
|
// Extract data
|
|
let city = self.resp.name.clone().unwrap_or_else(|| "Unknown".into());
|
|
let country = self
|
|
.resp
|
|
.sys
|
|
.as_ref()
|
|
.and_then(|s| s.country.clone())
|
|
.unwrap_or_default();
|
|
let (main, desc, icon) = self
|
|
.resp
|
|
.weather
|
|
.first()
|
|
.map(|w| (w.main.clone(), w.description.clone(), w.icon.clone()))
|
|
.unwrap_or(("N/A".into(), "N/A".into(), String::new()));
|
|
let temp = self.resp.main.as_ref().and_then(|m| m.temp).unwrap_or(0.0);
|
|
let temperature = format!("{temp:.1}");
|
|
let unit = match self.conf.query_params.units.as_str() {
|
|
"metric" => 'C',
|
|
"imperial" => 'F',
|
|
"standard" => 'K',
|
|
_ => '?',
|
|
};
|
|
|
|
let font = Font {
|
|
family: Family::Name("IosevkaTermSlab Nerd Font Mono"),
|
|
..Font::DEFAULT
|
|
};
|
|
|
|
// Top layout
|
|
let icon_text = Text::new(icon_to_nerd_font(&icon))
|
|
.font(font)
|
|
.size(88)
|
|
.height(88)
|
|
.width(88)
|
|
.align_x(Horizontal::Left)
|
|
.align_y(Vertical::Center)
|
|
.color(Color::WHITE);
|
|
let icon_block = Column::new().push(icon_text).height(88).width(88);
|
|
|
|
let location_text = Text::new(format!("{city}, {country}"))
|
|
.font(font)
|
|
.size(16)
|
|
.color(Color::WHITE)
|
|
.align_x(Horizontal::Right)
|
|
.width(192)
|
|
.height(26);
|
|
|
|
let temp_text = Text::new(temperature)
|
|
.font(font)
|
|
.size(46)
|
|
.align_x(Horizontal::Right)
|
|
.align_y(Vertical::Center)
|
|
.height(52)
|
|
.width(Length::Fill)
|
|
.color(Color::WHITE);
|
|
let temp_val_column = Column::new().push(temp_text).height(52).width(Length::Fill);
|
|
let degree_text = Text::new("o")
|
|
.font(font)
|
|
.size(16)
|
|
.color(Color::WHITE)
|
|
.height(23)
|
|
.align_y(Vertical::Bottom);
|
|
let unit_text = Text::new(format!("{unit}"))
|
|
.font(font)
|
|
.size(16)
|
|
.color(Color::WHITE)
|
|
.height(23)
|
|
.align_y(Vertical::Bottom);
|
|
let unit_block = Column::with_children(vec![
|
|
Row::new().push(degree_text).height(23).into(),
|
|
Row::new().push(unit_text).height(23).into(),
|
|
]);
|
|
|
|
let temp_block = Row::new()
|
|
.push(temp_val_column)
|
|
.push(Column::new().push(unit_block).height(52))
|
|
.align_y(Vertical::Center)
|
|
.height(52)
|
|
.width(192);
|
|
|
|
let right_block = Column::new()
|
|
.push(location_text)
|
|
.push(temp_block)
|
|
.width(192)
|
|
.height(Length::Fill);
|
|
|
|
let top_row = Row::new()
|
|
.push(icon_block)
|
|
.push(right_block)
|
|
.width(Length::Fill)
|
|
.height(88);
|
|
|
|
// Bottom layout
|
|
let main_text = Text::new(main)
|
|
.font(font)
|
|
.size(28)
|
|
.color(Color::WHITE)
|
|
.align_y(Vertical::Top)
|
|
.align_x(Horizontal::Left)
|
|
.width(Length::FillPortion(3));
|
|
|
|
let desc_text = Text::new(desc)
|
|
.font(font)
|
|
.size(16)
|
|
.align_x(Horizontal::Right)
|
|
.align_y(Vertical::Top)
|
|
.color(Color::from_rgba(1.0, 1.0, 1.0, 0.8))
|
|
.width(Length::FillPortion(4));
|
|
|
|
let bottom_row = Row::new()
|
|
.push(main_text)
|
|
.push(desc_text)
|
|
.width(Length::Fill)
|
|
.height(52);
|
|
|
|
// Combine
|
|
let content = Column::new()
|
|
.push(top_row)
|
|
.push(bottom_row)
|
|
.spacing(6)
|
|
.width(Length::Fill)
|
|
.height(Length::Fill);
|
|
|
|
container(content)
|
|
.style(|_theme| iced::widget::container::Style {
|
|
background: Some(Background::from(Color::from_rgba(0.20, 0.43, 0.55, 0.25))),
|
|
border: Border {
|
|
color: Color::TRANSPARENT,
|
|
width: 0.0,
|
|
radius: border::Radius::from(10.0),
|
|
},
|
|
shadow: Shadow::default(),
|
|
text_color: Some(Color::WHITE),
|
|
})
|
|
.padding(10)
|
|
.into()
|
|
}
|
|
}
|
|
|
|
pub fn show_popup(resp: WeatherResponse, conf: Config) -> iced::Result {
|
|
iced::application("Weather Popup", WeatherPopup::update, WeatherPopup::view)
|
|
.window(window::Settings {
|
|
size: Size {
|
|
width: 300.0,
|
|
height: 150.0,
|
|
},
|
|
position: window::Position::Specific(Point { x: 20.0, y: 20.0 }),
|
|
decorations: false,
|
|
..window::Settings::default()
|
|
})
|
|
.style(|_, _| Appearance {
|
|
background_color: Color::TRANSPARENT,
|
|
text_color: Color::WHITE,
|
|
})
|
|
.run_with(move || (WeatherPopup::new(resp, conf), Task::none()))
|
|
}
|
|
|
|
fn icon_to_nerd_font(code: &str) -> String {
|
|
match code {
|
|
"01d" => "",
|
|
"01n" => "",
|
|
"02d" | "02n" => "",
|
|
"03d" | "03n" => "",
|
|
"04d" | "04n" => "",
|
|
"09d" | "09n" => "",
|
|
"10d" | "10n" => "",
|
|
"11d" | "11n" => "",
|
|
"13d" | "13n" => "",
|
|
"50d" | "50n" => "",
|
|
_ => "",
|
|
}
|
|
.into()
|
|
}
|