diff --git a/widget/build.rs.bkp b/widget/build.rs.bkp deleted file mode 100644 index cc515dc..0000000 --- a/widget/build.rs.bkp +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - slint_build::compile("ui/widget-popup.slint").unwrap(); -} diff --git a/widget/src/show_popup.rs b/widget/src/show_popup.rs index 0334f92..14ad16c 100644 --- a/widget/src/show_popup.rs +++ b/widget/src/show_popup.rs @@ -1,27 +1,26 @@ use iced::{ - Alignment, Background, Border, Color, Font, Gradient, Length, Point, Settings, Shadow, Size, - Task, Vector, + Alignment, Background, Border, Color, Font, Length, Point, Settings, Shadow, Size, Task, + Vector, alignment::{Horizontal, Vertical}, application::Appearance, border, font::Family, - gradient, - widget::{Column, Container, Row, Space, Text, column, container, container::Style, row}, + widget::{Column, Container, Row, Text, container}, window, }; use owm_rs::free_api_v25::current::WeatherResponse; -use owm_widg_config; +use owm_widg_config::config::Config; #[derive(Debug, Clone)] enum Message {} struct WeatherPopup { resp: WeatherResponse, - conf: owm_widg_config::config::Config, + conf: Config, } impl WeatherPopup { - fn new(resp: WeatherResponse, conf: owm_widg_config::config::Config) -> Self { + fn new(resp: WeatherResponse, conf: Config) -> Self { Self { resp, conf } } @@ -30,20 +29,20 @@ impl WeatherPopup { } fn view(&self) -> iced::Element { - // Data + // 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_else(|| String::new()); - let (weather_main, weather_description, icon_code) = - if let Some(w) = self.resp.weather.first() { - (w.main.clone(), w.description.clone(), w.icon.clone()) - } else { - ("N/A".into(), "N/A".into(), String::new()) - }; + .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() { @@ -53,204 +52,145 @@ impl WeatherPopup { _ => '?', }; - // UI - let default_font = "IosevkaTermSlab Nerd Font Mono"; - //row1col1 - let icon_text: Text = Text::new(icon_to_nerd_font(&icon_code)) - .font(Font { - family: Family::Name(default_font), - ..Font::DEFAULT - }) - .align_x(Horizontal::Left) - .size(60) - .color(Color::from_rgb(1.0, 1.0, 1.0)) - .into(); - let icon_column = Column::new().push(icon_text).width(Length::FillPortion(1)); - // row1col2row1 - let location_text: Text = Text::new(format!("{city}, {country}")) - .font(Font { - family: Family::Name(default_font), - ..Font::DEFAULT - }) - .size(16) - .color(Color::from_rgb(1.0, 1.0, 1.0)) + 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(80) + .height(Length::Fill) .width(Length::Fill) - .align_x(Horizontal::Right) - .into(); - let location_row = Column::new() - .push(location_text) + .align_x(Horizontal::Center) + .align_y(Vertical::Center) + .color(Color::WHITE); + let icon_block = Column::new() + .push(icon_text) + .height(Length::Fill) .width(Length::FillPortion(1)); - // row1col2row2col1 - let temp_val_text: Text = Text::new(format!("{temperature}")) - .font(Font { - family: Family::Name(default_font), - ..Font::DEFAULT - }) + let location_text = Text::new(format!("{city}, {country}")) + .font(font) + .size(16) + .color(Color::WHITE) + .align_x(Horizontal::Right) + .width(Length::Fill) + .height(Length::FillPortion(1)); + + let temp_text = Text::new(temperature) + .font(font) .size(40) - .color(Color::from_rgb(1.0, 1.0, 1.0)) - .into(); - let temp_val_col = Column::new() - .push(temp_val_text) - .height(40) - .width(Length::Fill); - - // row1col2row2col2row1 - let degree_symbol: Text = Text::new(format!("°")) - .font(Font { - family: Family::Name(default_font), - ..Font::DEFAULT - }) - .size(16) - .color(Color::from_rgb(1.0, 1.0, 1.0)) - .into(); - let degree_symbol_row: Row = - Row::new().push(degree_symbol).height(20).width(10).into(); - - // row1col2row2col2row2 - let unit_symbol: Text = Text::new(format!("{unit}")) - .font(Font { - family: Family::Name(default_font), - ..Font::DEFAULT - }) - .size(16) - .color(Color::from_rgb(1.0, 1.0, 1.0)) - .into(); - let unit_symbol_row: Row = - Row::new().push(unit_symbol).height(20).width(10).into(); - - // row1col2row2col2 - let temp_unit_col: Column = Column::new() - .push(degree_symbol_row) - .push(unit_symbol_row) - .height(40) - .width(10) - .into(); - - // row1col2row2 - let temp_row: Row = Row::new() - .push(temp_val_col) - .push(temp_unit_col) + .align_x(Horizontal::Right) + .align_y(Vertical::Center) + .height(Length::Fill) + .width(Length::Fill) + .color(Color::WHITE); + let temp_val_column = Column::new() + .push(temp_text) .height(Length::FillPortion(3)) .width(Length::Fill); - - let row1col2: Column = Column::new() - .push(location_row) - .push(temp_row) - .height(Length::Fill) - .width(Length::Fill) - .into(); - - // row2col1 - let weather_main_text: Text = Text::new(format!("{weather_main}")) - .font(Font { - family: Family::Name(default_font), - ..Font::DEFAULT - }) + let degree_text = Text::new("O").font(font).size(16).color(Color::WHITE); + let unit_text = Text::new(format!("{unit}")) + .font(font) .size(16) - .color(Color::from_rgb(1.0, 1.0, 1.0)) - .width(Length::Fill) - .into(); - let weather_main_col: Column = Column::new() - .push(weather_main_text) - .height(Length::Fill) - .width(Length::FillPortion(3)) - .into(); + .color(Color::WHITE); + let unit_block = Column::with_children(vec![ + Row::new().push(degree_text).height(Length::Fill).into(), + Row::new().push(unit_text).height(Length::Fill).into(), + ]); - // row2col1 - let weather_descr_text: Text = Text::new(format!("{weather_description}")) - .font(Font { - family: Family::Name(default_font), - ..Font::DEFAULT - }) + let temp_block = Row::new() + .push(temp_val_column) + .push(Column::new().push(unit_block).height(Length::Fill)) + .width(Length::Fill); + + let right_block = Column::new() + .push(location_text) + .push(temp_block) + .width(Length::Fill) + .height(Length::Fill); + + let top_row = Row::new() + .push(icon_block) + .push(right_block) + .width(Length::Fill) + .height(Length::FillPortion(8)); + + // Bottom layout + let main_text = Text::new(main) + .font(font) .size(16) - .color(Color::from_rgb(1.0, 1.0, 1.0)) - .width(Length::Fill) - .into(); - let weather_descr_col: Column = Column::new() - .push(weather_descr_text) - .height(Length::Fill) - .width(Length::FillPortion(4)) - .into(); + .color(Color::WHITE) + .width(Length::FillPortion(3)); - let row2: Row = Row::new() - .push(weather_main_col) - .push(weather_descr_col) - .height(Length::FillPortion(3)) - .width(Length::Fill) - .into(); + let desc_text = Text::new(desc) + .font(font) + .size(16) + .color(Color::from_rgba(1.0, 1.0, 1.0, 0.8)) + .width(Length::FillPortion(4)); - let row1: Row = Row::new() - .push(icon_column) - .push(row1col2) - .height(Length::FillPortion(8)) + let bottom_row = Row::new() + .push(main_text) + .push(desc_text) .width(Length::Fill) - .into(); + .height(Length::FillPortion(3)); - let whole_thing: Column = Column::new() - .push(row1) - .push(row2) - .height(Length::Fill) + // Combine + let content = Column::new() + .push(top_row) + .push(bottom_row) + .spacing(6) .width(Length::Fill) - .into(); + .height(Length::Fill); - container(whole_thing) - .style(|_theme| Style { + container(content) + .style(|_theme| iced::widget::container::Style { background: Some(Background::from(Color::from_rgba(0.20, 0.43, 0.55, 0.25))), - text_color: Some(Color::from_rgba(1.0, 1.0, 1.0, 0.25)), border: Border { color: Color::TRANSPARENT, width: 0.0, radius: border::Radius::from(10.0), }, - shadow: Shadow { - color: Color::TRANSPARENT, - offset: Vector { x: 0.0, y: 0.0 }, - blur_radius: 0.0, - }, + shadow: Shadow::default(), + text_color: Some(Color::WHITE), }) - .width(Length::Fill) - .height(Length::Fill) .padding(10) .into() } } -pub fn show_popup(resp: WeatherResponse, conf: owm_widg_config::config::Config) -> iced::Result { - iced::application( - "Weather Popup", // Title - WeatherPopup::update, - WeatherPopup::view, - ) - .window(window::Settings { - size: Size { - width: 300., - height: 150., - }, - position: window::Position::Specific(Point { x: 20., y: 20. }), - decorations: false, - ..window::Settings::default() - }) - .style(|_state, _theme| Appearance { - background_color: Color::TRANSPARENT, - text_color: Color::default(), - }) - .run_with(move || (WeatherPopup::new(resp, conf), Task::none())) +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())) } -/// Convert OWM icon codes (e.g. "01d", "09n") to Nerd Font weather glyphs. fn icon_to_nerd_font(code: &str) -> String { match code { - "01d" => "", // clear day - "01n" => "", // clear night - "02d" | "02n" => "", // few clouds - "03d" | "03n" => "", // scattered clouds - "04d" | "04n" => "", // broken clouds - "09d" | "09n" => "", // shower rain - "10d" | "10n" => "", // rain - "11d" | "11n" => "", // thunderstorm - "13d" | "13n" => "", // snow - "50d" | "50n" => "", // mist + "01d" => "", + "01n" => "", + "02d" | "02n" => "", + "03d" | "03n" => "", + "04d" | "04n" => "", + "09d" | "09n" => "", + "10d" | "10n" => "", + "11d" | "11n" => "", + "13d" | "13n" => "", + "50d" | "50n" => "", _ => "", } .into() diff --git a/widget/src/show_popup.rs.old b/widget/src/show_popup.rs.old deleted file mode 100644 index 3c3e50a..0000000 --- a/widget/src/show_popup.rs.old +++ /dev/null @@ -1,44 +0,0 @@ -use slint::SharedString; - -slint::include_modules!(); - -/// Create and show the UI window populated with weather data. -pub fn show_popup( - city: String, - country: String, - weather_main: String, - weather_description: String, - icon_code: String, - temperature: String, - unit: char, -) -> Result<(), Box> { - let ui = MainWindow::new()?; - ui.set_city(SharedString::from(city)); - ui.set_country(SharedString::from(country)); - ui.set_weather_main(SharedString::from(weather_main)); - ui.set_weather_description(SharedString::from(weather_description)); - ui.set_weather_icon(SharedString::from(icon_to_nerd_font(&icon_code))); - ui.set_temperature(SharedString::from(temperature)); - ui.set_unit(SharedString::from(unit)); - - ui.run()?; - Ok(()) -} - -/// Convert OWM icon codes (e.g. "01d", "09n") to Nerd Font weather glyphs. -fn icon_to_nerd_font(code: &str) -> String { - match code { - "01d" => "", // clear day - "01n" => "", // clear night - "02d" | "02n" => "", // few clouds - "03d" | "03n" => "", // scattered clouds - "04d" | "04n" => "", // broken clouds - "09d" | "09n" => "", // shower rain - "10d" | "10n" => "", // rain - "11d" | "11n" => "", // thunderstorm - "13d" | "13n" => "", // snow - "50d" | "50n" => "", // mist - _ => "", - } - .into() -} diff --git a/widget/ui/widget-popup.slint b/widget/ui/widget-popup.slint deleted file mode 100644 index e1ce605..0000000 --- a/widget/ui/widget-popup.slint +++ /dev/null @@ -1,139 +0,0 @@ -export component MainWindow inherits Window { - - in property city; - in property country; - in property weather_main; - in property weather_description; - in property weather_icon; - in property temperature; - in property unit; - - always-on-top: true; - no-frame: true; - width: 300px; - height: 124px; - background: transparent; - default-font-family: "IosevkaTermSlab Nerd Font Mono"; - - weather_popup := Rectangle { - width: 100%; - height: 100%; - border-radius: 12px; - background: @linear-gradient(160deg, #236a8bc7, #c5edff5c); - drop-shadow-blur: 15px; - drop-shadow-color: #00000066; - - VerticalLayout { - spacing: 2px; // Consistent vertical spacing - horizontal-stretch: 1.0; - padding: 12px; // Consistent padding throughout - - /* Top: City */ - Text { - text: city + ", " + country; - font-size: 14px; - color: #fff; - //font-family: - horizontal-alignment: TextHorizontalAlignment.left; - height: 18px; // Fixed height for consistent spacing - font-weight: 600; - } - - /* Middle: icon on left, temp on right */ - HorizontalLayout { - horizontal-stretch: 1.0; - height: 50px; // Fixed height for main content area - padding: 0px; - - /* Icon */ - Text { - text: weather_icon; - font-size: 40px; - horizontal-alignment: TextHorizontalAlignment.left; - color: #fff; - vertical-alignment: TextVerticalAlignment.center; - width: 46px; // Fixed width for consistent alignment - } - - /* Flexible spacer to help with alignment */ - Rectangle { - horizontal-stretch: 1.0; - background: transparent; - } - - /* Temperature group */ - HorizontalLayout { - spacing: 4px; // Tighter spacing for temperature elements - padding: 0px; - //height: 50px; - - Text { - text: temperature; - font-size: 40px; - color: #fff; - vertical-alignment: TextVerticalAlignment.center; - font-weight: 900; - } - - VerticalLayout { - height: 50px; // Match temperature text height - padding: 0px; - spacing: 0px; - - Text { - text: "o"; - font-size: 16px; - color: #fff; - horizontal-alignment: TextHorizontalAlignment.left; - vertical-alignment: TextVerticalAlignment.center; - height: 25px; - font-weight: 800; - } - Text { - text: unit; - font-size: 16px; - color: #fff; - horizontal-alignment: TextHorizontalAlignment.left; - vertical-alignment: TextVerticalAlignment.center; - height: 20px; - width: 20px; - font-weight: 800; - } - } - } - } - - /* Bottom: summary + description */ - HorizontalLayout { - spacing: 8px; - horizontal-stretch: 1.0; - height: 32px; - padding: 0px; - - /* Summary */ - Text { - text: weather_main; - font-size: 18px; - color: #fff; - vertical-alignment: TextVerticalAlignment.top; - horizontal-alignment: TextHorizontalAlignment.left; - width: 40%; // Fixed percentage width for consistent layout - font-weight: 800; - } - - /* Description */ - Text { - text: weather_description; - font-size: 12px; - color: #ffffffcc; - horizontal-alignment: TextHorizontalAlignment.right; - vertical-alignment: TextVerticalAlignment.top; - horizontal-stretch: 1.0; - wrap: word-wrap; - height: 100%; - font-weight: 600; - } - } - } - } -} \ No newline at end of file