Add theme and theme_changes functions to system

This commit is contained in:
Héctor Ramón Jiménez 2025-09-08 14:32:24 +02:00
parent 9518573fce
commit 09c604c92d
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
24 changed files with 186 additions and 102 deletions

View file

@ -54,7 +54,7 @@ tokio = ["iced_futures/tokio"]
# Enables `smol` as the `executor::Default` on native platforms # Enables `smol` as the `executor::Default` on native platforms
smol = ["iced_futures/smol"] smol = ["iced_futures/smol"]
# Enables querying system information # Enables querying system information
system = ["iced_winit/system"] sysinfo = ["iced_winit/sysinfo"]
# Enables broken "sRGB linear" blending to reproduce color management of the Web # Enables broken "sRGB linear" blending to reproduce color management of the Web
web-colors = ["iced_renderer/web-colors"] web-colors = ["iced_renderer/web-colors"]
# Enables pixel snapping for crisp edges by default (can cause jitter!) # Enables pixel snapping for crisp edges by default (can cause jitter!)

View file

@ -1,4 +1,3 @@
use crate::theme;
use crate::time::Instant; use crate::time::Instant;
use crate::{Point, Size}; use crate::{Point, Size};
@ -72,7 +71,4 @@ pub enum Event {
/// ///
/// - **Wayland:** Not implemented. /// - **Wayland:** Not implemented.
FilesHoveredLeft, FilesHoveredLeft,
/// The theme mode of the window has changed.
ThemeModeChanged(theme::Mode),
} }

View file

@ -37,7 +37,7 @@ impl Events {
} }
Message::EventOccurred(event) => { Message::EventOccurred(event) => {
if let Event::Window(window::Event::CloseRequested) = event { if let Event::Window(window::Event::CloseRequested) = event {
window::get_latest().and_then(window::close) window::latest().and_then(window::close)
} else { } else {
Task::none() Task::none()
} }
@ -47,7 +47,7 @@ impl Events {
Task::none() Task::none()
} }
Message::Exit => window::get_latest().and_then(window::close), Message::Exit => window::latest().and_then(window::close),
} }
} }

View file

