From c322179bb99fe98b2b5be281cfd23d0cbef9a3c6 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Wed, 7 Feb 2024 11:51:18 -0500 Subject: [PATCH] Initialize the `SystemAppearance` using the app's global window appearance (#7508) This PR changes our approach to initializing the `SystemAppearance` so that we can do it earlier in the startup process. Previously we were using the appearance from the window, meaning that we couldn't initialize the value until we first opened the window. Now we read the `window_appearance` from the `AppContext`. On macOS this is backed by the [`effectiveAppearance`](https://developer.apple.com/documentation/appkit/nsapplication/2967171-effectiveappearance) on the `NSApplication`. We currently still watch for changes to the appearance at the window level, as the only hook I could find in the documentation is [`viewDidChangeEffectiveAppearance`](https://developer.apple.com/documentation/appkit/nsview/2977088-viewdidchangeeffectiveappearance), which is at the `NSView` level. In my testing this makes it so Zed appropriately chooses the correct light/dark theme on startup. Release Notes: - N/A --- .../incoming_call_notification.rs | 4 +-- .../project_shared_notification.rs | 4 +-- crates/gpui/src/app.rs | 6 +++++ crates/gpui/src/platform.rs | 26 +++++++++++-------- crates/gpui/src/platform/mac/platform.rs | 11 +++++++- crates/gpui/src/platform/test/platform.rs | 6 ++++- crates/theme/src/settings.rs | 14 +++++----- crates/workspace/src/workspace.rs | 2 -- crates/zed/src/main.rs | 1 + 9 files changed, 46 insertions(+), 28 deletions(-) diff --git a/crates/collab_ui/src/notifications/incoming_call_notification.rs b/crates/collab_ui/src/notifications/incoming_call_notification.rs index 12662fe6cbc8ae6c4cc596c742e78a44eaa770c5..f66194c52a0006c46c37cba2514e505c4ee5ec9b 100644 --- a/crates/collab_ui/src/notifications/incoming_call_notification.rs +++ b/crates/collab_ui/src/notifications/incoming_call_notification.rs @@ -5,7 +5,7 @@ use futures::StreamExt; use gpui::{prelude::*, AppContext, WindowHandle}; use settings::Settings; use std::sync::{Arc, Weak}; -use theme::{SystemAppearance, ThemeSettings}; +use theme::ThemeSettings; use ui::{prelude::*, Button, Label}; use util::ResultExt; use workspace::AppState; @@ -35,8 +35,6 @@ pub fn init(app_state: &Arc, cx: &mut AppContext) { let options = notification_window_options(screen, window_size); let window = cx .open_window(options, |cx| { - SystemAppearance::init_for_window(cx); - cx.new_view(|_| { IncomingCallNotification::new( incoming_call.clone(), diff --git a/crates/collab_ui/src/notifications/project_shared_notification.rs b/crates/collab_ui/src/notifications/project_shared_notification.rs index bb70fc9571684cc9ec441855449c835401eec8e7..b8ceefcd765f4e1797bcf1584ac93594a1fffdaa 100644 --- a/crates/collab_ui/src/notifications/project_shared_notification.rs +++ b/crates/collab_ui/src/notifications/project_shared_notification.rs @@ -6,7 +6,7 @@ use collections::HashMap; use gpui::{AppContext, Size}; use settings::Settings; use std::sync::{Arc, Weak}; -use theme::{SystemAppearance, ThemeSettings}; +use theme::ThemeSettings; use ui::{prelude::*, Button, Label}; use workspace::AppState; @@ -28,8 +28,6 @@ pub fn init(app_state: &Arc, cx: &mut AppContext) { for screen in cx.displays() { let options = notification_window_options(screen, window_size); let window = cx.open_window(options, |cx| { - SystemAppearance::init_for_window(cx); - cx.new_view(|_| { ProjectSharedNotification::new( owner.clone(), diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 45c26b0ba81029b2490274cc4cd491ad522b946f..85d76bb3faa624309c9daaad210b97b834dda792 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -14,6 +14,7 @@ use smol::future::FutureExt; pub use test_context::*; use time::UtcOffset; +use crate::WindowAppearance; use crate::{ current_platform, image_cache::ImageCache, init_app_menus, Action, ActionRegistry, Any, AnyView, AnyWindowHandle, AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context, @@ -522,6 +523,11 @@ impl AppContext { self.platform.displays() } + /// Returns the appearance of the application's windows. + pub fn window_appearance(&self) -> WindowAppearance { + self.platform.window_appearance() + } + /// Writes data to the platform clipboard. pub fn write_to_clipboard(&self, item: ClipboardItem) { self.platform.write_to_clipboard(item) diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 62ac543319417207a454f2fa31f987c2289f4ad3..612a2711111b3f72e803ca4ccd4c151e507a6eaf 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -67,6 +67,9 @@ pub(crate) trait Platform: 'static { options: WindowOptions, ) -> Box; + /// Returns the appearance of the application's windows. + fn window_appearance(&self) -> WindowAppearance; + fn set_display_link_output_callback( &self, display_id: DisplayId, @@ -559,29 +562,30 @@ pub enum WindowBounds { Fixed(Bounds), } -/// The appearance of the window, as defined by the operating system -/// On macOS, this corresponds to named [NSAppearance](https://developer.apple.com/documentation/appkit/nsappearance) -/// values +/// The appearance of the window, as defined by the operating system. +/// +/// On macOS, this corresponds to named [`NSAppearance`](https://developer.apple.com/documentation/appkit/nsappearance) +/// values. #[derive(Copy, Clone, Debug)] pub enum WindowAppearance { - /// A light appearance + /// A light appearance. /// - /// on macOS, this corresponds to the `aqua` appearance + /// On macOS, this corresponds to the `aqua` appearance. Light, - /// A light appearance with vibrant colors + /// A light appearance with vibrant colors. /// - /// on macOS, this corresponds to the `NSAppearanceNameVibrantLight` appearance + /// On macOS, this corresponds to the `NSAppearanceNameVibrantLight` appearance. VibrantLight, - /// A dark appearance + /// A dark appearance. /// - /// on macOS, this corresponds to the `darkAqua` appearance + /// On macOS, this corresponds to the `darkAqua` appearance. Dark, - /// A dark appearance with vibrant colors + /// A dark appearance with vibrant colors. /// - /// on macOS, this corresponds to the `NSAppearanceNameVibrantDark` appearance + /// On macOS, this corresponds to the `NSAppearanceNameVibrantDark` appearance. VibrantDark, } diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 0e3864065f1675cb9da15f145410010859405738..d2cbd52574605bea47d058cecd15253be012c9dc 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -3,7 +3,8 @@ use crate::{ Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, ForegroundExecutor, Keymap, MacDispatcher, MacDisplay, MacDisplayLinker, MacTextSystem, MacWindow, Menu, MenuItem, PathPromptOptions, Platform, PlatformDisplay, PlatformInput, - PlatformTextSystem, PlatformWindow, Result, SemanticVersion, Task, WindowOptions, + PlatformTextSystem, PlatformWindow, Result, SemanticVersion, Task, WindowAppearance, + WindowOptions, }; use anyhow::anyhow; use block::ConcreteBlock; @@ -505,6 +506,14 @@ impl Platform for MacPlatform { )) } + fn window_appearance(&self) -> WindowAppearance { + unsafe { + let app = NSApplication::sharedApplication(nil); + let appearance: id = msg_send![app, effectiveAppearance]; + WindowAppearance::from_native(appearance) + } + } + fn set_display_link_output_callback( &self, display_id: DisplayId, diff --git a/crates/gpui/src/platform/test/platform.rs b/crates/gpui/src/platform/test/platform.rs index 4d954b32669f653e70e14d60279532e8cdfc0d1c..b7e1c99f30becbef0cd42b1bd5d644bac3a1907b 100644 --- a/crates/gpui/src/platform/test/platform.rs +++ b/crates/gpui/src/platform/test/platform.rs @@ -1,7 +1,7 @@ use crate::{ AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, ForegroundExecutor, Keymap, Platform, PlatformDisplay, PlatformTextSystem, Task, TestDisplay, TestWindow, - WindowOptions, + WindowAppearance, WindowOptions, }; use anyhow::{anyhow, Result}; use collections::VecDeque; @@ -178,6 +178,10 @@ impl Platform for TestPlatform { Box::new(window) } + fn window_appearance(&self) -> WindowAppearance { + WindowAppearance::Light + } + fn set_display_link_output_callback( &self, _display_id: DisplayId, diff --git a/crates/theme/src/settings.rs b/crates/theme/src/settings.rs index 17404d7c67c6dcdd25a8fdfabf2ba9560a10724f..dd1623970841988b6207390e51c2546fd37ae3f6 100644 --- a/crates/theme/src/settings.rs +++ b/crates/theme/src/settings.rs @@ -4,7 +4,7 @@ use anyhow::Result; use derive_more::{Deref, DerefMut}; use gpui::{ px, AppContext, Font, FontFeatures, FontStyle, FontWeight, Global, Pixels, Subscription, - ViewContext, WindowContext, + ViewContext, }; use refineable::Refineable; use schemars::{ @@ -49,6 +49,12 @@ struct GlobalSystemAppearance(SystemAppearance); impl Global for GlobalSystemAppearance {} impl SystemAppearance { + /// Initializes the [`SystemAppearance`] for the application. + pub fn init(cx: &mut AppContext) { + *cx.default_global::() = + GlobalSystemAppearance(SystemAppearance(cx.window_appearance().into())); + } + /// Returns the global [`SystemAppearance`]. /// /// Inserts a default [`SystemAppearance`] if one does not yet exist. @@ -56,12 +62,6 @@ impl SystemAppearance { cx.default_global::().0 } - /// Initializes the [`SystemAppearance`] for the current window. - pub fn init_for_window(cx: &mut WindowContext) { - *cx.default_global::() = - GlobalSystemAppearance(SystemAppearance(cx.appearance().into())); - } - /// Returns the global [`SystemAppearance`]. pub fn global(cx: &AppContext) -> Self { cx.global::().0 diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 181f14386de4786aac1ed5a88ed641f7e6d46b55..77a2a47400335afc4758a7b21ec23473f3c7e2a9 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -855,8 +855,6 @@ impl Workspace { let workspace_id = workspace_id.clone(); let project_handle = project_handle.clone(); move |cx| { - SystemAppearance::init_for_window(cx); - cx.new_view(|cx| { Workspace::new(workspace_id, project_handle, app_state, cx) }) diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 33fcfc040f531f6adc36f5c3601cef1cbe312ba6..f010468d26a508cb7ee17a5db1c88b1f7d4da96d 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -126,6 +126,7 @@ fn main() { AppCommitSha::set_global(AppCommitSha(build_sha.into()), cx); } + SystemAppearance::init(cx); OpenListener::set_global(listener.clone(), cx); load_embedded_fonts(cx);