Skip to content

Ensure that ActualThemeVariant always has a proper value - #21930

Merged
MrJul merged 10 commits into
AvaloniaUI:mainfrom
MrJul:fix/actualthemevariant
Aug 18, 2026
Merged

MrJul merged 10 commits into
AvaloniaUI:mainfrom
MrJul:fix/actualthemevariant

Conversation

@MrJul

@MrJul MrJul commented Aug 5, 2026

Copy link
Copy Markdown
Member

What does the pull request do?

This PR ensures that ActualThemeVariant always has a proper value (typically Light/Dark).
Currently, there are cases where ActualThemeVariant gets set to null.

It was actually implemented in #16340 but caused several other problems. First, the fact that ActualThemeVariant could have an invalid value prevented the theme from propagating correctly. This led to workarounds like have to raise PlatformSettings.ColorValuesChanged to properly propagate the theme, even though the system settings didn't really change. Which caused in turn performance issues such as #21897.

Instead, this PR sets ActualThemeVariant properly again on Application/TopLevel/ThemeVariantScope whenever the RequestedThemeVariant or the system settings change.

Doing so actually regressed #16340 on Android though, as a non-null ThemeVariant is now again always passed to ITopLevelImpl.SetFrameThemeVariant(), causing the app to force a default mode, preventing system changes from happening (as documented). Instead, a local night mode is now set, and a custom receiver listens to configuration changes.

Unit tests have been added for non-platform code (verifying ActualThemeVariant).
Windows, macOS, Android, iOS, X11, Wayland and Browser have all been modified and re-tested.

Fixed issues

@MrJul MrJul added bug backport-candidate-12.1.x Consider this PR for backporting to 12.1 branch labels Aug 5, 2026
@rabbitism

Copy link
Copy Markdown
Contributor

@zdpcdt emm again

@MrJul
MrJul force-pushed the fix/actualthemevariant branch from cf0b2f1 to 074e7aa Compare August 5, 2026 15:11
@avaloniaui-bot

Copy link
Copy Markdown

You can test this PR using the following package version. 12.2.999-cibuild0068125-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR reworks theme-variant propagation so ActualThemeVariant is always resolved to a concrete value (typically Light/Dark) at theme roots (Application/TopLevel/etc.), and stops using platform “workarounds” (like raising ColorValuesChanged from SetFrameThemeVariant) that caused performance regressions.

Changes:

  • Centralizes RequestedThemeVariant → ActualThemeVariant resolution in ThemeVariant.UpdateActualThemeVariant using a new IThemeVariantRoot marker for platform-root resolution vs. parent inheritance.
  • Updates multiple platform backends to cache/compare PlatformColorValues and avoid spurious ColorValuesChanged notifications (notably Win32), plus Android-specific changes for night mode/config change handling.
  • Adds unit-test infrastructure and new tests validating ActualThemeVariant behavior for Application/TopLevel/ThemeVariantScope.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/Avalonia.UnitTests/UnitTestApplication.cs Allows injecting IPlatformSettings into test app and initializes theme variant resolution.
tests/Avalonia.UnitTests/TestServices.cs Adds PlatformSettings to test service plumbing and With(...) builder.
tests/Avalonia.UnitTests/TestPlatformSettings.cs New test IPlatformSettings implementation with controllable PlatformColorValues.
tests/Avalonia.Controls.UnitTests/ThemeVariantTests.cs New tests covering Application/TopLevel/ThemeVariantScope theme-variant resolution and inheritance.
src/Windows/Avalonia.Win32/WindowImpl.cs Removes unconditional OnColorValuesChanged() call from SetFrameThemeVariant.
src/Windows/Avalonia.Win32/Win32PlatformSettings.cs Caches color values and only raises ColorValuesChanged when values actually change.
src/iOS/Avalonia.iOS/PlatformSettings.cs Caches uncached color values and only raises when changed on trait changes.
src/iOS/Avalonia.iOS/AvaloniaView.cs Removes “null themeVariant triggers platform settings refresh” behavior.
src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs Removes platform-settings refresh trigger when themeVariant is null.
src/Browser/Avalonia.Browser/BrowserPlatformSettings.cs Adds caching + change detection for browser platform color values.
src/Avalonia.X11/X11Window.cs Removes DBus “request default theme” trigger from SetFrameThemeVariant and drops unnecessary async void.
src/Avalonia.Wayland/WindowImplBase.cs Removes DBus “request default theme” trigger from SetFrameThemeVariant and drops unnecessary async void.
src/Avalonia.Native/WindowImplBase.cs Stops forcing platform-settings refresh when themeVariant is null; falls back to platform theme if needed.
src/Avalonia.Native/NativePlatformSettings.cs Caches color values and only raises ColorValuesChanged when values actually change.
src/Avalonia.FreeDesktop/DBusPlatformSettings.cs Refactors DBus settings into cached _colorValues + update-on-change approach.
src/Avalonia.Controls/TopLevel.cs Makes TopLevel a theme-variant root when no application theme host exists; removes old platform binding workaround.
src/Avalonia.Controls/Application.cs Implements theme-variant root behavior and centralizes initialization via InitializeThemeVariant().
src/Avalonia.Base/Styling/ThemeVariant.cs Adds centralized update logic + class handler to keep ActualThemeVariant consistent and platform-resolved at roots.
src/Avalonia.Base/Styling/IThemeVariantRoot.cs New internal interface to identify objects that resolve Default from platform settings.
src/Avalonia.Base/StyledElement.cs Removes duplicated Requested→Actual theme-variant handling (now centralized in ThemeVariant).
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs Uses local night mode instead of global DefaultNightMode; stores context.
src/Android/Avalonia.Android/Platform/AndroidPlatformSettings.cs Refactors to cached _colorValues and adds configuration-change receiver to update values.
src/Android/Avalonia.Android/AvaloniaView.cs Removes direct forwarding of config changes into AndroidPlatformSettings.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/Android/Avalonia.Android/Platform/AndroidPlatformSettings.cs
Comment thread src/Avalonia.FreeDesktop/DBusPlatformSettings.cs Outdated
@avaloniaui-bot

Copy link
Copy Markdown

You can test this PR using the following package version. 12.2.999-cibuild0068220-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@MrJul
MrJul added this pull request to the merge queue Aug 18, 2026
Merged via the queue into AvaloniaUI:main with commit a56f8c1 Aug 18, 2026
10 checks passed
@MrJul
MrJul deleted the fix/actualthemevariant branch August 18, 2026 18:17
MrJul added a commit to MrJul/Avalonia that referenced this pull request Sep 2, 2026
…21930)

* Correctly set ActualThemeVariant

* Don't call OnColorValuesChanged unnecessarily on Win32

* Don't call OnColorValuesChanged unnecessarily on macOS

* Change iOS PlatformSettings to match other platforms

* Don't call OnColorValuesChanged unnecessarily on Android

* Android: set theme using SetLocalNightMode

* Don't call OnColorValuesChanged unnecessarily on X11/Wayland

* Don't call OnColorValuesChanged unnecessarily on Browser

* Fix timer Start order

* Remove dead code in DBusPlatformSettings
@MrJul MrJul added backported-12.1.x and removed backport-candidate-12.1.x Consider this PR for backporting to 12.1 branch labels Sep 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Win32: ColorValuesChanged is raised on every TopLevel construction since 12.1.0, causing UI stalls proportional to visual-tree size

5 participants