@ -20,7 +20,7 @@ enum Message {
impl Exit { impl Exit {
fn update(&mut self, message: Message) -> Task<Message> { fn update(&mut self, message: Message) -> Task<Message> {
match message { match message {
Message::Confirm => window::get_latest().and_then(window::close), Message::Confirm => window::latest().and_then(window::close),
Message::Exit => { Message::Exit => {
self.show_confirm = true; self.show_confirm = true;

View file

@ -66,7 +66,7 @@ impl Example {
return Task::none(); return Task::none();
}; };
window::get_position(*last_window) window::position(*last_window)
.then(|last_position| { .then(|last_position| {
let position = last_position.map_or( let position = last_position.map_or(
window::Position::Default, window::Position::Default,

View file

@ -49,7 +49,7 @@ impl Example {
fn update(&mut self, message: Message) -> Task<Message> { fn update(&mut self, message: Message) -> Task<Message> {
match message { match message {
Message::Screenshot => { Message::Screenshot => {
return window::get_latest() return window::latest()
.and_then(window::screenshot) .and_then(window::screenshot)
.map(Message::Screenshotted); .map(Message::Screenshotted);
} }

View file

@ -7,6 +7,6 @@ publish = false
[dependencies] [dependencies]
iced.workspace = true iced.workspace = true
iced.features = ["system"] iced.features = ["sysinfo"]
bytesize = "1.1" bytesize = "1.1"

View file

@ -1,5 +1,6 @@
use iced::system;
use iced::widget::{button, center, column, text}; use iced::widget::{button, center, column, text};
use iced::{Element, Task, system}; use iced::{Element, Task};
pub fn main() -> iced::Result { pub fn main() -> iced::Result {
iced::application(Example::new, Example::update, Example::view).run() iced::application(Example::new, Example::update, Example::view).run()
@ -26,7 +27,7 @@ impl Example {
fn new() -> (Self, Task<Message>) { fn new() -> (Self, Task<Message>) {
( (
Self::Loading, Self::Loading,
system::fetch_information().map(Message::InformationReceived), system::information().map(Message::InformationReceived),
) )
} }

View file

@ -151,7 +151,7 @@ impl Todos {
widget::focus_next() widget::focus_next()
} }
} }
Message::ToggleFullscreen(mode) => window::get_latest() Message::ToggleFullscreen(mode) => window::latest()
.and_then(move |window| window::set_mode(window, mode)), .and_then(move |window| window::set_mode(window, mode)),
Message::Loaded(_) => Command::none(), Message::Loaded(_) => Command::none(),
}; };

View file

@ -37,6 +37,7 @@ where
event: Event::Window(window::Event::RedrawRequested(_)), event: Event::Window(window::Event::RedrawRequested(_)),
.. ..
} }
| subscription::Event::SystemThemeChanged(_)
| subscription::Event::PlatformSpecific(_) => None, | subscription::Event::PlatformSpecific(_) => None,
subscription::Event::Interaction { subscription::Event::Interaction {
window, window,
@ -66,7 +67,8 @@ where
event, event,
status, status,
} => f(event, status, window), } => f(event, status, window),
subscription::Event::PlatformSpecific(_) => None, subscription::Event::SystemThemeChanged(_)
| subscription::Event::PlatformSpecific(_) => None,
}) })
} }

View file

@ -4,6 +4,7 @@ mod tracker;
pub use tracker::Tracker; pub use tracker::Tracker;
use crate::core::event; use crate::core::event;
use crate::core::theme;
use crate::core::window; use crate::core::window;
use crate::futures::Stream; use crate::futures::Stream;
use crate::{BoxStream, MaybeSend}; use crate::{BoxStream, MaybeSend};
@ -27,6 +28,9 @@ pub enum Event {
status: event::Status, status: event::Status,
}, },
/// The system theme has changed.
SystemThemeChanged(theme::Mode),
/// A platform specific event. /// A platform specific event.
PlatformSpecific(PlatformSpecific), PlatformSpecific(PlatformSpecific),
} }
@ -422,7 +426,8 @@ where
} }
} }
pub(crate) fn filter_map<I, F, T>(id: I, f: F) -> Subscription<T> /// Creatges a [`Subscription`] from a hashable id and a filter function.
pub fn filter_map<I, F, T>(id: I, f: F) -> Subscription<T>
where where
I: Hash + 'static, I: Hash + 'static,
F: Fn(Event) -> Option<T> + MaybeSend + 'static, F: Fn(Event) -> Option<T> + MaybeSend + 'static,

View file

@ -59,7 +59,7 @@ pub trait Compositor: Sized {
); );
/// Returns [`Information`] used by this [`Compositor`]. /// Returns [`Information`] used by this [`Compositor`].
fn fetch_information(&self) -> Information; fn information(&self) -> Information;
/// Loads a font from its bytes. /// Loads a font from its bytes.
fn load_font(&mut self, font: Cow<'static, [u8]>) { 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 load_font(&mut self, _font: Cow<'static, [u8]>) {}
fn fetch_information(&self) -> Information { fn information(&self) -> Information {
Information { Information {
adapter: String::from("Null Renderer"), adapter: String::from("Null Renderer"),
backend: String::from("Null"), backend: String::from("Null"),

View file

@ -313,8 +313,8 @@ where
delegate!(self, compositor, compositor.load_font(font)); delegate!(self, compositor, compositor.load_font(font));
} }
fn fetch_information(&self) -> compositor::Information { fn information(&self) -> compositor::Information {
delegate!(self, compositor, compositor.fetch_information()) delegate!(self, compositor, compositor.information())
} }
fn present( fn present(

View file

@ -25,7 +25,6 @@ pub use iced_futures as futures;
pub use task::Task; pub use task::Task;
pub use user_interface::UserInterface; pub use user_interface::UserInterface;
use crate::core::theme;
use crate::core::widget; use crate::core::widget;
use crate::futures::futures::channel::oneshot; use crate::futures::futures::channel::oneshot;
@ -57,9 +56,6 @@ pub enum Action<T> {
/// Run a system action. /// Run a system action.
System(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. /// Recreate all user interfaces and redraw all windows.
Reload, Reload,
@ -86,7 +82,6 @@ impl<T> Action<T> {
Action::Clipboard(action) => Err(Action::Clipboard(action)), Action::Clipboard(action) => Err(Action::Clipboard(action)),
Action::Window(action) => Err(Action::Window(action)), Action::Window(action) => Err(Action::Window(action)),
Action::System(action) => Err(Action::System(action)), Action::System(action) => Err(Action::System(action)),
Action::ChangeTheme(mode) => Err(Action::ChangeTheme(mode)),
Action::Reload => Err(Action::Reload), Action::Reload => Err(Action::Reload),
Action::Exit => Err(Action::Exit), Action::Exit => Err(Action::Exit),
} }
@ -111,9 +106,6 @@ where
} }
Action::Window(_) => write!(f, "Action::Window"), Action::Window(_) => write!(f, "Action::Window"),
Action::System(action) => write!(f, "Action::System({action:?})"), Action::System(action) => write!(f, "Action::System({action:?})"),
Action::ChangeTheme(mode) => {
write!(f, "Action::ChangeTheme({mode:?})")
}
Action::Reload => write!(f, "Action::Reload"), Action::Reload => write!(f, "Action::Reload"),
Action::Exit => write!(f, "Action::Exit"), Action::Exit => write!(f, "Action::Exit"),
} }

View file

@ -1,11 +1,20 @@
//! Access the native system. //! Access the native system.
use crate::core::theme;
use crate::futures::futures::channel::oneshot; 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. /// An operation to be performed on the system.
#[derive(Debug)] #[derive(Debug)]
pub enum Action { pub enum Action {
/// Query system information and produce `T` with the result. /// Send available system information.
QueryInformation(oneshot::Sender<Information>), GetInformation(oneshot::Sender<Information>),
/// Send the current system theme mode.
GetTheme(oneshot::Sender<theme::Mode>),
/// 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). /// 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 /// Model information for the active graphics adapter
pub graphics_adapter: String, pub graphics_adapter: String,
} }
/// Returns available system information.
pub fn information() -> Task<Information> {
task::oneshot(|channel| {
crate::Action::System(Action::GetInformation(channel))
})
}
/// Returns the current system theme.
pub fn theme() -> Task<theme::Mode> {
task::oneshot(|sender| crate::Action::System(Action::GetTheme(sender)))
}
/// Subscribes to system theme changes.
pub fn theme_changes() -> Subscription<theme::Mode> {
#[derive(Hash)]
struct ThemeChanges;
subscription::filter_map(ThemeChanges, |event| {
let subscription::Event::SystemThemeChanged(mode) = event else {
return None;
};
Some(mode)
})
}

View file

@ -266,12 +266,12 @@ pub fn close<T>(id: Id) -> Task<T> {
} }
/// Gets the window [`Id`] of the oldest window. /// Gets the window [`Id`] of the oldest window.
pub fn get_oldest() -> Task<Option<Id>> { pub fn oldest() -> Task<Option<Id>> {
task::oneshot(|channel| crate::Action::Window(Action::GetOldest(channel))) task::oneshot(|channel| crate::Action::Window(Action::GetOldest(channel)))
} }
/// Gets the window [`Id`] of the latest window. /// Gets the window [`Id`] of the latest window.
pub fn get_latest() -> Task<Option<Id>> { pub fn latest() -> Task<Option<Id>> {
task::oneshot(|channel| crate::Action::Window(Action::GetLatest(channel))) task::oneshot(|channel| crate::Action::Window(Action::GetLatest(channel)))
} }
@ -315,14 +315,14 @@ pub fn set_resize_increments<T>(id: Id, increments: Option<Size>) -> Task<T> {
} }
/// Get the window's size in logical dimensions. /// Get the window's size in logical dimensions.
pub fn get_size(id: Id) -> Task<Size> { pub fn size(id: Id) -> Task<Size> {
task::oneshot(move |channel| { task::oneshot(move |channel| {
crate::Action::Window(Action::GetSize(id, channel)) crate::Action::Window(Action::GetSize(id, channel))
}) })
} }
/// Gets the maximized state of the window with the given [`Id`]. /// Gets the maximized state of the window with the given [`Id`].
pub fn get_maximized(id: Id) -> Task<bool> { pub fn is_maximized(id: Id) -> Task<bool> {
task::oneshot(move |channel| { task::oneshot(move |channel| {
crate::Action::Window(Action::GetMaximized(id, channel)) crate::Action::Window(Action::GetMaximized(id, channel))
}) })
@ -334,7 +334,7 @@ pub fn maximize<T>(id: Id, maximized: bool) -> Task<T> {
} }
/// Gets the minimized state of the window with the given [`Id`]. /// Gets the minimized state of the window with the given [`Id`].
pub fn get_minimized(id: Id) -> Task<Option<bool>> { pub fn is_minimized(id: Id) -> Task<Option<bool>> {
task::oneshot(move |channel| { task::oneshot(move |channel| {
crate::Action::Window(Action::GetMinimized(id, channel)) crate::Action::Window(Action::GetMinimized(id, channel))
}) })
@ -346,14 +346,14 @@ pub fn minimize<T>(id: Id, minimized: bool) -> Task<T> {
} }
/// Gets the position in logical coordinates of the window with the given [`Id`]. /// Gets the position in logical coordinates of the window with the given [`Id`].
pub fn get_position(id: Id) -> Task<Option<Point>> { pub fn position(id: Id) -> Task<Option<Point>> {
task::oneshot(move |channel| { task::oneshot(move |channel| {
crate::Action::Window(Action::GetPosition(id, channel)) crate::Action::Window(Action::GetPosition(id, channel))
}) })
} }
/// Gets the scale factor of the window with the given [`Id`]. /// Gets the scale factor of the window with the given [`Id`].
pub fn get_scale_factor(id: Id) -> Task<f32> { pub fn scale_factor(id: Id) -> Task<f32> {
task::oneshot(move |channel| { task::oneshot(move |channel| {
crate::Action::Window(Action::GetScaleFactor(id, channel)) crate::Action::Window(Action::GetScaleFactor(id, channel))
}) })
@ -365,7 +365,7 @@ pub fn move_to<T>(id: Id, position: Point) -> Task<T> {
} }
/// Gets the current [`Mode`] of the window. /// Gets the current [`Mode`] of the window.
pub fn get_mode(id: Id) -> Task<Mode> { pub fn mode(id: Id) -> Task<Mode> {
task::oneshot(move |channel| { task::oneshot(move |channel| {
crate::Action::Window(Action::GetMode(id, channel)) crate::Action::Window(Action::GetMode(id, channel))
}) })
@ -426,7 +426,7 @@ pub fn show_system_menu<T>(id: Id) -> Task<T> {
/// Gets an identifier unique to the window, provided by the underlying windowing system. This is /// Gets an identifier unique to the window, provided by the underlying windowing system. This is
/// not to be confused with [`Id`]. /// not to be confused with [`Id`].
pub fn get_raw_id<Message>(id: Id) -> Task<u64> { pub fn raw_id<Message>(id: Id) -> Task<u64> {
task::oneshot(|channel| { task::oneshot(|channel| {
crate::Action::Window(Action::GetRawId(id, channel)) crate::Action::Window(Action::GetRawId(id, channel))
}) })

View file

@ -587,11 +587,12 @@ pub mod mouse {
}; };
} }
#[cfg(feature = "system")]
pub mod system { pub mod system {
//! Retrieve system information. //! Retrieve system information.
pub use crate::runtime::system::Information; pub use crate::runtime::system::{theme, theme_changes};
pub use crate::shell::system::*;
#[cfg(feature = "sysinfo")]
pub use crate::runtime::system::{Information, information};
} }
pub mod overlay { pub mod overlay {

View file

@ -100,7 +100,7 @@ impl crate::graphics::Compositor for Compositor {
surface.layer_stack.clear(); surface.layer_stack.clear();
} }
fn fetch_information(&self) -> Information { fn information(&self) -> Information {
Information { Information {
adapter: String::from("CPU"), adapter: String::from("CPU"),
backend: String::from("tiny-skia"), backend: String::from("tiny-skia"),

View file

@ -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(); let information = self.adapter.get_info();
compositor::Information { compositor::Information {

View file

@ -16,7 +16,7 @@ workspace = true
[features] [features]
default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"] default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
debug = ["iced_debug/enable"] debug = ["iced_debug/enable"]
system = ["sysinfo"] sysinfo = ["dep:sysinfo"]
program = [] program = []
x11 = ["winit/x11"] x11 = ["winit/x11"]
wayland = ["winit/wayland"] wayland = ["winit/wayland"]

View file

@ -319,9 +319,6 @@ pub fn window_event(
Some(Event::Window(window::Event::Moved(Point::new(x, y)))) Some(Event::Window(window::Event::Moved(Point::new(x, y))))
} }
WindowEvent::ThemeChanged(theme) => Some(Event::Window(
window::Event::ThemeModeChanged(theme_mode(theme)),
)),
_ => None, _ => None,
} }
} }

View file

@ -29,9 +29,6 @@ pub use winit;
pub mod clipboard; pub mod clipboard;
pub mod conversion; pub mod conversion;
#[cfg(feature = "system")]
pub mod system;
mod error; mod error;
mod proxy; mod proxy;
mod window; mod window;
@ -53,6 +50,7 @@ use crate::futures::futures::{Future, StreamExt};
use crate::futures::subscription; use crate::futures::subscription;
use crate::futures::{Executor, Runtime}; use crate::futures::{Executor, Runtime};
use crate::graphics::{Compositor, compositor}; use crate::graphics::{Compositor, compositor};
use crate::runtime::system;
use crate::runtime::user_interface::{self, UserInterface}; use crate::runtime::user_interface::{self, UserInterface};
use crate::runtime::{Action, Task}; use crate::runtime::{Action, Task};
@ -126,6 +124,7 @@ where
let (event_sender, event_receiver) = mpsc::unbounded(); let (event_sender, event_receiver) = mpsc::unbounded();
let (control_sender, control_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::<P>( let instance = Box::pin(run_instance::<P>(
program, program,
@ -136,6 +135,7 @@ where
is_daemon, is_daemon,
graphics_settings, graphics_settings,
settings.fonts, settings.fonts,
system_theme_receiver,
)); ));
let context = task::Context::from_waker(task::noop_waker_ref()); let context = task::Context::from_waker(task::noop_waker_ref());
@ -147,6 +147,7 @@ where
sender: mpsc::UnboundedSender<Event<Action<Message>>>, sender: mpsc::UnboundedSender<Event<Action<Message>>>,
receiver: mpsc::UnboundedReceiver<Control>, receiver: mpsc::UnboundedReceiver<Control>,
error: Option<Error>, error: Option<Error>,
system_theme: Option<oneshot::Sender<theme::Mode>>,
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
canvas: Option<web_sys::HtmlCanvasElement>, canvas: Option<web_sys::HtmlCanvasElement>,
@ -159,6 +160,7 @@ where
sender: event_sender, sender: event_sender,
receiver: control_receiver, receiver: control_receiver,
error: None, error: None,
system_theme: Some(system_theme_sender),
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
canvas: None, canvas: None,
@ -172,10 +174,15 @@ where
Message: std::fmt::Debug, Message: std::fmt::Debug,
F: Future<Output = ()>, F: Future<Output = ()>,
{ {
fn resumed( fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
&mut self, if let Some(sender) = self.system_theme.take() {
_event_loop: &winit::event_loop::ActiveEventLoop, let _ = sender.send(
) { event_loop
.system_theme()
.map(conversion::theme_mode)
.unwrap_or_default(),
);
}
} }
fn new_events( fn new_events(
@ -498,6 +505,7 @@ async fn run_instance<P>(
is_daemon: bool, is_daemon: bool,
graphics_settings: graphics::Settings, graphics_settings: graphics::Settings,
default_fonts: Vec<Cow<'static, [u8]>>, default_fonts: Vec<Cow<'static, [u8]>>,
mut _system_theme: oneshot::Receiver<theme::Mode>,
) where ) where
P: Program + 'static, P: Program + 'static,
P::Theme: theme::Base, P::Theme: theme::Base,
@ -518,7 +526,7 @@ async fn run_instance<P>(
let mut clipboard = Clipboard::unconnected(); let mut clipboard = Clipboard::unconnected();
#[cfg(all(feature = "linux-theme-detection", target_os = "linux"))] #[cfg(all(feature = "linux-theme-detection", target_os = "linux"))]
let system_theme = { let mut system_theme = {
let to_mode = |color_scheme| match color_scheme { let to_mode = |color_scheme| match color_scheme {
mundy::ColorScheme::NoPreference => theme::Mode::None, mundy::ColorScheme::NoPreference => theme::Mode::None,
mundy::ColorScheme::Light => theme::Mode::Light, mundy::ColorScheme::Light => theme::Mode::Light,
@ -528,7 +536,9 @@ async fn run_instance<P>(
runtime.run( runtime.run(
mundy::Preferences::stream(mundy::Interest::ColorScheme) mundy::Preferences::stream(mundy::Interest::ColorScheme)
.map(move |preferences| { .map(move |preferences| {
Action::ChangeTheme(to_mode(preferences.color_scheme)) Action::System(system::Action::NotifyTheme(to_mode(
preferences.color_scheme,
)))
}) })
.boxed(), .boxed(),
); );
@ -542,7 +552,10 @@ async fn run_instance<P>(
}; };
#[cfg(not(all(feature = "linux-theme-detection", target_os = "linux")))] #[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 { loop {
// Empty the queue if possible // Empty the queue if possible
@ -728,6 +741,7 @@ async fn run_instance<P>(
run_action( run_action(
action, action,
&program, &program,
&mut runtime,
&mut compositor, &mut compositor,
&mut events, &mut events,
&mut messages, &mut messages,
@ -737,6 +751,7 @@ async fn run_instance<P>(
&mut window_manager, &mut window_manager,
&mut ui_caches, &mut ui_caches,
&mut is_window_opening, &mut is_window_opening,
&mut system_theme,
); );
actions += 1; actions += 1;
} }
@ -895,11 +910,24 @@ async fn run_instance<P>(
continue; continue;
}; };
if matches!( match window_event {
window_event, winit::event::WindowEvent::Resized(_) => {
winit::event::WindowEvent::Resized(_) window.raw.request_redraw();
) { }
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!( if matches!(
@ -912,6 +940,7 @@ async fn run_instance<P>(
id, id,
)), )),
&program, &program,
&mut runtime,
&mut compositor, &mut compositor,
&mut events, &mut events,
&mut messages, &mut messages,
@ -921,6 +950,7 @@ async fn run_instance<P>(
&mut window_manager, &mut window_manager,
&mut ui_caches, &mut ui_caches,
&mut is_window_opening, &mut is_window_opening,
&mut system_theme,
); );
} else { } else {
window.state.update( window.state.update(
@ -1132,6 +1162,7 @@ fn update<P: Program, E: Executor>(
fn run_action<'a, P, C>( fn run_action<'a, P, C>(
action: Action<P::Message>, action: Action<P::Message>,
program: &'a program::Instance<P>, program: &'a program::Instance<P>,
runtime: &mut Runtime<P::Executor, Proxy<P::Message>, Action<P::Message>>,
compositor: &mut Option<C>, compositor: &mut Option<C>,
events: &mut Vec<(window::Id, core::Event)>, events: &mut Vec<(window::Id, core::Event)>,
messages: &mut Vec<P::Message>, messages: &mut Vec<P::Message>,
@ -1144,13 +1175,13 @@ fn run_action<'a, P, C>(
window_manager: &mut WindowManager<P, C>, window_manager: &mut WindowManager<P, C>,
ui_caches: &mut FxHashMap<window::Id, user_interface::Cache>, ui_caches: &mut FxHashMap<window::Id, user_interface::Cache>,
is_window_opening: &mut bool, is_window_opening: &mut bool,
system_theme: &mut theme::Mode,
) where ) where
P: Program, P: Program,
C: Compositor<Renderer = P::Renderer> + 'static, C: Compositor<Renderer = P::Renderer> + 'static,
P::Theme: theme::Base, P::Theme: theme::Base,
{ {
use crate::runtime::clipboard; use crate::runtime::clipboard;
use crate::runtime::system;
use crate::runtime::window; use crate::runtime::window;
match action { match action {
@ -1458,21 +1489,44 @@ fn run_action<'a, P, C>(
} }
}, },
Action::System(action) => match action { Action::System(action) => match action {
system::Action::QueryInformation(_channel) => { system::Action::GetInformation(_channel) => {
#[cfg(feature = "system")] #[cfg(feature = "sysinfo")]
{ {
if let Some(compositor) = compositor { if let Some(compositor) = compositor {
let graphics_info = compositor.fetch_information(); let graphics_info = compositor.information();
let _ = std::thread::spawn(move || { let _ = std::thread::spawn(move || {
let information = let information = system_information(graphics_info);
crate::system::information(graphics_info);
let _ = _channel.send(information); 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) => { Action::Widget(operation) => {
let mut current_operation = Some(operation); let mut current_operation = Some(operation);
@ -1501,27 +1555,6 @@ fn run_action<'a, P, C>(
let _ = channel.send(Ok(())); 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 => { Action::Reload => {
for (id, window) in window_manager.iter_mut() { for (id, window) in window_manager.iter_mut() {
let Some(ui) = interfaces.remove(&id) else { let Some(ui) = interfaces.remove(&id) else {
@ -1602,3 +1635,37 @@ pub fn user_force_quit(
_ => false, _ => 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,
}
}

View file

@ -3,13 +3,6 @@ use crate::graphics::compositor;
use crate::runtime::system::{Action, Information}; use crate::runtime::system::{Action, Information};
use crate::runtime::{self, Task}; use crate::runtime::{self, Task};
/// Query for available system information.
pub fn fetch_information() -> Task<Information> {
runtime::task::oneshot(|channel| {
runtime::Action::System(Action::QueryInformation(channel))
})
}
pub(crate) fn information( pub(crate) fn information(
graphics_info: compositor::Information, graphics_info: compositor::Information,
) -> Information { ) -> Information {

View file

@ -53,11 +53,6 @@ where
window: &Window, window: &Window,
system_theme: theme::Mode, system_theme: theme::Mode,
) -> Self { ) -> Self {
let system_theme = window
.theme()
.map(conversion::theme_mode)
.unwrap_or(system_theme);
let title = program.title(window_id); let title = program.title(window_id);
let scale_factor = program.scale_factor(window_id); let scale_factor = program.scale_factor(window_id);
let theme = program.theme(window_id); let theme = program.theme(window_id);