Skip to main content

gpui_macos/
system_theme.rs

1//! Appearance policy and per-window system observation.
2use crate::{ActiveTheme, Theme};
3use gpui_kit::{App, Global, Window};
4use std::collections::HashSet;
5#[derive(Default)]
6struct AppearancePolicy {
7    following: bool,
8    windows: HashSet<gpui_kit::WindowId>,
9}
10impl Global for AppearancePolicy {}
11
12impl Theme {
13    /// Restore system appearance, preserving accent and material preferences.
14    /// Attach [`crate::observe_window`] once to each newly opened window.
15    pub fn follow_system(cx: &mut App) {
16        cx.set_window_appearance(None);
17        cx.default_global::<AppearancePolicy>().following = true;
18        sync(cx);
19    }
20    pub fn is_following_system(cx: &App) -> bool {
21        cx.try_global::<AppearancePolicy>()
22            .is_some_and(|policy| policy.following)
23    }
24}
25pub(crate) fn stop_following(cx: &mut App) {
26    cx.default_global::<AppearancePolicy>().following = false;
27}
28fn sync(cx: &mut App) {
29    let mut next = Theme::system(cx);
30    if let Some(current) = cx.try_global::<Theme>() {
31        next.glass_tint = current.glass_tint;
32        // An accent explicitly set by an app should survive system appearance changes.
33        let default = if current.is_dark() {
34            Theme::dark()
35        } else {
36            Theme::light()
37        };
38        if current.accent != default.accent {
39            next = next.with_accent(current.accent);
40        }
41    }
42    if cx.try_global::<Theme>() != Some(&next) {
43        next.apply_palette(cx);
44    }
45}
46/// Follow macOS light/dark changes for this window. The observer lives with it.
47/// Repeated calls on the same window do not install duplicate subscriptions.
48pub fn observe_window(window: &mut Window, cx: &mut App) {
49    if cx
50        .default_global::<AppearancePolicy>()
51        .windows
52        .insert(window.window_handle().window_id())
53    {
54        window
55            .observe_window_appearance(|_, cx| {
56                if Theme::is_following_system(cx) {
57                    sync(cx);
58                }
59                gpui_kit::base::apply_system_reduce_motion(cx);
60            })
61            .detach();
62    }
63}
64/// Re-read Display accessibility preferences after an application settings change.
65pub fn refresh_accessibility(cx: &mut App) {
66    crate::native::refresh_sidebar_metrics();
67    let mut theme = cx.macos().clone();
68    theme.read_accessibility();
69    theme.apply_palette(cx);
70    gpui_kit::base::apply_system_reduce_motion(cx);
71}