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 { Task::none() } fn view(&self) -> iced::Element { // 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() }