use owm_rs::free_api_v25::current::WeatherResponse; use owm_widg_config::config::Config; use slint::{LogicalPosition, LogicalSize, SharedString}; use i_slint_backend_winit::{ Backend, winit::{ platform::x11::{WindowAttributesExtX11, WindowType}, window::WindowAttributes, }, }; slint::include_modules!(); fn unit_symbol(units: &str) -> char { match units { "metric" => 'C', "imperial" => 'F', "standard" => 'K', _ => '?', } } pub fn show_popup(resp: &WeatherResponse, cfg: &Config) -> Result<(), Box> { // Closure that adjusts winit WindowAttributes before Slint creates the window. let window_attrs = |attrs: WindowAttributes| { // Mark the X11 window as a DOCK so the WM treats it as a top bar/panel. attrs.with_x11_window_type(vec![WindowType::Dock]) }; // Build a Slint backend that applies this attribute hook to all windows. let backend = Backend::builder() .with_window_attributes_hook(window_attrs) // Register the hook .build()?; // Construct backend // Activate this customized backend for all Slint window creation and events. slint::platform::set_platform(Box::new(backend))?; let ui = MainWindow::new()?; let city = resp.name.clone().unwrap_or_else(|| "Unknown".into()); let country = resp .sys .as_ref() .and_then(|s| s.country.clone()) .unwrap_or_default(); let (weather_main, weather_description, icon_code) = resp.weather.first().map_or_else( || ("N/A".into(), "N/A".into(), String::new()), |w| (w.main.clone(), w.description.clone(), w.icon.clone()), ); let temp = resp.main.as_ref().and_then(|m| m.temp).unwrap_or(0.0); let temperature = format!("{temp:.1}"); let unit_symbol = unit_symbol(cfg.query_params.units.as_str()); 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_symbol)); #[allow(clippy::cast_precision_loss)] ui.window().set_size(LogicalSize::new(300.0, 124.0)); // Position the window (bar) at the top-left. ui.window().set_position(LogicalPosition::new(0.0, 0.0)); 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() }