Skip to content

macOS: Set the theme on the NSWindow, instead of application-wide#3736

Merged
kchibisov merged 1 commit intomasterfrom
madsmtm/macos-appearance
Jun 20, 2024
Merged

macOS: Set the theme on the NSWindow, instead of application-wide#3736
kchibisov merged 1 commit intomasterfrom
madsmtm/macos-appearance

Conversation

@madsmtm
Copy link
Copy Markdown
Member

@madsmtm madsmtm commented Jun 17, 2024

This new implementation uses:

  • The NSAppearanceCustomization protocol for retrieving the appearance of the window, instead of using the application-wide -[NSApplication effectiveAppearance].
  • Key-Value observing for observing the effectiveAppearance to compute the ThemeChanged event, instead of using the undocumented AppleInterfaceThemeChangedNotification notification.

This also fixes WindowBuilder::with_theme not having any effect (fixes alacritty/alacritty#8046), and the conversion between Theme and NSAppearance is made a bit more robust.

  • Tested on all platforms changed
  • Added an entry to the changelog module if knowledge of this change could be valuable to users
  • Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior
  • Created or updated an example program if it would help users understand this functionality

@madsmtm madsmtm added B - bug Dang, that shouldn't have happened S - enhancement Wouldn't this be the coolest? DS - appkit Affects the AppKit/macOS backend labels Jun 17, 2024
@madsmtm madsmtm force-pushed the madsmtm/macos-appearance branch 2 times, most recently from c056a77 to 102ffb3 Compare June 17, 2024 08:04
@madsmtm madsmtm requested a review from kchibisov June 17, 2024 08:04
@madsmtm madsmtm force-pushed the madsmtm/macos-appearance branch 2 times, most recently from 0fcebe1 to 00030a4 Compare June 17, 2024 08:46
_context: *mut c_void,
) {
trace_scope!("observeValueForKeyPath:ofObject:change:context:");
if key_path == Some(ns_string!("effectiveAppearance")) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that entire function is inside the if probably should just early return.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At some point in the future we might get more key_paths that we're observing here, and then it makes sense to handle each in their own if - that's the whole reason why I'm checking the keypath, in reality we don't have to right now, since we're only observing one.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future, I'd prefer each if case like that to be in its own function/method, but for now, I'd use early return, because it's the best thing you can do now.

@madsmtm madsmtm requested a review from kchibisov June 17, 2024 10:31

- On Web, let events wake up event loop immediately when using
`ControlFlow::Poll`.
- On macOS, set the window theme on the `NSWindow` instead of application-wide.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd consider that this is a fix, given that it was not behaving as on any other platform, though, it's a change in behavior which could be excluded from the point release... Not sure about it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's a "change", but I'd be fine with including it in a point release.

While it could technically break applications that relied on the window theme being set for the whole application by setting it on a single window, I think that's neither likely nor something that we intended to support in the first place.

src/event.rs Outdated
Comment on lines +392 to +393
/// This only reports a change if the window theme was not overridden by the application.
///
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the previous sentence says the same thing. system changes the window theme is not a user changes the theme with Window::set_theme.

Copy link
Copy Markdown
Member Author

@madsmtm madsmtm Jun 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed this to reference Window::set_theme specifically instead, though I do think it still makes sense to be explicit about this. Open for a better wording?

_context: *mut c_void,
) {
trace_scope!("observeValueForKeyPath:ofObject:change:context:");
if key_path == Some(ns_string!("effectiveAppearance")) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future, I'd prefer each if case like that to be in its own function/method, but for now, I'd use early return, because it's the best thing you can do now.

@kchibisov kchibisov force-pushed the madsmtm/macos-appearance branch from ca983f6 to b8153fe Compare June 20, 2024 13:51
This new implementation uses:
- The NSAppearanceCustomization protocol for retrieving the appearance
  of the window, instead of using the application-wide
  `-[NSApplication effectiveAppearance]`.
- Key-Value observing for observing the `effectiveAppearance` to compute
  the `ThemeChanged` event, instead of using the undocumented
  `AppleInterfaceThemeChangedNotification` notification.

This also fixes `WindowBuilder::with_theme` not having any effect, and
the conversion between `Theme` and `NSAppearance` is made a bit more
robust.
@tronical
Copy link
Copy Markdown
Contributor

After this change, I can observe the Window::theme() on macOS always returns None unless set_theme() was called. This is strictly as per the documentation, but I see an issue here:

If None has the meaning of "system default is active", and WindowEvent::ThemeChanged(theme) is only sent when the system default changes, then what is the correct API to obtain that initial system default?

Comment on lines +1379 to +1380
/// Returns `None` if the current theme is set as the system default, or if it cannot be
/// determined on the current platform.
Copy link
Copy Markdown
Member

@daxpedda daxpedda Jul 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was overlooked. I think earlier conversations about the ThemeChanged event and set_theme() were accidentally equated with the getter.

Web definitely does support theme() and does actually return the current theme even if its the default.
Windows, the only other platform implementing this API, also returns the current theme, even without an override.

So this documentation/implementation is not correct.
I would argue that the solution would probably be to introduce a second method for returning the system theme, while leaving this method exactly in place as it is.
Windows would have to be adjusted and Web would always return None in Window::theme().

I will open a separate issue for this.
Thanks @tronical.

@daxpedda
Copy link
Copy Markdown
Member

See #3837.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

B - bug Dang, that shouldn't have happened DS - appkit Affects the AppKit/macOS backend S - enhancement Wouldn't this be the coolest?

Development

Successfully merging this pull request may close these issues.

Not picking up decorations_theme_variant on startup on macOS

4 participants