diff --git a/Cargo.toml b/Cargo.toml index 157c59bc..a92f7778 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,7 +54,7 @@ tokio = ["iced_futures/tokio"] # Enables `smol` as the `executor::Default` on native platforms smol = ["iced_futures/smol"] # Enables querying system information -system = ["iced_winit/system"] +sysinfo = ["iced_winit/sysinfo"] # Enables broken "sRGB linear" blending to reproduce color management of the Web web-colors = ["iced_renderer/web-colors"] # Enables pixel snapping for crisp edges by default (can cause jitter!) diff --git a/core/src/window/event.rs b/core/src/window/event.rs index 48622b70..45d29179 100644 --- a/core/src/window/event.rs +++ b/core/src/window/event.rs @@ -1,4 +1,3 @@ -use crate::theme; use crate::time::Instant; use crate::{Point, Size}; @@ -72,7 +71,4 @@ pub enum Event { /// /// - **Wayland:** Not implemented. FilesHoveredLeft, - - /// The theme mode of the window has changed. - ThemeModeChanged(theme::Mode), } diff --git a/examples/events/src/main.rs b/examples/events/src/main.rs index 57b36f7c..cde7eac2 100644 --- a/examples/events/src/main.rs +++ b/examples/events/src/main.rs @@ -37,7 +37,7 @@ impl Events { } Message::EventOccurred(event) => { if let Event::Window(window::Event::CloseRequested) = event { - window::get_latest().and_then(window::close) + window::latest().and_then(window::close) } else { Task::none() } @@ -47,7 +47,7 @@ impl Events { Task::none() } - Message::Exit => window::get_latest().and_then(window::close), + Message::Exit => window::latest().and_then(window::close), } } diff --git a/examples/exit/src/main.rs b/examples/exit/src/main.rs index 767ff93f..567a0ce6 100644 --- a/examples/exit/src/main.rs +++ b/examples/exit/src/main.rs @@ -20,7 +20,7 @@ enum Message { impl Exit { fn update(&mut self, message: Message) -> Task { match message { - Message::Confirm => window::get_latest().and_then(window::close), + Message::Confirm => window::latest().and_then(window::close), Message::Exit => { self.show_confirm = true; diff --git a/examples/multi_window/src/main.rs b/examples/multi_window/src/main.rs index d299648e..42082f21 100644 --- a/examples/multi_window/src/main.rs +++ b/examples/multi_window/src/main.rs @@ -66,7 +66,7 @@ impl Example { return Task::none(); }; - window::get_position(*last_window) + window::position(*last_window) .then(|last_position| { let position = last_position.map_or( window::Position::Default, diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs index 61285b72..100eca94 100644 --- a/examples/screenshot/src/main.rs +++ b/examples/screenshot/src/main.rs @@ -49,7 +49,7 @@ impl Example { fn update(&mut self, message: Message) -> Task { match message { Message::Screenshot => { - return window::get_latest() + return window::latest() .and_then(window::screenshot) .map(Message::Screenshotted); } diff --git a/examples/system_information/Cargo.toml b/examples/system_information/Cargo.toml index 55e00d59..6c99e006 100644 --- a/examples/system_information/Cargo.toml +++ b/examples/system_information/Cargo.toml @@ -7,6 +7,6 @@ publish = false [dependencies] iced.workspace = true -iced.features = ["system"] +iced.features = ["sysinfo"] bytesize = "1.1" diff --git a/examples/system_information/src/main.rs b/examples/system_information/src/main.rs index 61bca4c8..4de4ab23 100644 --- a/examples/system_information/src/main.rs +++ b/examples/system_information/src/main.rs @@ -1,5 +1,6 @@ +use iced::system; use iced::widget::{button, center, column, text}; -use iced::{Element, Task, system}; +use iced::{Element, Task}; pub fn main() -> iced::Result { iced::application(Example::new, Example::update, Example::view).run() @@ -26,7 +27,7 @@ impl Example { fn new() -> (Self, Task) { ( Self::Loading, - system::fetch_information().map(Message::InformationReceived), + system::information().map(Message::InformationReceived), ) } diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 9c57487c..31f20ab6 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -151,7 +151,7 @@ impl Todos { widget::focus_next() } } - Message::ToggleFullscreen(mode) => window::get_latest() + Message::ToggleFullscreen(mode) => window::latest() .and_then(move |window| window::set_mode(window, mode)), Message::Loaded(_) => Command::none(), }; diff --git a/futures/src/event.rs b/futures/src/event.rs index bd75d82c..58bc9df2 100644 --- a/futures/src/event.rs +++ b/futures/src/event.rs @@ -37,6 +37,7 @@ where event: Event::Window(window::Event::RedrawRequested(_)), .. } + | subscription::Event::SystemThemeChanged(_) | subscription::Event::PlatformSpecific(_) => None, subscription::Event::Interaction { window, @@ -66,7 +67,8 @@ where event, status, } => f(event, status, window), - subscription::Event::PlatformSpecific(_) => None, + subscription::Event::SystemThemeChanged(_) + | subscription::Event::PlatformSpecific(_) => None, }) } diff --git a/futures/src/subscription.rs b/futures/src/subscription.rs index e347e81f..338b2622 100644 --- a/futures/src/subscription.rs +++ b/futures/src/subscription.rs @@ -4,6 +4,7 @@ mod tracker; pub use tracker::Tracker; use crate::core::event; +use crate::core::theme; use crate::core::window; use crate::futures::Stream; use crate::{BoxStream, MaybeSend}; @@ -27,6 +28,9 @@ pub enum Event { status: event::Status, }, + /// The system theme has changed. + SystemThemeChanged(theme::Mode), + /// A platform specific event. PlatformSpecific(PlatformSpecific), } @@ -422,7 +426,8 @@ where } } -pub(crate) fn filter_map(id: I, f: F) -> Subscription +/// Creatges a [`Subscription`] from a hashable id and a filter function. +pub fn filter_map(id: I, f: F) -> Subscription where I: Hash + 'static, F: Fn(Event) -> Option + MaybeSend + 'static, diff --git a/graphics/src/compositor.rs b/graphics/src/compositor.rs index 338d648f..e0c8eb46 100644 --- a/graphics/src/compositor.rs +++ b/graphics/src/compositor.rs @@ -59,7 +59,7 @@ pub trait Compositor: Sized { ); /// Returns [`Information`] used by this [`Compositor`]. - fn fetch_information(&self) -> Information; + fn information(&self) -> Information; /// Loads a font from its bytes. fn load_font(&mut self, font: Cow<'static, [u8]>) { @@ -178,7 +178,7 @@ impl Compositor for () { fn load_font(&mut self, _font: Cow<'static, [u8]>) {} - fn fetch_information(&self) -> Information { + fn information(&self) -> Information { Information { adapter: String::from("Null Renderer"), backend: String::from("Null"), diff --git a/renderer/src/fallback.rs b/renderer/src/fallback.rs index d8d04d45..d018a242 100644 --- a/renderer/src/fallback.rs +++ b/renderer/src/fallback.rs @@ -313,8 +313,8 @@ where delegate!(self, compositor, compositor.load_font(font)); } - fn fetch_information(&self) -> compositor::Information { - delegate!(self, compositor, compositor.fetch_information()) + fn information(&self) -> compositor::Information { + delegate!(self, compositor, compositor.information()) } fn present( diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 77e80b8d..457f723c 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -25,7 +25,6 @@ pub use iced_futures as futures; pub use task::Task; pub use user_interface::UserInterface; -use crate::core::theme; use crate::core::widget; use crate::futures::futures::channel::oneshot; @@ -57,9 +56,6 @@ pub enum Action { /// Run a system action. System(system::Action), - /// Change the default system theme mode of all windows. - ChangeTheme(theme::Mode), - /// Recreate all user interfaces and redraw all windows. Reload, @@ -86,7 +82,6 @@ impl Action { Action::Clipboard(action) => Err(Action::Clipboard(action)), Action::Window(action) => Err(Action::Window(action)), Action::System(action) => Err(Action::System(action)), - Action::ChangeTheme(mode) => Err(Action::ChangeTheme(mode)), Action::Reload => Err(Action::Reload), Action::Exit => Err(Action::Exit), } @@ -111,9 +106,6 @@ where } Action::Window(_) => write!(f, "Action::Window"), Action::System(action) => write!(f, "Action::System({action:?})"), - Action::ChangeTheme(mode) => { - write!(f, "Action::ChangeTheme({mode:?})") - } Action::Reload => write!(f, "Action::Reload"), Action::Exit => write!(f, "Action::Exit"), } diff --git a/runtime/src/system.rs b/runtime/src/system.rs index 8b0ec2d8..09f112f5 100644 --- a/runtime/src/system.rs +++ b/runtime/src/system.rs @@ -1,11 +1,20 @@ //! Access the native system. +use crate::core::theme; use crate::futures::futures::channel::oneshot; +use crate::futures::subscription::{self, Subscription}; +use crate::task::{self, Task}; /// An operation to be performed on the system. #[derive(Debug)] pub enum Action { - /// Query system information and produce `T` with the result. - QueryInformation(oneshot::Sender), + /// Send available system information. + GetInformation(oneshot::Sender), + + /// Send the current system theme mode. + GetTheme(oneshot::Sender), + + /// Notify to the runtime that the system theme has changed. + NotifyTheme(theme::Mode), } /// Contains information about the system (e.g. system name, processor, memory, graphics adapter). @@ -37,3 +46,29 @@ pub struct Information { /// Model information for the active graphics adapter pub graphics_adapter: String, } + +/// Returns available system information. +pub fn information() -> Task { + task::oneshot(|channel| { + crate::Action::System(Action::GetInformation(channel)) + }) +} + +/// Returns the current system theme. +pub fn theme() -> Task { + task::oneshot(|sender| crate::Action::System(Action::GetTheme(sender))) +} + +/// Subscribes to system theme changes. +pub fn theme_changes() -> Subscription { + #[derive(Hash)] + struct ThemeChanges; + + subscription::filter_map(ThemeChanges, |event| { + let subscription::Event::SystemThemeChanged(mode) = event else { + return None; + }; + + Some(mode) + }) +} diff --git a/runtime/src/window.rs b/runtime/src/window.rs index ccd8721b..dde540b8 100644 --- a/runtime/src/window.rs +++ b/runtime/src/window.rs @@ -266,12 +266,12 @@ pub fn close(id: Id) -> Task { } /// Gets the window [`Id`] of the oldest window. -pub fn get_oldest() -> Task> { +pub fn oldest() -> Task> { task::oneshot(|channel| crate::Action::Window(Action::GetOldest(channel))) } /// Gets the window [`Id`] of the latest window. -pub fn get_latest() -> Task> { +pub fn latest() -> Task> { task::oneshot(|channel| crate::Action::Window(Action::GetLatest(channel))) } @@ -315,14 +315,14 @@ pub fn set_resize_increments(id: Id, increments: Option) -> Task { } /// Get the window's size in logical dimensions. -pub fn get_size(id: Id) -> Task { +pub fn size(id: Id) -> Task { task::oneshot(move |channel| { crate::Action::Window(Action::GetSize(id, channel)) }) } /// Gets the maximized state of the window with the given [`Id`]. -pub fn get_maximized(id: Id) -> Task { +pub fn is_maximized(id: Id) -> Task { task::oneshot(move |channel| { crate::Action::Window(Action::GetMaximized(id, channel)) }) @@ -334,7 +334,7 @@ pub fn maximize(id: Id, maximized: bool) -> Task { } /// Gets the minimized state of the window with the given [`Id`]. -pub fn get_minimized(id: Id) -> Task> { +pub fn is_minimized(id: Id) -> Task> { task::oneshot(move |channel| { crate::Action::Window(Action::GetMinimized(id, channel)) }) @@ -346,14 +346,14 @@ pub fn minimize(id: Id, minimized: bool) -> Task { } /// Gets the position in logical coordinates of the window with the given [`Id`]. -pub fn get_position(id: Id) -> Task> { +pub fn position(id: Id) -> Task> { task::oneshot(move |channel| { crate::Action::Window(Action::GetPosition(id, channel)) }) } /// Gets the scale factor of the window with the given [`Id`]. -pub fn get_scale_factor(id: Id) -> Task { +pub fn scale_factor(id: Id) -> Task { task::oneshot(move |channel| { crate::Action::Window(Action::GetScaleFactor(id, channel)) }) @@ -365,7 +365,7 @@ pub fn move_to(id: Id, position: Point) -> Task { } /// Gets the current [`Mode`] of the window. -pub fn get_mode(id: Id) -> Task { +pub fn mode(id: Id) -> Task { task::oneshot(move |channel| { crate::Action::Window(Action::GetMode(id, channel)) }) @@ -426,7 +426,7 @@ pub fn show_system_menu(id: Id) -> Task { /// Gets an identifier unique to the window, provided by the underlying windowing system. This is /// not to be confused with [`Id`]. -pub fn get_raw_id(id: Id) -> Task { +pub fn raw_id(id: Id) -> Task { task::oneshot(|channel| { crate::Action::Window(Action::GetRawId(id, channel)) }) diff --git a/src/lib.rs b/src/lib.rs index 4ced8d30..8dfad67d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -587,11 +587,12 @@ pub mod mouse { }; } -#[cfg(feature = "system")] pub mod system { //! Retrieve system information. - pub use crate::runtime::system::Information; - pub use crate::shell::system::*; + pub use crate::runtime::system::{theme, theme_changes}; + + #[cfg(feature = "sysinfo")] + pub use crate::runtime::system::{Information, information}; } pub mod overlay { diff --git a/tiny_skia/src/window/compositor.rs b/tiny_skia/src/window/compositor.rs index 321a003f..02cace72 100644 --- a/tiny_skia/src/window/compositor.rs +++ b/tiny_skia/src/window/compositor.rs @@ -100,7 +100,7 @@ impl crate::graphics::Compositor for Compositor { surface.layer_stack.clear(); } - fn fetch_information(&self) -> Information { + fn information(&self) -> Information { Information { adapter: String::from("CPU"), backend: String::from("tiny-skia"), diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs index a8b161a8..c912ef1d 100644 --- a/wgpu/src/window/compositor.rs +++ b/wgpu/src/window/compositor.rs @@ -332,7 +332,7 @@ impl graphics::Compositor for Compositor { ); } - fn fetch_information(&self) -> compositor::Information { + fn information(&self) -> compositor::Information { let information = self.adapter.get_info(); compositor::Information { diff --git a/winit/Cargo.toml b/winit/Cargo.toml index 85390a0d..85f235f7 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -16,7 +16,7 @@ workspace = true [features] default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"] debug = ["iced_debug/enable"] -system = ["sysinfo"] +sysinfo = ["dep:sysinfo"] program = [] x11 = ["winit/x11"] wayland = ["winit/wayland"] diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index 654cfb54..250918ab 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -319,9 +319,6 @@ pub fn window_event( Some(Event::Window(window::Event::Moved(Point::new(x, y)))) } - WindowEvent::ThemeChanged(theme) => Some(Event::Window( - window::Event::ThemeModeChanged(theme_mode(theme)), - )), _ => None, } } diff --git a/winit/src/lib.rs b/winit/src/lib.rs index 970366c7..90a4529b 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -29,9 +29,6 @@ pub use winit; pub mod clipboard; pub mod conversion; -#[cfg(feature = "system")] -pub mod system; - mod error; mod proxy; mod window; @@ -53,6 +50,7 @@ use crate::futures::futures::{Future, StreamExt}; use crate::futures::subscription; use crate::futures::{Executor, Runtime}; use crate::graphics::{Compositor, compositor}; +use crate::runtime::system; use crate::runtime::user_interface::{self, UserInterface}; use crate::runtime::{Action, Task}; @@ -126,6 +124,7 @@ where let (event_sender, event_receiver) = mpsc::unbounded(); let (control_sender, control_receiver) = mpsc::unbounded(); + let (system_theme_sender, system_theme_receiver) = oneshot::channel(); let instance = Box::pin(run_instance::

