Add linux-theme-detection feature through mundy

This commit is contained in:
Héctor Ramón Jiménez 2025-09-08 09:49:11 +02:00
parent ab7eb88951
commit e92c87061d
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
8 changed files with 566 additions and 50 deletions

View file

@ -326,7 +326,7 @@ pub fn window_event(
}
}
/// Converts a [`window::Level`] to a [`winit`] window level.
/// Converts a [`window::Level`] into a [`winit`] window level.
///
/// [`winit`]: https://github.com/rust-windowing/winit
pub fn window_level(level: window::Level) -> winit::window::WindowLevel {
@ -339,7 +339,7 @@ pub fn window_level(level: window::Level) -> winit::window::WindowLevel {
}
}
/// Converts a [`window::Position`] to a [`winit`] logical position for a given monitor.
/// Converts a [`window::Position`] into a [`winit`] logical position for a given monitor.
///
/// [`winit`]: https://github.com/rust-windowing/winit
pub fn position(
@ -411,7 +411,7 @@ pub fn position(
}
}
/// Converts a [`window::Mode`] to a [`winit`] fullscreen mode.
/// Converts a [`window::Mode`] into a [`winit`] fullscreen mode.
///
/// [`winit`]: https://github.com/rust-windowing/winit
pub fn fullscreen(
@ -426,7 +426,7 @@ pub fn fullscreen(
}
}
/// Converts a [`window::Mode`] to a visibility flag.
/// Converts a [`window::Mode`] into a visibility flag.
pub fn visible(mode: window::Mode) -> bool {
match mode {
window::Mode::Windowed | window::Mode::Fullscreen => true,
@ -434,7 +434,7 @@ pub fn visible(mode: window::Mode) -> bool {
}
}
/// Converts a [`winit`] fullscreen mode to a [`window::Mode`].
/// Converts a [`winit`] fullscreen mode into a [`window::Mode`].
///
/// [`winit`]: https://github.com/rust-windowing/winit
pub fn mode(mode: Option<winit::window::Fullscreen>) -> window::Mode {
@ -444,7 +444,7 @@ pub fn mode(mode: Option<winit::window::Fullscreen>) -> window::Mode {
}
}
/// Converts a [`winit`] window theme to a [`theme::Mode`].
/// Converts a [`winit`] window theme into a [`theme::Mode`].
///
/// [`winit`]: https://github.com/rust-windowing/winit
pub fn theme_mode(theme: winit::window::Theme) -> theme::Mode {
@ -454,7 +454,18 @@ pub fn theme_mode(theme: winit::window::Theme) -> theme::Mode {
}
}
/// Converts a [`mouse::Interaction`] to a [`winit`] cursor icon.
/// Converts a [`theme::Mode`] into a window theme.
///
/// [`winit`]: https://github.com/rust-windowing/winit
pub fn window_theme(mode: theme::Mode) -> Option<winit::window::Theme> {
match mode {
theme::Mode::None => None,
theme::Mode::Light => Some(winit::window::Theme::Light),
theme::Mode::Dark => Some(winit::window::Theme::Dark),
}
}
/// Converts a [`mouse::Interaction`] into a [`winit`] cursor icon.
///
/// [`winit`]: https://github.com/rust-windowing/winit
pub fn mouse_interaction(
@ -525,7 +536,7 @@ pub fn modifiers(
result
}
/// Converts a physical cursor position to a logical `Point`.
/// Converts a physical cursor position into a logical `Point`.
pub fn cursor_position(
position: winit::dpi::PhysicalPosition<f64>,
scale_factor: f32,
@ -1196,7 +1207,7 @@ pub fn icon(icon: window::Icon) -> Option<winit::window::Icon> {
winit::window::Icon::from_rgba(pixels, size.width, size.height).ok()
}
/// Convertions some [`input_method::Purpose`] to its `winit` counterpart.
/// Convertions some [`input_method::Purpose`] into its `winit` counterpart.
pub fn ime_purpose(
purpose: input_method::Purpose,
) -> winit::window::ImePurpose {

View file

@ -111,7 +111,7 @@ where
let (_id, open) = runtime::window::open(window_settings);
open.then(move |_| task.take().unwrap_or(Task::none()))
open.then(move |_| task.take().unwrap_or_else(Task::none))
} else {
task
};
@ -517,6 +517,33 @@ async fn run_instance<P>(
let mut user_interfaces = ManuallyDrop::new(FxHashMap::default());
let mut clipboard = Clipboard::unconnected();
#[cfg(all(feature = "linux-theme-detection", target_os = "linux"))]
let system_theme = {
let to_mode = |color_scheme| match color_scheme {
mundy::ColorScheme::NoPreference => theme::Mode::None,
mundy::ColorScheme::Light => theme::Mode::Light,
mundy::ColorScheme::Dark => theme::Mode::Dark,
};
runtime.run(
mundy::Preferences::stream(mundy::Interest::ColorScheme)
.map(move |preferences| {
Action::ChangeTheme(to_mode(preferences.color_scheme))
})
.boxed(),
);
mundy::Preferences::once_blocking(
mundy::Interest::ColorScheme,
core::time::Duration::from_millis(200),
)
.map(|preferences| to_mode(preferences.color_scheme))
.unwrap_or_default()
};
#[cfg(not(all(feature = "linux-theme-detection", target_os = "linux")))]
let system_theme = theme::Mode::None;
loop {
// Empty the queue if possible
let event = if let Ok(event) = event_receiver.try_next() {
@ -604,6 +631,7 @@ async fn run_instance<P>(
.as_mut()
.expect("Compositor must be initialized"),
exit_on_close_request,
system_theme,
);
debug::theme_changed(|| {
@ -891,7 +919,11 @@ async fn run_instance<P>(
&mut is_window_opening,
);
} else {
window.state.update(&window.raw, &window_event);
window.state.update(
&program,
&window.raw,
&window_event,
);
if let Some(event) = conversion::window_event(
window_event,
@ -1465,6 +1497,27 @@ 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 {

View file

@ -55,8 +55,9 @@ where
program: &program::Instance<P>,
compositor: &mut C,
exit_on_close_request: bool,
system_theme: theme::Mode,
) -> &mut Window<P, C> {
let state = State::new(program, id, &window);
let state = State::new(program, id, &window, system_theme);
let viewport_version = state.viewport_version();
let physical_size = state.physical_size();
let surface = compositor.create_surface(

View file

@ -21,7 +21,7 @@ where
cursor_position: Option<winit::dpi::PhysicalPosition<f64>>,
modifiers: winit::keyboard::ModifiersState,
theme: Option<P::Theme>,
system_theme: P::Theme,
default_theme: P::Theme,
style: theme::Style,
}
@ -50,16 +50,18 @@ where
program: &program::Instance<P>,
window_id: window::Id,
window: &Window,
system_theme: theme::Mode,
) -> Self {
let title = program.title(window_id);
let scale_factor = program.scale_factor(window_id);
let theme_mode = window
.theme()
.map(conversion::theme_mode)
.unwrap_or_default();
.unwrap_or(system_theme);
let title = program.title(window_id);
let scale_factor = program.scale_factor(window_id);
let theme = program.theme(window_id);
let system_theme = <P::Theme as theme::Base>::default(theme_mode);
let style = program.style(theme.as_ref().unwrap_or(&system_theme));
let default_theme = <P::Theme as theme::Base>::default(theme_mode);
let style = program.style(theme.as_ref().unwrap_or(&default_theme));
let viewport = {
let physical_size = window.inner_size();
@ -78,7 +80,7 @@ where
cursor_position: None,
modifiers: winit::keyboard::ModifiersState::default(),
theme,
system_theme,
default_theme,
style,
}
}
@ -130,7 +132,7 @@ where
/// Returns the current theme of the [`State`].
pub fn theme(&self) -> &P::Theme {
self.theme.as_ref().unwrap_or(&self.system_theme)
self.theme.as_ref().unwrap_or(&self.default_theme)
}
/// Returns the current background [`Color`] of the [`State`].
@ -144,7 +146,12 @@ where
}
/// Processes the provided window event and updates the [`State`] accordingly.
pub fn update(&mut self, window: &Window, event: &WindowEvent) {
pub fn update(
&mut self,
program: &program::Instance<P>,
window: &Window,
event: &WindowEvent,
) {
match event {
WindowEvent::Resized(new_size) => {
let size = Size::new(new_size.width, new_size.height);
@ -182,9 +189,10 @@ where
self.modifiers = new_modifiers.state();
}
WindowEvent::ThemeChanged(theme) => {
self.system_theme = <P::Theme as theme::Base>::default(
self.default_theme = <P::Theme as theme::Base>::default(
conversion::theme_mode(*theme),
);
self.style = program.style(self.theme());
if self.theme.is_none() {
window.request_redraw();
@ -198,7 +206,7 @@ where
/// window.
///
/// Normally, a [`Program`] should be synchronized with its [`State`]
/// and window after calling [`State::update`].
/// and window after calling [`Program::update`].
pub fn synchronize(
&mut self,
program: &program::Instance<P>,