diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index aae8255..8f86319 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -166,6 +166,8 @@ pub struct CadApp { styled: bool, /// The theme last installed into egui (`None` until the first frame). shown_theme: Option, + /// Whether the last applied theme was handed to egui as the System preference. + shown_follows_system: bool, pub frame_ms: f64, pub quit_requested: bool, /// The app may close although drawings have unsaved changes (they were dealt with, or the @@ -195,6 +197,7 @@ impl CadApp { frame_cap: None, styled: false, shown_theme: None, + shown_follows_system: false, frame_ms: 0.0, quit_requested: false, quit_confirmed: false, @@ -359,9 +362,11 @@ impl CadApp { // browser report changes as input); the saved choice itself never changes here. let shown = self.ui.theme.resolve(ctx.system_theme()); theme::set_active(shown); - if self.shown_theme != Some(shown) { - theme::apply(ctx, shown); + let follows_system = self.ui.theme == theme::ThemePref::System; + if self.shown_theme != Some(shown) || self.shown_follows_system != follows_system { + theme::apply(ctx, shown, follows_system); self.shown_theme = Some(shown); + self.shown_follows_system = follows_system; } self.drain_control(ctx); if !self.synthetic.is_empty() { @@ -582,6 +587,22 @@ mod tests { assert_eq!(a.prefs_json(), r#"{"interfaceLanguage":"auto","saveFormat":"dxf","theme":"system"}"#); } + #[test] + fn system_theme_keeps_egui_on_the_system_preference() { + let mut a = app(); + a.start("ui.theme.system"); + let ctx = egui::Context::default(); + frame(&mut a, &ctx, Some(egui::Theme::Light)); + // A concrete Light would pin the native window appearance and hide later OS changes. + assert_eq!(ctx.options(|o| o.theme_preference), egui::ThemePreference::System); + a.start("ui.theme.dark"); + frame(&mut a, &ctx, Some(egui::Theme::Light)); + assert_eq!(ctx.options(|o| o.theme_preference), egui::ThemePreference::Dark); + a.start("ui.theme.system"); + frame(&mut a, &ctx, Some(egui::Theme::Light)); + assert_eq!(ctx.options(|o| o.theme_preference), egui::ThemePreference::System); + } + #[test] fn default_save_format_persists_and_names_new_drawings() { let mut a = app(); diff --git a/crates/ui-egui/src/theme.rs b/crates/ui-egui/src/theme.rs index 0c299bd..00c541c 100644 --- a/crates/ui-egui/src/theme.rs +++ b/crates/ui-egui/src/theme.rs @@ -284,12 +284,15 @@ fn visuals(theme: Theme) -> Visuals { } /// Install the styles of both themes and show `theme` (egui's own widgets, popups and menus -/// follow it; our own painting follows [`Tokens::get`]). -pub fn apply(ctx: &egui::Context, theme: Theme) { +/// follow it; our own painting follows [`Tokens::get`]). `follow_system` is true for the +/// [`ThemePref::System`] choice, whose `theme` was resolved from the OS report. +pub fn apply(ctx: &egui::Context, theme: Theme, follow_system: bool) { set_active(theme); ctx.set_visuals_of(Theme::Dark, visuals(Theme::Dark)); ctx.set_visuals_of(Theme::Light, visuals(Theme::Light)); - ctx.set_theme(theme); + // A System choice keeps egui's own System preference: a concrete Light/Dark would pin the native + // window appearance (winit then stops reporting OS changes) and the app would never follow again. + ctx.set_theme(if follow_system { egui::ThemePreference::System } else { egui::ThemePreference::from(theme) }); ctx.all_styles_mut(|s| { s.spacing.item_spacing = egui::vec2(6.0, 4.0); s.spacing.button_padding = egui::vec2(6.0, 3.0);