( program, @@ -136,6 +135,7 @@ where is_daemon, graphics_settings, settings.fonts, + system_theme_receiver, )); let context = task::Context::from_waker(task::noop_waker_ref()); @@ -147,6 +147,7 @@ where sender: mpsc::UnboundedSender>>, receiver: mpsc::UnboundedReceiver, error: Option, + system_theme: Option>, #[cfg(target_arch = "wasm32")] canvas: Option, @@ -159,6 +160,7 @@ where sender: event_sender, receiver: control_receiver, error: None, + system_theme: Some(system_theme_sender), #[cfg(target_arch = "wasm32")] canvas: None, @@ -172,10 +174,15 @@ where Message: std::fmt::Debug, F: Future, { - fn resumed( - &mut self, - _event_loop: &winit::event_loop::ActiveEventLoop, - ) { + fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { + if let Some(sender) = self.system_theme.take() { + let _ = sender.send( + event_loop + .system_theme() + .map(conversion::theme_mode) + .unwrap_or_default(), + ); + } } fn new_events( @@ -498,6 +505,7 @@ async fn run_instance

( is_daemon: bool, graphics_settings: graphics::Settings, default_fonts: Vec>, + mut _system_theme: oneshot::Receiver, ) where P: Program + 'static, P::Theme: theme::Base, @@ -518,7 +526,7 @@ async fn run_instance

