diff --git a/crates/ui-egui/src/theme.rs b/crates/ui-egui/src/theme.rs index e6c06e5dd..3b1a0524d 100644 --- a/crates/ui-egui/src/theme.rs +++ b/crates/ui-egui/src/theme.rs @@ -587,6 +587,15 @@ pub fn apply(ctx: &egui::Context, b: Brightness) { pub fn apply_with(ctx: &egui::Context, b: Brightness, accent: Accent, scheme: Scheme) { let t = Tokens::for_scheme(scheme, b, accent); ctx.data_mut(|d| d.insert_temp(egui::Id::NULL, t)); + // egui keeps a style for each system theme and by default follows the system's setting. The + // chosen brightness sets egui's theme, so these colors stay in force when the system's setting + // differs or changes. A change of theme starts from the current style, which keeps settings + // such as the tool tip delay. + let theme = if t.dark { egui::Theme::Dark } else { egui::Theme::Light }; + if ctx.theme() != theme { + ctx.set_style_of(theme, ctx.global_style()); + } + ctx.set_theme(theme); let mut v = if t.dark { Visuals::dark() } else { Visuals::light() }; v.panel_fill = t.panel; v.window_fill = t.panel; @@ -669,4 +678,55 @@ mod tests { assert_eq!(Scheme::parse(Scheme::Classic.id()), Some(Scheme::Classic)); } } + + /// #3818: the brightness chosen in Preferences stays in force when the system's light or dark + /// setting differs from it or changes while the app runs. Tool tips keep the theme's panel fill + /// and text color instead of egui's own white box and gray text. + #[test] + fn the_theme_holds_whatever_the_system_theme() { + let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(400.0, 400.0)); + for b in Brightness::ALL { + for scheme in Scheme::ALL { + for (first, then) in [(egui::Theme::Dark, egui::Theme::Light), (egui::Theme::Light, egui::Theme::Dark)] { + let t = Tokens::for_scheme(scheme, b, Accent::default()); + let ctx = egui::Context::default(); + let mut time = 0.0; + let mut tip = None; + for i in 0..5 { + time += 0.5; + let system_theme = Some(if i == 0 { first } else { then }); + let events = if i == 1 { vec![egui::Event::PointerMoved(egui::pos2(30.0, 15.0))] } else { vec![] }; + let input = egui::RawInput { time: Some(time), screen_rect: Some(screen), events, system_theme, ..Default::default() }; + let mut out = ctx.run_ui(input, |ui| { + if i == 0 { + apply_with(ui.ctx(), b, Accent::default(), scheme); + ui.ctx().global_style_mut(|s| s.interaction.tooltip_delay = 0.0); + } + ui.button("Hover me").on_hover_text("Tip text"); + }); + out.textures_delta.clear(); + tip = out.shapes.iter().find_map(|c| match &c.shape { + egui::Shape::Text(s) if s.galley.text() == "Tip text" => Some(s.fallback_color), + _ => None, + }); + } + let v = ctx.global_style().visuals.clone(); + assert_eq!((v.window_fill, v.override_text_color), (t.panel, Some(t.text)), "{scheme:?} {b:?}, system {first:?} then {then:?}"); + assert_eq!(tip, Some(t.text), "{scheme:?} {b:?}, system {first:?} then {then:?}: the tool tip's text"); + } + } + } + } + + /// Changing brightness keeps the interface settings made after the theme was applied, such as + /// General › Show Tool Tips' delay. + #[test] + fn changing_brightness_keeps_other_interface_settings() { + let ctx = egui::Context::default(); + apply(&ctx, Brightness::Dark); + ctx.global_style_mut(|s| s.interaction.tooltip_delay = f32::INFINITY); + apply(&ctx, Brightness::Light); + assert_eq!(ctx.global_style().interaction.tooltip_delay, f32::INFINITY); + assert_eq!(ctx.global_style().visuals.window_fill, Tokens::for_brightness(Brightness::Light).panel); + } }