OpenWeatherWidget/widget/src/show_popup.rs

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()
}