( let mut clipboard = Clipboard::unconnected(); #[cfg(all(feature = "linux-theme-detection", target_os = "linux"))] - let system_theme = { + let mut system_theme = { let to_mode = |color_scheme| match color_scheme { mundy::ColorScheme::NoPreference => theme::Mode::None, mundy::ColorScheme::Light => theme::Mode::Light, @@ -528,7 +536,9 @@ async fn run_instance

( runtime.run( mundy::Preferences::stream(mundy::Interest::ColorScheme) .map(move |preferences| { - Action::ChangeTheme(to_mode(preferences.color_scheme)) + Action::System(system::Action::NotifyTheme(to_mode( + preferences.color_scheme, + ))) }) .boxed(), ); @@ -542,7 +552,10 @@ async fn run_instance

( }; #[cfg(not(all(feature = "linux-theme-detection", target_os = "linux")))] - let system_theme = theme::Mode::None; + let mut system_theme = + _system_theme.try_recv().ok().flatten().unwrap_or_default(); + + log::info!("System theme: {system_theme:?}"); loop { // Empty the queue if possible @@ -728,6 +741,7 @@ async fn run_instance

( run_action( action, &program, + &mut runtime, &mut compositor, &mut events, &mut messages, @@ -737,6 +751,7 @@ async fn run_instance

( &mut window_manager, &mut ui_caches, &mut is_window_opening, + &mut system_theme, ); actions += 1; } @@ -895,11 +910,24 @@ async fn run_instance

( continue; }; - if matches!( - window_event, - winit::event::WindowEvent::Resized(_) - ) { - window.raw.request_redraw(); + match window_event { + winit::event::WindowEvent::Resized(_) => { + window.raw.request_redraw(); + } + winit::event::WindowEvent::ThemeChanged(theme) => { + let mode = conversion::theme_mode(theme); + + if mode != system_theme { + system_theme = mode; + + runtime.broadcast( + subscription::Event::SystemThemeChanged( + mode, + ), + ); + } + } + _ => {} } if matches!( @@ -912,6 +940,7 @@ async fn run_instance

( id, )), &program, + &mut runtime, &mut compositor, &mut events, &mut messages, @@ -921,6 +950,7 @@ async fn run_instance

( &mut window_manager, &mut ui_caches, &mut is_window_opening, + &mut system_theme, ); } else { window.state.update( @@ -1132,6 +1162,7 @@ fn update( fn run_action<'a, P, C>( action: Action, program: &'a program::Instance

, + runtime: &mut Runtime, Action>, compositor: &mut Option, events: &mut Vec<(window::Id, core::Event)>, messages: &mut Vec, @@ -1144,13 +1175,13 @@ fn run_action<'a, P, C>( window_manager: &mut WindowManager, ui_caches: &mut FxHashMap, is_window_opening: &mut bool, + system_theme: &mut theme::Mode, ) where P: Program, C: Compositor + 'static, P::Theme: theme::Base, { use crate::runtime::clipboard; - use crate::runtime::system; use crate::runtime::window; match action { @@ -1458,21 +1489,44 @@ fn run_action<'a, P, C>( } }, Action::System(action) => match action { - system::Action::QueryInformation(_channel) => { - #[cfg(feature = "system")] + system::Action::GetInformation(_channel) => { + #[cfg(feature = "sysinfo")] { if let Some(compositor) = compositor { - let graphics_info = compositor.fetch_information(); + let graphics_info = compositor.information(); let _ = std::thread::spawn(move || { - let information = - crate::system::information(graphics_info); + let information = system_information(graphics_info); let _ = _channel.send(information); }); } } } + system::Action::GetTheme(channel) => { + let _ = channel.send(*system_theme); + } + system::Action::NotifyTheme(mode) => { + if mode != *system_theme { + *system_theme = mode; + + runtime.broadcast(subscription::Event::SystemThemeChanged( + mode, + )); + } + + let Some(theme) = conversion::window_theme(mode) else { + return; + }; + + for (_id, window) in window_manager.iter_mut() { + window.state.update( + program, + &window.raw, + &winit::event::WindowEvent::ThemeChanged(theme), + ); + } + } }, Action::Widget(operation) => { let mut current_operation = Some(operation); @@ -1501,27 +1555,6 @@ fn run_action<'a, P, C>( let _ = channel.send(Ok(())); } } - Action::ChangeTheme(mode) => { - let Some(theme) = conversion::window_theme(mode) else { - return; - }; - - for (id, window) in window_manager.iter_mut() { - window.raw.set_theme(Some(theme)); - window.state.update( - program, - &window.raw, - &winit::event::WindowEvent::ThemeChanged(theme), - ); - - events.push(( - id, - core::Event::Window(core::window::Event::ThemeModeChanged( - mode, - )), - )); - } - } Action::Reload => { for (id, window) in window_manager.iter_mut() { let Some(ui) = interfaces.remove(&id) else { @@ -1602,3 +1635,37 @@ pub fn user_force_quit( _ => false, } } + +#[cfg(feature = "sysinfo")] +fn system_information( + graphics: compositor::Information, +) -> system::Information { + use sysinfo::{Process, System}; + + let mut system = System::new_all(); + system.refresh_all(); + + let cpu_brand = system + .cpus() + .first() + .map(|cpu| cpu.brand().to_string()) + .unwrap_or_default(); + + let memory_used = sysinfo::get_current_pid() + .and_then(|pid| system.process(pid).ok_or("Process not found")) + .map(Process::memory) + .ok(); + + system::Information { + system_name: System::name(), + system_kernel: System::kernel_version(), + system_version: System::long_os_version(), + system_short_version: System::os_version(), + cpu_brand, + cpu_cores: system.physical_core_count(), + memory_total: system.total_memory(), + memory_used, + graphics_adapter: graphics.adapter, + graphics_backend: graphics.backend, + } +} diff --git a/winit/src/system.rs b/winit/src/system.rs index 0b476773..ec13c0f3 100644 --- a/winit/src/system.rs +++ b/winit/src/system.rs @@ -3,13 +3,6 @@ use crate::graphics::compositor; use crate::runtime::system::{Action, Information}; use crate::runtime::{self, Task}; -/// Query for available system information. -pub fn fetch_information() -> Task { - runtime::task::oneshot(|channel| { - runtime::Action::System(Action::QueryInformation(channel)) - }) -} - pub(crate) fn information( graphics_info: compositor::Information, ) -> Information { diff --git a/winit/src/window/state.rs b/winit/src/window/state.rs index 4bc28cd4..4c3cb403 100644 --- a/winit/src/window/state.rs +++ b/winit/src/window/state.rs @@ -53,11 +53,6 @@ where window: &Window, system_theme: theme::Mode, ) -> Self { - let system_theme = window - .theme() - .map(conversion::theme_mode) - .unwrap_or(system_theme); - let title = program.title(window_id); let scale_factor = program.scale_factor(window_id); let theme = program.theme(window_id);