Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add accent color support for macOS #589

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/macos-accent-color.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tao": minor
---

Add support and `WindowEvent` for accent color on macOS.
7 changes: 7 additions & 0 deletions examples/theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ fn main() {
.unwrap();

println!("Initial theme: {:?}", window.theme());
println!("Initial accent color: {:?}", window.accent_color());

event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
Expand All @@ -36,6 +37,12 @@ fn main() {
} if window_id == window.id() => {
println!("Theme is changed: {:?}", theme)
}
Event::WindowEvent {
event: WindowEvent::AccentColorChanged(accent_color),
..
} => {
println!("Accent color has changed: {:?}", accent_color)
}
_ => (),
}
});
Expand Down
11 changes: 10 additions & 1 deletion src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ use crate::{
keyboard::{self, ModifiersState},
menu::{MenuId, MenuType},
platform_impl,
window::{Theme, WindowId},
window::{AccentColor, Theme, WindowId},
};

/// Describes a generic event.
Expand Down Expand Up @@ -482,6 +482,13 @@ pub enum WindowEvent<'a> {
/// - **Linux / Android / iOS:** Unsupported
ThemeChanged(Theme),

/// The system accent color has changed.
///
/// ## Platform-specific
///
/// - **iOS / Android / Linux / Windows:** Unsupported.
AccentColorChanged(Option<AccentColor>),

/// The window decorations has been clicked.
///
/// ## Platform-specific
Expand Down Expand Up @@ -574,6 +581,7 @@ impl Clone for WindowEvent<'static> {
},
Touch(touch) => Touch(*touch),
ThemeChanged(theme) => ThemeChanged(*theme),
AccentColorChanged(color) => AccentColorChanged(*color),
ScaleFactorChanged { .. } => {
unreachable!("Static event can't be about scale factor changing")
}
Expand Down Expand Up @@ -661,6 +669,7 @@ impl<'a> WindowEvent<'a> {
}),
Touch(touch) => Some(Touch(touch)),
ThemeChanged(theme) => Some(ThemeChanged(theme)),
AccentColorChanged(color) => Some(AccentColorChanged(color)),
ScaleFactorChanged { .. } => None,
DecorationsClick => Some(DecorationsClick),
}
Expand Down
41 changes: 40 additions & 1 deletion src/platform_impl/macos/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ use crate::{
OsError,
},
window::{
CursorIcon, Fullscreen, Theme, UserAttentionType, WindowAttributes, WindowId as RootWindowId,
AccentColor, CursorIcon, Fullscreen, Theme, UserAttentionType, WindowAttributes,
WindowId as RootWindowId,
},
};
use cocoa::{
Expand Down Expand Up @@ -336,6 +337,32 @@ pub(super) fn set_ns_theme(theme: Theme) {
}
}

pub(super) fn get_ns_accent_color() -> Option<AccentColor> {
unsafe {
let key_name = NSString::alloc(nil).init_str("AppleAccentColor");

let user_defaults: id = msg_send![class!(NSUserDefaults), standardUserDefaults];
let color_obj: id = msg_send![user_defaults, objectForKey: key_name];

if color_obj == nil {
return None;
}

let color_int: id = msg_send![user_defaults, integerForKey: key_name];

match color_int as i8 {
-1 => Some(AccentColor::Graphite),
0 => Some(AccentColor::Red),
1 => Some(AccentColor::Orange),
2 => Some(AccentColor::Yellow),
3 => Some(AccentColor::Green),
4 => Some(AccentColor::Blue),
5 => Some(AccentColor::Purple),
6 => Some(AccentColor::Pink),
_ => None,
}
}
}
struct WindowClass(*const Class);
unsafe impl Send for WindowClass {}
unsafe impl Sync for WindowClass {}
Expand Down Expand Up @@ -402,6 +429,7 @@ pub struct SharedState {
save_presentation_opts: Option<NSApplicationPresentationOptions>,
pub saved_desktop_display_mode: Option<(CGDisplay, CGDisplayMode)>,
pub current_theme: Theme,
pub current_accent_color: Option<AccentColor>,
}

impl SharedState {
Expand Down Expand Up @@ -537,6 +565,11 @@ impl UnownedWindow {
}
}

{
let mut state = window.shared_state.lock().unwrap();
state.current_accent_color = get_ns_accent_color();
}

let delegate = new_delegate(&window, fullscreen.is_some());

// Set fullscreen mode after we setup everything
Expand Down Expand Up @@ -1341,6 +1374,12 @@ impl UnownedWindow {
state.current_theme
}

#[inline]
pub fn accent_color(&self) -> Option<AccentColor> {
let state = self.shared_state.lock().unwrap();
state.current_accent_color
}

pub fn set_content_protection(&self, enabled: bool) {
unsafe {
let _: () = msg_send![*self.ns_window, setSharingType: !enabled as i32];
Expand Down
28 changes: 27 additions & 1 deletion src/platform_impl/macos/window_delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use crate::{
event::{EventProxy, EventWrapper},
util::{self, IdRef},
view::ViewState,
window::{get_ns_theme, get_window_id, UnownedWindow},
window::{get_ns_accent_color, get_ns_theme, get_window_id, UnownedWindow},
},
window::{Fullscreen, WindowId},
};
Expand Down Expand Up @@ -252,6 +252,10 @@ lazy_static! {
sel!(effectiveAppearanceDidChangedOnMainThread:),
effective_appearance_did_changed_on_main_thread as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(accentColorChanged:),
accent_color_did_change as extern "C" fn(&Object, Sel, id),
);

decl.add_ivar::<*mut c_void>("taoState");
WindowDelegateClass(decl.register())
Expand Down Expand Up @@ -295,6 +299,17 @@ extern "C" fn init_with_tao(this: &Object, _sel: Sel, state: *mut c_void) -> id
object: nil
];

let notification_accent_color_name =
NSString::alloc(nil).init_str("AppleColorPreferencesChangedNotification");

let _: () = msg_send![
notification_center,
addObserver: this
selector: sel!(accentColorChanged:)
name: notification_accent_color_name
object: nil
];

this
}
}
Expand Down Expand Up @@ -659,3 +674,14 @@ extern "C" fn effective_appearance_did_changed_on_main_thread(this: &Object, _:
});
trace!("Completed `effectiveAppearDidChange:`");
}

extern "C" fn accent_color_did_change(this: &Object, _: Sel, _: id) {
with_state(this, |state| {
let accent_color = get_ns_accent_color();
state.with_window(|window| {
let mut shared_state = window.shared_state.lock().unwrap();
shared_state.current_accent_color = accent_color;
});
state.emit_event(WindowEvent::AccentColorChanged(accent_color));
});
}
24 changes: 24 additions & 0 deletions src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1062,6 +1062,17 @@ impl Window {
self.window.theme()
}

/// Returns the accent color of the system.
///
/// ## Platform-specific
///
/// - **iOS / Android / Linux / Windows:** Unsupported.
#[inline]
pub fn accent_color(&self) -> Option<AccentColor> {
#[cfg(target_os = "macos")]
self.window.accent_color()
}

/// Prevents the window contents from being captured by other apps.
///
/// ## Platform-specific
Expand Down Expand Up @@ -1303,6 +1314,19 @@ pub enum Theme {
Dark,
}

#[non_exhaustive]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum AccentColor {
Graphite,
Red,
Orange,
Yellow,
Green,
Blue,
Purple,
Pink,
}

impl Default for Theme {
fn default() -> Self {
Theme::Light
Expand Down