Sync window theme with egui theme

This commit is contained in:
lucasmerlin
2026-07-07 10:16:32 +02:00
parent f005960bdc
commit a2695b5546
2 changed files with 56 additions and 0 deletions
+40
View File
@@ -243,6 +243,12 @@ pub struct ViewportState {
// ----------------------
// Cross-frame statistics:
pub num_multipass_in_row: usize,
/// The last theme we sent to the native window via [`ViewportCommand::SetTheme`],
/// used to avoid sending redundant commands.
///
/// See [`crate::Options::sync_window_theme`].
pub(crate) last_sent_window_theme: Option<crate::SystemTheme>,
}
/// What called [`Context::request_repaint`] or [`Context::request_discard`]?
@@ -2385,6 +2391,8 @@ impl Context {
}
}
self.sync_window_theme();
#[cfg(debug_assertions)]
self.debug_painting();
@@ -2396,6 +2404,38 @@ impl Context {
output
}
/// Keep the native window theme in sync with the egui [`ThemePreference`],
/// if [`crate::Options::sync_window_theme`] is enabled.
///
/// Sends a [`ViewportCommand::SetTheme`] to the current viewport whenever the
/// derived theme changes, so the native window decorations match the egui theme.
fn sync_window_theme(&self) {
if !self.options(|o| o.sync_window_theme) {
return;
}
use crate::{SystemTheme, ThemePreference};
let window_theme = match self.options(|o| o.theme_preference) {
ThemePreference::System => SystemTheme::SystemDefault,
ThemePreference::Dark => SystemTheme::Dark,
ThemePreference::Light => SystemTheme::Light,
};
let changed = self.write(|ctx| {
let viewport = ctx.viewport();
if viewport.last_sent_window_theme == Some(window_theme) {
false
} else {
viewport.last_sent_window_theme = Some(window_theme);
true
}
});
if changed {
self.send_viewport_cmd(ViewportCommand::SetTheme(window_theme));
}
}
/// Called at the end of the pass.
#[cfg(debug_assertions)]
fn debug_painting(&self) {
+16
View File
@@ -216,6 +216,18 @@ pub struct Options {
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) system_theme: Option<Theme>,
/// If `true`, egui will keep the native window theme in sync with
/// [`Self::theme_preference`] by sending a [`crate::ViewportCommand::SetTheme`]
/// to the root viewport whenever the preference changes.
///
/// This makes the native window decorations (title bar, borders, …) match the
/// theme selected inside egui.
///
/// Set this to `false` if you want to manage the native window theme yourself.
///
/// This is `true` by default.
pub sync_window_theme: bool,
/// Global zoom factor of the UI.
///
/// This is used to calculate the `pixels_per_point`
@@ -318,6 +330,7 @@ impl Default for Options {
theme_preference: Default::default(),
fallback_theme: Theme::Dark,
system_theme: None,
sync_window_theme: true,
zoom_factor: 1.0,
zoom_with_keyboard: true,
quit_shortcuts: vec![crate::KeyboardShortcut::new(
@@ -381,6 +394,7 @@ impl Options {
theme_preference,
fallback_theme: _,
system_theme: _,
sync_window_theme,
zoom_factor,
zoom_with_keyboard,
quit_shortcuts: _, // not shown in ui
@@ -429,6 +443,8 @@ impl Options {
.show(ui, |ui| {
theme_preference.radio_buttons(ui);
ui.checkbox(sync_window_theme, "Sync window theme with egui theme");
let style = std::sync::Arc::make_mut(match theme {
Theme::Dark => dark_style,
Theme::Light => light_style,