From 4fe5878d364b7467efd7357f5b2a523acbb0fb5b Mon Sep 17 00:00:00 2001 From: Candifloss Date: Tue, 4 Nov 2025 20:05:59 +0530 Subject: [PATCH] Dynamic values - Pass values to display in Slint UI - Actual weather data instead of fixed dummy data --- widget/Cargo.toml | 5 ++- widget/build.rs | 3 ++ widget/src/main.rs | 77 ++++++++++++++++++++++++------------ widget/ui/widget-popup.slint | 55 +++++++++++++++----------- 4 files changed, 88 insertions(+), 52 deletions(-) create mode 100644 widget/build.rs diff --git a/widget/Cargo.toml b/widget/Cargo.toml index 6caf859..94dc2a9 100644 --- a/widget/Cargo.toml +++ b/widget/Cargo.toml @@ -2,6 +2,7 @@ name = "openweatherwidget" version = "0.0.1" edition = "2024" +build = "build.rs" [dependencies] owm-rs = { git = "https://git.candifloss.cc/candifloss/OpenWeatherMapSDK.git" } @@ -10,7 +11,7 @@ reqwest = {version = "0.12.23", features = ["blocking", "json"] } toml = "0.9.7" dirs = "6.0.0" serde_json = "1.0.145" -slint = "1.13" +slint = "1.14.1" [build-dependencies] -slint-build = "1.13.1" \ No newline at end of file +slint-build = "1.14.1" diff --git a/widget/build.rs b/widget/build.rs new file mode 100644 index 0000000..2559b5e --- /dev/null +++ b/widget/build.rs @@ -0,0 +1,3 @@ +fn main() { + slint_build::compile("ui/widget-popup.slint").unwrap(); +} \ No newline at end of file diff --git a/widget/src/main.rs b/widget/src/main.rs index 8923086..a4350be 100644 --- a/widget/src/main.rs +++ b/widget/src/main.rs @@ -2,6 +2,9 @@ use owm_rs::free_api_v25::current::WeatherResponse; use owm_widg_config::config::Config; use owm_widg_config::general::ApiVersion; use std::fs; +use slint::SharedString; + +slint::include_modules!(); fn main() -> Result<(), Box> { let path = dirs::config_dir() @@ -11,12 +14,10 @@ fn main() -> Result<(), Box> { let toml_str = fs::read_to_string(&path) .map_err(|_| format!("Failed to read config: {}", path.display()))?; - // Deserialize whole config let cfg: Config = toml::from_str(&toml_str)?; match cfg.general.api_version() { ApiVersion::Free25 => { - // Read json data from cache file let cache_path = dirs::cache_dir() .ok_or("No cache dir found")? .join("candydesktop/owm_widget.json"); @@ -24,35 +25,59 @@ fn main() -> Result<(), Box> { let json_data = fs::read_to_string(&cache_path)?; let resp: WeatherResponse = serde_json::from_str(&json_data)?; - // Print city and country - if let Some(city) = &resp.name { - print!("City: {city}"); - if let Some(sys) = &resp.sys - && let Some(country) = &sys.country - { - print!(", {country}"); - } + let city = resp.name.clone().unwrap_or_else(|| "Unknown".into()); + let country = resp + .sys + .as_ref() + .and_then(|s| s.country.clone()) + .unwrap_or_else(|| "".into()); - println!(); - } + let (weather_main, weather_description, icon) = if let Some(w) = resp.weather.first() { + ( + w.main.clone(), + w.description.clone(), + w.icon.clone(), + ) + } else { + ("N/A".into(), "N/A".into(), "".into()) + }; - // Print weather description - if let Some(weather) = resp.weather.first() { - println!("Weather: {}", weather.main); - println!("Icon: {}", weather.icon); - } + let temp = resp.main.as_ref().and_then(|m| m.temp).unwrap_or(0.0); + let temperature = format!("{temp:.1}"); + let unit = "C"; - // Print temperature - if let Some(main_data) = &resp.main - && let Some(temp) = main_data.temp - { - println!("Temperature: {temp:.1}°C"); - } - } - other => { - return Err(format!("Unsupported api_version: {other:?}").into()); + // Create and show the UI + 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))); + ui.set_temperature(SharedString::from(temperature)); + ui.set_unit(SharedString::from(unit)); + + ui.run()?; } + other => return Err(format!("Unsupported api_version: {other:?}").into()), } 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(), + } + .into() +} diff --git a/widget/ui/widget-popup.slint b/widget/ui/widget-popup.slint index 47cf238..e1ce605 100644 --- a/widget/ui/widget-popup.slint +++ b/widget/ui/widget-popup.slint @@ -1,15 +1,25 @@ 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: 120px; + 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, #246a8ac9, #c5edff75); + background: @linear-gradient(160deg, #236a8bc7, #c5edff5c); drop-shadow-blur: 15px; drop-shadow-color: #00000066; @@ -20,10 +30,10 @@ export component MainWindow inherits Window { /* Top: City */ Text { - text: "Province of Turin"; + text: city + ", " + country; font-size: 14px; color: #fff; - font-family: "IosevkaTermSlab Nerd Font Mono"; + //font-family: horizontal-alignment: TextHorizontalAlignment.left; height: 18px; // Fixed height for consistent spacing font-weight: 600; @@ -32,14 +42,14 @@ export component MainWindow inherits Window { /* Middle: icon on left, temp on right */ HorizontalLayout { horizontal-stretch: 1.0; - height: 46px; // Fixed height for main content area + height: 50px; // Fixed height for main content area padding: 0px; /* Icon */ Text { - text: ""; - font-family: "IosevkaTermSlab Nerd Font Mono"; + text: weather_icon; font-size: 40px; + horizontal-alignment: TextHorizontalAlignment.left; color: #fff; vertical-alignment: TextVerticalAlignment.center; width: 46px; // Fixed width for consistent alignment @@ -55,10 +65,10 @@ export component MainWindow inherits Window { HorizontalLayout { spacing: 4px; // Tighter spacing for temperature elements padding: 0px; + //height: 50px; Text { - text: "124.4"; - font-family: "IosevkaTermSlab Nerd Font Mono"; + text: temperature; font-size: 40px; color: #fff; vertical-alignment: TextVerticalAlignment.center; @@ -66,28 +76,27 @@ export component MainWindow inherits Window { } VerticalLayout { - height: 46px; // Match temperature text height + height: 50px; // Match temperature text height padding: 0px; spacing: 0px; Text { text: "o"; - font-size: 20px; + font-size: 16px; color: #fff; - font-family: "IosevkaTermSlab Nerd Font Mono"; horizontal-alignment: TextHorizontalAlignment.left; - vertical-alignment: TextVerticalAlignment.bottom; - height: 20px; + vertical-alignment: TextVerticalAlignment.center; + height: 25px; font-weight: 800; } Text { - text: "F"; - font-size: 18px; + text: unit; + font-size: 16px; color: #fff; - font-family: "IosevkaTermSlab Nerd Font Mono"; horizontal-alignment: TextHorizontalAlignment.left; - vertical-alignment: TextVerticalAlignment.top; + vertical-alignment: TextVerticalAlignment.center; height: 20px; + width: 20px; font-weight: 800; } } @@ -103,10 +112,9 @@ export component MainWindow inherits Window { /* Summary */ Text { - text: "Thunderstorm"; + text: weather_main; font-size: 18px; color: #fff; - font-family: "IosevkaTermSlab Nerd Font Mono"; vertical-alignment: TextVerticalAlignment.top; horizontal-alignment: TextHorizontalAlignment.left; width: 40%; // Fixed percentage width for consistent layout @@ -115,12 +123,11 @@ export component MainWindow inherits Window { /* Description */ Text { - text: "Thunderstorm with heavy drizzle"; + text: weather_description; font-size: 12px; color: #ffffffcc; - font-family: "IosevkaTermSlab Nerd Font Mono"; - horizontal-alignment: TextHorizontalAlignment.left; - vertical-alignment: TextVerticalAlignment.bottom; + horizontal-alignment: TextHorizontalAlignment.right; + vertical-alignment: TextVerticalAlignment.top; horizontal-stretch: 1.0; wrap: word-wrap; height: 100%;