diff --git a/api/Avalonia.nupkg.xml b/api/Avalonia.nupkg.xml
index d66ef8f68d..4b2aa63ef1 100644
--- a/api/Avalonia.nupkg.xml
+++ b/api/Avalonia.nupkg.xml
@@ -43,6 +43,18 @@
baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+ CP0002
+ M:Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetFrameThemeVariant(Avalonia.Platform.PlatformThemeVariant)
+ baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll
+ current/Avalonia/lib/net10.0/Avalonia.Controls.dll
+
+
+ CP0002
+ M:Avalonia.Platform.ITopLevelImpl.SetFrameThemeVariant(Avalonia.Platform.PlatformThemeVariant)
+ baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll
+ current/Avalonia/lib/net10.0/Avalonia.Controls.dll
+
CP0002
M:Avalonia.OpenGL.Egl.EglPlatformSurfaceRenderTargetBase.BeginDraw(Avalonia.OpenGL.Egl.EglSurface,Avalonia.PixelSize,System.Double,System.Action,System.Boolean)
@@ -91,6 +103,18 @@
baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+ CP0002
+ M:Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetFrameThemeVariant(Avalonia.Platform.PlatformThemeVariant)
+ baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll
+ current/Avalonia/lib/net8.0/Avalonia.Controls.dll
+
+
+ CP0002
+ M:Avalonia.Platform.ITopLevelImpl.SetFrameThemeVariant(Avalonia.Platform.PlatformThemeVariant)
+ baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll
+ current/Avalonia/lib/net8.0/Avalonia.Controls.dll
+
CP0002
M:Avalonia.OpenGL.Egl.EglPlatformSurfaceRenderTargetBase.BeginDraw(Avalonia.OpenGL.Egl.EglSurface,Avalonia.PixelSize,System.Double,System.Action,System.Boolean)
@@ -103,10 +127,22 @@
baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll
current/Avalonia/lib/net10.0/Avalonia.Controls.dll
+
+ CP0006
+ M:Avalonia.Platform.ITopLevelImpl.SetFrameThemeVariant(System.Nullable{Avalonia.Platform.PlatformThemeVariant})
+ baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll
+ current/Avalonia/lib/net10.0/Avalonia.Controls.dll
+
CP0006
M:Avalonia.Controls.Platform.IWin32OptionsTopLevelImpl.SetWindowCornerPreference(Avalonia.Controls.Win32Properties.WindowCornerPreference)
baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll
current/Avalonia/lib/net8.0/Avalonia.Controls.dll
+
+ CP0006
+ M:Avalonia.Platform.ITopLevelImpl.SetFrameThemeVariant(System.Nullable{Avalonia.Platform.PlatformThemeVariant})
+ baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll
+ current/Avalonia/lib/net8.0/Avalonia.Controls.dll
+
diff --git a/samples/SafeAreaDemo/App.xaml b/samples/SafeAreaDemo/App.xaml
index f406bfe710..70fd759dad 100644
--- a/samples/SafeAreaDemo/App.xaml
+++ b/samples/SafeAreaDemo/App.xaml
@@ -2,7 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:SafeAreaDemo"
x:Class="SafeAreaDemo.App"
- RequestedThemeVariant="Light">
+ RequestedThemeVariant="Default">
diff --git a/samples/SafeAreaDemo/ViewModels/MainViewModel.cs b/samples/SafeAreaDemo/ViewModels/MainViewModel.cs
index 4679948a6a..ef7fd35651 100644
--- a/samples/SafeAreaDemo/ViewModels/MainViewModel.cs
+++ b/samples/SafeAreaDemo/ViewModels/MainViewModel.cs
@@ -3,6 +3,7 @@ using Avalonia;
using Avalonia.Animation.Easings;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
+using Avalonia.Styling;
using MiniMvvm;
namespace SafeAreaDemo.ViewModels
@@ -33,7 +34,7 @@ namespace SafeAreaDemo.ViewModels
public Rect InputPaneRect => _inputPane?.OccludedRect ?? default;
public Rect CanvasSize { get; set; }
-
+
public Thickness SafeAreaPadding
{
get
@@ -105,7 +106,7 @@ namespace SafeAreaDemo.ViewModels
set
{
_autoSafeAreaPadding = value;
-
+
RaisePropertyChanged();
RaiseSafeAreaChanged();
}
@@ -159,7 +160,7 @@ namespace SafeAreaDemo.ViewModels
this.RaisePropertyChanged(nameof(ViewPadding));
this.RaisePropertyChanged(nameof(InputPaneMarkerMargin));
}
-
+
private void RaiseKeyboardChanged()
{
this.RaisePropertyChanged(nameof(InputPaneState));
@@ -168,5 +169,29 @@ namespace SafeAreaDemo.ViewModels
this.RaisePropertyChanged(nameof(InputPaneDuration));
this.RaisePropertyChanged(nameof(InputPaneMarkerMargin));
}
+
+ public void SetLight()
+ {
+ if (Application.Current is { } application)
+ {
+ application.RequestedThemeVariant = ThemeVariant.Light;
+ }
+ }
+
+ public void SetDark()
+ {
+ if (Application.Current is { } application)
+ {
+ application.RequestedThemeVariant = ThemeVariant.Dark;
+ }
+ }
+
+ public void SetDefault()
+ {
+ if (Application.Current is { } application)
+ {
+ application.RequestedThemeVariant = ThemeVariant.Default;
+ }
+ }
}
}
diff --git a/samples/SafeAreaDemo/Views/MainView.xaml b/samples/SafeAreaDemo/Views/MainView.xaml
index c03ffc1f39..c62410aa83 100644
--- a/samples/SafeAreaDemo/Views/MainView.xaml
+++ b/samples/SafeAreaDemo/Views/MainView.xaml
@@ -8,7 +8,6 @@
d:DesignHeight="450"
x:Class="SafeAreaDemo.Views.MainView"
x:DataType="vm:MainViewModel"
- Background="#ccc"
TopLevel.AutoSafeAreaPadding="{Binding AutoSafeAreaPadding, Mode=TwoWay}">
@@ -47,7 +47,12 @@
Use Safe Area
Automatic Paddings
Hide System Bars
-
+
+
+
+
+
+
diff --git a/src/Android/Avalonia.Android/AvaloniaView.Input.cs b/src/Android/Avalonia.Android/AvaloniaView.Input.cs
index 4862daaf0b..413a21ba9c 100644
--- a/src/Android/Avalonia.Android/AvaloniaView.Input.cs
+++ b/src/Android/Avalonia.Android/AvaloniaView.Input.cs
@@ -22,14 +22,14 @@ namespace Avalonia.Android
protected override void OnFocusChanged(bool gainFocus, FocusSearchDirection direction, global::Android.Graphics.Rect? previouslyFocusedRect)
{
base.OnFocusChanged(gainFocus, direction, previouslyFocusedRect);
- _accessHelper.OnFocusChanged(gainFocus, (int)direction, previouslyFocusedRect);
+ _accessHelper?.OnFocusChanged(gainFocus, (int)direction, previouslyFocusedRect);
}
protected override bool DispatchHoverEvent(MotionEvent? e)
{
var res = _view.PointerHelper.DispatchMotionEvent(e, out var callBase);
if (res == false)
- callBase = !_accessHelper.DispatchHoverEvent(e!) && callBase;
+ callBase = (_accessHelper?.DispatchHoverEvent(e!) == true) && callBase;
var baseResult = callBase && base.DispatchHoverEvent(e);
@@ -63,7 +63,7 @@ namespace Avalonia.Android
{
var res = _view.KeyboardHelper.DispatchKeyEvent(e, out var callBase);
if (res == false)
- callBase = !_accessHelper.DispatchKeyEvent(e!) && callBase;
+ callBase = _accessHelper?.DispatchKeyEvent(e!) == false && callBase;
var baseResult = callBase && base.DispatchKeyEvent(e);
diff --git a/src/Android/Avalonia.Android/AvaloniaView.cs b/src/Android/Avalonia.Android/AvaloniaView.cs
index f1c3b95aa1..cec520acb7 100644
--- a/src/Android/Avalonia.Android/AvaloniaView.cs
+++ b/src/Android/Avalonia.Android/AvaloniaView.cs
@@ -19,11 +19,13 @@ namespace Avalonia.Android
{
public partial class AvaloniaView : FrameLayout
{
- private EmbeddableControlRoot _root;
+ private EmbeddableControlRoot? _root;
+ private ExploreByTouchHelper? _accessHelper;
+
private readonly ViewImpl _view;
- private readonly ExploreByTouchHelper _accessHelper;
private IDisposable? _timerSubscription;
+ private object? _content;
private bool _surfaceCreated;
public AvaloniaView(Context context) : base(context)
@@ -32,11 +34,13 @@ namespace Avalonia.Android
AddView(_view.View);
+ this.SetBackgroundColor(global::Android.Graphics.Color.Transparent);
+
_root = new EmbeddableControlRoot(_view);
_root.Prepare();
SetBackgroundColor(global::Android.Graphics.Color.Transparent);
- OnConfigurationChanged();
+ OnConfigurationChanged(null);
_view.InternalView!.SurfaceWindowCreated += InternalView_SurfaceWindowCreated;
_view.InternalView.SurfaceWindowDestroyed += InternalView_SurfaceWindowDestroyed;
@@ -69,16 +73,50 @@ namespace Avalonia.Android
public object? Content
{
- get { return _root.Content; }
- set { _root.Content = value; }
+ get { return _root?.Content; }
+ set
+ {
+ _content = value;
+ if (_root != null)
+ _root.Content = value;
+ }
}
internal new void Dispose()
{
+ _root?.Dispose();
+ _root = null;
+ _content = null;
+ }
+
+ protected override void OnDetachedFromWindow()
+ {
+ base.OnDetachedFromWindow();
OnVisibilityChanged(false);
_surfaceCreated = false;
- _root?.Dispose();
- _root = null!;
+
+ if (_accessHelper is { } accessHelper)
+ {
+ ViewCompat.SetAccessibilityDelegate(this, null);
+ _accessHelper = null;
+ }
+ }
+
+ protected override void OnAttachedToWindow()
+ {
+ _root?.Content = null;
+ _root = new EmbeddableControlRoot(_view);
+ _root.Prepare();
+ if (_content != null)
+ {
+ _root.Content = _content;
+ }
+
+ _accessHelper = new AvaloniaAccessHelper(this);
+ ViewCompat.SetAccessibilityDelegate(this, _accessHelper);
+ SendConfigurationChanged(Context?.Resources?.Configuration);
+
+ base.OnAttachedToWindow();
}
[SupportedOSPlatform("android24.0")]
@@ -98,6 +136,7 @@ namespace Avalonia.Android
{
if (_root == null || !_surfaceCreated)
return;
+
if (isVisible && _timerSubscription == null)
{
if (AndroidPlatform.Timer is { } timer)
@@ -119,20 +158,21 @@ namespace Avalonia.Android
_timerSubscription = null;
}
}
-
+
protected override void OnConfigurationChanged(Configuration? newConfig)
{
base.OnConfigurationChanged(newConfig);
- OnConfigurationChanged();
+ SendConfigurationChanged(newConfig ?? Context?.Resources?.Configuration);
}
- private void OnConfigurationChanged()
+ private void SendConfigurationChanged(Configuration? newConfig)
{
- if (Context is { } context)
+ _view.InsetsManager?.SetDefaultSystemLightMode(!(newConfig?.UiMode.HasFlag(UiMode.NightYes) ?? false));
+ if (Context is { } context && newConfig is { } config)
{
var settings =
AvaloniaLocator.Current.GetRequiredService() as AndroidPlatformSettings;
- settings?.OnViewConfigurationChanged(context);
+ settings?.OnViewConfigurationChanged(context, config);
((AndroidScreens)_view.TryGetFeature()!).OnChanged();
}
}
@@ -146,7 +186,7 @@ namespace Avalonia.Android
private void ViewImpl_FocusChange(object? sender, FocusChangeEventArgs e)
{
- if(!e.HasFocus)
+ if (!e.HasFocus)
LostFocus?.Invoke();
}
}
diff --git a/src/Android/Avalonia.Android/Platform/AndroidInsetsManager.cs b/src/Android/Avalonia.Android/Platform/AndroidInsetsManager.cs
index a8ab825808..2cf2896c7b 100644
--- a/src/Android/Avalonia.Android/Platform/AndroidInsetsManager.cs
+++ b/src/Android/Avalonia.Android/Platform/AndroidInsetsManager.cs
@@ -187,6 +187,11 @@ namespace Avalonia.Android.Platform
return insets;
}
+ internal void SetDefaultSystemLightMode(bool islightMode)
+ {
+ _isDefaultSystemBarLightTheme = islightMode;
+ }
+
private void NotifySafeAreaChanged(Thickness safeAreaPadding)
{
Dispatcher.UIThread.Send(_ => SafeAreaChanged?.Invoke(this, new SafeAreaChangedArgs(safeAreaPadding)));
diff --git a/src/Android/Avalonia.Android/Platform/AndroidPlatformSettings.cs b/src/Android/Avalonia.Android/Platform/AndroidPlatformSettings.cs
index 1e77b7d880..5da78439e2 100644
--- a/src/Android/Avalonia.Android/Platform/AndroidPlatformSettings.cs
+++ b/src/Android/Avalonia.Android/Platform/AndroidPlatformSettings.cs
@@ -49,14 +49,14 @@ internal class AndroidPlatformSettings : DefaultPlatformSettings
public override TimeSpan HoldWaitDuration => _holdWaitDuration;
- internal void OnViewConfigurationChanged(Context context)
+ internal void OnViewConfigurationChanged(Context context, Configuration configuration)
{
- if (context.Resources?.Configuration is null)
+ if (context.Resources is null)
{
return;
}
- var systemTheme = (context.Resources.Configuration.UiMode & UiMode.NightMask) switch
+ PlatformThemeVariant systemTheme = (configuration.UiMode & UiMode.NightMask) switch
{
UiMode.NightYes => PlatformThemeVariant.Dark,
UiMode.NightNo => PlatformThemeVariant.Light,
@@ -98,7 +98,7 @@ internal class AndroidPlatformSettings : DefaultPlatformSettings
}
finally
{
- array.Recycle();
+ array.Recycle();
}
}
}
diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
index a7bf5f626d..a732e90689 100644
--- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
+++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
@@ -8,6 +8,7 @@ using Android.OS;
using Android.Runtime;
using Android.Views;
using AndroidX.AppCompat.App;
+using AndroidX.Core.View;
using Avalonia.Android.Platform.Input;
using Avalonia.Android.Platform.Specific.Helpers;
using Avalonia.Android.Platform.Storage;
@@ -232,7 +233,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
}
}
- public void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
+ public void SetFrameThemeVariant(PlatformThemeVariant? themeVariant)
{
if (_insetsManager != null)
{
@@ -244,7 +245,23 @@ namespace Avalonia.Android.Platform.SkiaPlatform
};
}
- AppCompatDelegate.DefaultNightMode = themeVariant == PlatformThemeVariant.Light ? AppCompatDelegate.ModeNightNo : AppCompatDelegate.ModeNightYes;
+ // Sets the default app NightMode to AppCompatDelegate.ModeNightFollowSystem when themeVariant is null. This
+ // allows app to follow the current OS night mode. Using either ModeNightNo or ModeNightYes will force the app
+ // to use one night mode, ignoring the system's configuration and preventing us from detecting system theme changes
+ // in View.OnConfigurationChanged. In this case, only Activity.OnConfigurationChanged is called when system theme is
+ // changed, but we don't have access to that method if the toplevel view is embedded in a custom activity
+ var nightMode = themeVariant == null ? AppCompatDelegate.ModeNightFollowSystem :
+ themeVariant == PlatformThemeVariant.Light ? AppCompatDelegate.ModeNightNo : AppCompatDelegate.ModeNightYes;
+
+ AppCompatDelegate.DefaultNightMode = nightMode;
+
+ if (nightMode == AppCompatDelegate.ModeNightFollowSystem && _view?.Context is { } context
+ && context.Resources?.Configuration is { } config)
+ {
+ var settings =
+ AvaloniaLocator.Current.GetRequiredService() as AndroidPlatformSettings;
+ settings?.OnViewConfigurationChanged(context, config);
+ }
}
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels => new(1, 1, 1);
@@ -254,8 +271,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform
PixelSize EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Size => _view?.Size ?? default;
double EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Scaling => _view?.Scaling ?? default;
+ internal AndroidInsetsManager? InsetsManager => _insetsManager;
internal AndroidKeyboardEventsHelper KeyboardHelper => _keyboardHelper;
-
internal AndroidMotionEventsHelper PointerHelper => _pointerHelper;
public void SetTransparencyLevelHint(IReadOnlyList transparencyLevels)
diff --git a/src/Avalonia.Base/Styling/ThemeVariant.cs b/src/Avalonia.Base/Styling/ThemeVariant.cs
index 5414d340bf..b469ea5b28 100644
--- a/src/Avalonia.Base/Styling/ThemeVariant.cs
+++ b/src/Avalonia.Base/Styling/ThemeVariant.cs
@@ -100,11 +100,11 @@ public sealed record ThemeVariant
{
PlatformThemeVariant.Light => Light,
PlatformThemeVariant.Dark => Dark,
- _ => throw new ArgumentOutOfRangeException(nameof(themeVariant), themeVariant, null)
+ _ => throw new ArgumentOutOfRangeException(nameof(themeVariant), themeVariant, null),
};
}
- public static explicit operator PlatformThemeVariant?(ThemeVariant themeVariant)
+ public static explicit operator PlatformThemeVariant?(ThemeVariant? themeVariant)
{
if (themeVariant == Light)
{
@@ -114,7 +114,7 @@ public sealed record ThemeVariant
{
return PlatformThemeVariant.Dark;
}
- else if (themeVariant.InheritVariant is { } inheritVariant)
+ else if (themeVariant?.InheritVariant is { } inheritVariant)
{
return (PlatformThemeVariant?)inheritVariant;
}
diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs
index 90e272c7bd..0b1e20c395 100644
--- a/src/Avalonia.Controls/Application.cs
+++ b/src/Avalonia.Controls/Application.cs
@@ -53,7 +53,7 @@ namespace Avalonia
///
public static readonly StyledProperty ActualThemeVariantProperty =
ThemeVariantScope.ActualThemeVariantProperty.AddOwner();
-
+
///
public static readonly StyledProperty RequestedThemeVariantProperty =
ThemeVariantScope.RequestedThemeVariantProperty.AddOwner();
@@ -91,7 +91,7 @@ namespace Avalonia
get => GetValue(RequestedThemeVariantProperty);
set => SetValue(RequestedThemeVariantProperty, value);
}
-
+
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1031", Justification = "This property is supposed to be a styled readonly property.")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1030", Justification = "False positive.")]
@@ -201,7 +201,7 @@ namespace Avalonia
/// as specific top levels might have different settings set-up.
///
public IPlatformSettings? PlatformSettings => this.TryGetFeature();
-
+
event Action>? IGlobalStyles.GlobalStylesAdded
{
add => _stylesAdded += value;
@@ -218,7 +218,7 @@ namespace Avalonia
/// Initializes the application by loading XAML etc.
///
public virtual void Initialize() { }
-
+
///
public bool TryGetResource(object key, ThemeVariant? theme, out object? value)
{
@@ -262,7 +262,7 @@ namespace Avalonia
.Bind().ToConstant(this)
.Bind().ToConstant(this)
.Bind().ToConstant(InputManager)
- .Bind< IToolTipService>().ToConstant(new ToolTipService(InputManager))
+ .Bind().ToConstant(new ToolTipService(InputManager))
.Bind().ToTransient()
.Bind().ToConstant(DragDropDevice.Instance);
@@ -330,20 +330,28 @@ namespace Avalonia
if (change.Property == RequestedThemeVariantProperty)
{
- if (change.GetNewValue() is {} themeVariant && themeVariant != ThemeVariant.Default)
- SetValue(ActualThemeVariantProperty, themeVariant);
- else
- ClearValue(ActualThemeVariantProperty);
+ if (change.GetNewValue() is { } themeVariant)
+ {
+ if (themeVariant == ThemeVariant.Default)
+ {
+ ClearValue(ActualThemeVariantProperty);
+ }
+ else
+ {
+ SetValue(ActualThemeVariantProperty, themeVariant);
+ }
+ }
}
else if (change.Property == ActualThemeVariantProperty)
{
ActualThemeVariantChanged?.Invoke(this, EventArgs.Empty);
}
}
-
+
private void OnColorValuesChanged(object? sender, PlatformColorValues e)
{
- SetValue(ActualThemeVariantProperty, (ThemeVariant)e.ThemeVariant, BindingPriority.Template);
+ if ((RequestedThemeVariant ?? ThemeVariant.Default) == ThemeVariant.Default)
+ SetValue(ActualThemeVariantProperty, (ThemeVariant)e.ThemeVariant);
}
}
}
diff --git a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
index 27f927a3c4..32df6bafd9 100644
--- a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
+++ b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
@@ -63,7 +63,7 @@ namespace Avalonia.Controls.Embedding.Offscreen
public Action? TransparencyLevelChanged { get; set; }
- public void SetFrameThemeVariant(PlatformThemeVariant themeVariant) { }
+ public void SetFrameThemeVariant(PlatformThemeVariant? themeVariant) { }
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 1, 1);
diff --git a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs
index bab5e4ffe9..548d4989b6 100644
--- a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs
+++ b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs
@@ -137,6 +137,6 @@ namespace Avalonia.Platform
/// Sets the on the frame if it should be dark or light.
/// Also applies for the mobile status bar.
///
- void SetFrameThemeVariant(PlatformThemeVariant themeVariant);
+ void SetFrameThemeVariant(PlatformThemeVariant? themeVariant);
}
}
diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs
index c30047fe7a..eeb6ae3488 100644
--- a/src/Avalonia.Controls/TopLevel.cs
+++ b/src/Avalonia.Controls/TopLevel.cs
@@ -239,11 +239,6 @@ namespace Avalonia.Controls
impl.TransparencyLevelChanged = HandleTransparencyLevelChanged;
CreatePlatformImplBinding(TransparencyLevelHintProperty, hint => PlatformImpl.SetTransparencyLevelHint(hint ?? Array.Empty()));
- CreatePlatformImplBinding(ActualThemeVariantProperty, variant =>
- {
- variant ??= ThemeVariant.Default;
- PlatformImpl?.SetFrameThemeVariant((PlatformThemeVariant?)variant ?? PlatformThemeVariant.Light);
- });
_keyboardNavigationHandler?.SetOwner(this);
_accessKeyHandler?.SetOwner(this);
@@ -258,6 +253,16 @@ namespace Avalonia.Controls
SetValue(ActualThemeVariantProperty, _applicationThemeHost.ActualThemeVariant, BindingPriority.Template);
_applicationThemeHost.ActualThemeVariantChanged += GlobalActualThemeVariantChanged;
}
+ CreatePlatformImplBinding(ActualThemeVariantProperty, variant =>
+ {
+ if(_applicationThemeHost is AvaloniaObject element)
+ {
+ if (element.GetValue(ThemeVariantScope.RequestedThemeVariantProperty) is { } requestedThemeVariant)
+ variant = requestedThemeVariant == ThemeVariant.Default ? ThemeVariant.Default : variant;
+ }
+ variant ??= ThemeVariant.Default;
+ PlatformImpl?.SetFrameThemeVariant((PlatformThemeVariant?)variant);
+ });
ClientSize = impl.ClientSize;
diff --git a/src/Avalonia.DesignerSupport/Remote/Stubs.cs b/src/Avalonia.DesignerSupport/Remote/Stubs.cs
index 8149e5ad71..b53645c67b 100644
--- a/src/Avalonia.DesignerSupport/Remote/Stubs.cs
+++ b/src/Avalonia.DesignerSupport/Remote/Stubs.cs
@@ -196,7 +196,7 @@ namespace Avalonia.DesignerSupport.Remote
public bool NeedsManagedDecorations => false;
- public void SetFrameThemeVariant(PlatformThemeVariant themeVariant) { }
+ public void SetFrameThemeVariant(PlatformThemeVariant? themeVariant) { }
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 1, 1);
diff --git a/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs b/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs
index 882219f9a1..1e505d77fb 100644
--- a/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs
+++ b/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs
@@ -37,6 +37,11 @@ namespace Avalonia.FreeDesktop
Dispatcher.UIThread.Post(() => OnColorValuesChanged(_lastColorValues));
}
+ internal async void OnRequestDefaultThemeVariant()
+ {
+ await TryGetInitialValuesAsync();
+ }
+
private async Task TryGetThemeVariantAsync()
{
try
diff --git a/src/Avalonia.Native/NativePlatformSettings.cs b/src/Avalonia.Native/NativePlatformSettings.cs
index 5e3ee0cfc2..b95d9a9439 100644
--- a/src/Avalonia.Native/NativePlatformSettings.cs
+++ b/src/Avalonia.Native/NativePlatformSettings.cs
@@ -8,7 +8,6 @@ namespace Avalonia.Native;
internal class NativePlatformSettings : DefaultPlatformSettings
{
private readonly IAvnPlatformSettings _platformSettings;
- private PlatformColorValues? _lastColorValues;
public NativePlatformSettings(IAvnPlatformSettings platformSettings)
{
@@ -30,7 +29,7 @@ internal class NativePlatformSettings : DefaultPlatformSettings
if (color > 0)
{
- _lastColorValues = new PlatformColorValues
+ return new PlatformColorValues
{
ThemeVariant = theme,
ContrastPreference = contrast,
@@ -39,25 +38,17 @@ internal class NativePlatformSettings : DefaultPlatformSettings
}
else
{
- _lastColorValues = new PlatformColorValues
+ return new PlatformColorValues
{
ThemeVariant = theme,
ContrastPreference = contrast
};
}
-
- return _lastColorValues;
}
public void OnColorValuesChanged()
{
- var oldColorValues = _lastColorValues;
- var colorValues = GetColorValues();
-
- if (oldColorValues != colorValues)
- {
- OnColorValuesChanged(colorValues);
- }
+ OnColorValuesChanged(GetColorValues());
}
private class ColorsChangeCallback : NativeCallbackBase, IAvnActionCallback
diff --git a/src/Avalonia.Native/TopLevelImpl.cs b/src/Avalonia.Native/TopLevelImpl.cs
index 04e1318da8..4bd6e94f7d 100644
--- a/src/Avalonia.Native/TopLevelImpl.cs
+++ b/src/Avalonia.Native/TopLevelImpl.cs
@@ -158,7 +158,7 @@ internal class TopLevelImpl : ITopLevelImpl, IFramebufferPlatformSurface
}
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 0, 0);
- public virtual void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
+ public virtual void SetFrameThemeVariant(PlatformThemeVariant? themeVariant)
{
//noop
}
diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs
index 852317a793..dda1162f7a 100644
--- a/src/Avalonia.Native/WindowImplBase.cs
+++ b/src/Avalonia.Native/WindowImplBase.cs
@@ -68,8 +68,9 @@ namespace Avalonia.Native
Native?.Resize(clientSize.Width, clientSize.Height, (AvnPlatformResizeReason)reason);
}
- public override void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
+ public override void SetFrameThemeVariant(PlatformThemeVariant? themeVariant)
{
+ themeVariant ??= AvaloniaLocator.Current.GetService()?.GetColorValues().ThemeVariant ?? PlatformThemeVariant.Light;
Native?.SetFrameThemeVariant((AvnPlatformThemeVariant)themeVariant);
}
diff --git a/src/Avalonia.Wayland/WindowImplBase.cs b/src/Avalonia.Wayland/WindowImplBase.cs
index 3a95933d9c..8fe854feb5 100644
--- a/src/Avalonia.Wayland/WindowImplBase.cs
+++ b/src/Avalonia.Wayland/WindowImplBase.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
+using Avalonia.FreeDesktop;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Input.Raw;
@@ -76,7 +77,14 @@ internal abstract partial class WindowBaseImpl : IWindowBaseImpl
public WindowTransparencyLevel TransparencyLevel { get; } = WindowTransparencyLevel.Transparent;
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = default;
public void SetTransparencyLevelHint(IReadOnlyList transparencyLevels) { }
- public void SetFrameThemeVariant(PlatformThemeVariant themeVariant) { }
+
+ public async void SetFrameThemeVariant(PlatformThemeVariant? themeVariant)
+ {
+ if (themeVariant == null && AvaloniaLocator.Current.GetService() is DBusPlatformSettings platformSettings)
+ {
+ platformSettings.OnRequestDefaultThemeVariant();
+ }
+ }
public Action? Closed { get; set; }
public Action? LostFocus { get; set; }
diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs
index ef644e2c72..12c3453d75 100644
--- a/src/Avalonia.X11/X11Window.cs
+++ b/src/Avalonia.X11/X11Window.cs
@@ -1662,7 +1662,13 @@ namespace Avalonia.X11
public WindowTransparencyLevel TransparencyLevel =>
_transparencyHelper?.CurrentLevel ?? WindowTransparencyLevel.None;
- public void SetFrameThemeVariant(PlatformThemeVariant themeVariant) { }
+ public async void SetFrameThemeVariant(PlatformThemeVariant? themeVariant)
+ {
+ if (themeVariant == null && AvaloniaLocator.Current.GetService() is DBusPlatformSettings platformSettings)
+ {
+ platformSettings.OnRequestDefaultThemeVariant();
+ }
+ }
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 0.8, 0.8);
diff --git a/src/Browser/Avalonia.Browser/BrowserPlatformSettings.cs b/src/Browser/Avalonia.Browser/BrowserPlatformSettings.cs
index 2a3cef4334..5d9f18d993 100644
--- a/src/Browser/Avalonia.Browser/BrowserPlatformSettings.cs
+++ b/src/Browser/Avalonia.Browser/BrowserPlatformSettings.cs
@@ -37,6 +37,11 @@ internal class BrowserPlatformSettings : DefaultPlatformSettings
_isHighContrast = isHighContrast;
OnColorValuesChanged(GetColorValues());
}
+
+ public void OnValuesChanged()
+ {
+ OnColorValuesChanged(GetColorValues());
+ }
private void EnsureBackend()
{
diff --git a/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs b/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs
index 6b712391e4..67773a6d5c 100644
--- a/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs
+++ b/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs
@@ -144,9 +144,13 @@ namespace Avalonia.Browser
public Action? LostFocus { get; set; }
public WindowTransparencyLevel TransparencyLevel => WindowTransparencyLevel.None;
- public void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
+ public void SetFrameThemeVariant(PlatformThemeVariant? themeVariant)
{
// not in the standard, but we potentially can use "apple-mobile-web-app-status-bar-style" for iOS and "theme-color" for android.
+ if (themeVariant == null && AvaloniaLocator.Current.GetService() is BrowserPlatformSettings platformSettings)
+ {
+ platformSettings.OnValuesChanged();
+ }
}
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; }
diff --git a/src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs b/src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs
index f2a7bf3575..0036288ac0 100644
--- a/src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs
+++ b/src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs
@@ -438,7 +438,7 @@ namespace Avalonia.Headless
}
- public void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
+ public void SetFrameThemeVariant(PlatformThemeVariant? themeVariant)
{
}
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
index 22965c05fe..0ef7d08677 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
+++ b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
@@ -78,7 +78,7 @@ using Avalonia.Rendering.Composition;
public WindowTransparencyLevel TransparencyLevel => WindowTransparencyLevel.None;
- public void SetFrameThemeVariant(PlatformThemeVariant themeVariant) { }
+ public void SetFrameThemeVariant(PlatformThemeVariant? themeVariant) { }
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 1, 1);
public object? TryGetFeature(Type featureType) => null;
diff --git a/src/Windows/Avalonia.Win32/SwapChainTopLevelImpl.cs b/src/Windows/Avalonia.Win32/SwapChainTopLevelImpl.cs
index 15ea304709..f59d39a75d 100644
--- a/src/Windows/Avalonia.Win32/SwapChainTopLevelImpl.cs
+++ b/src/Windows/Avalonia.Win32/SwapChainTopLevelImpl.cs
@@ -99,7 +99,7 @@ internal class SwapChainTopLevelImpl : ITopLevelImpl
public void SetTransparencyLevelHint(IReadOnlyList transparencyLevels) { }
- public void SetFrameThemeVariant(PlatformThemeVariant themeVariant) { }
+ public void SetFrameThemeVariant(PlatformThemeVariant? themeVariant) { }
///
/// Optional IME implementation provided by the host.
diff --git a/src/Windows/Avalonia.Win32/Win32PlatformSettings.cs b/src/Windows/Avalonia.Win32/Win32PlatformSettings.cs
index c4b016c1b9..efcdb32458 100644
--- a/src/Windows/Avalonia.Win32/Win32PlatformSettings.cs
+++ b/src/Windows/Avalonia.Win32/Win32PlatformSettings.cs
@@ -10,9 +10,7 @@ internal class Win32PlatformSettings : DefaultPlatformSettings
{
private static readonly Lazy s_uiSettingsSupported = new(() =>
WinRTApiInformation.IsTypePresent("Windows.UI.ViewManagement.UISettings")
- && WinRTApiInformation.IsTypePresent("Windows.UI.ViewManagement.AccessibilitySettings"));
-
- private PlatformColorValues? _lastColorValues;
+ && WinRTApiInformation.IsTypePresent("Windows.UI.ViewManagement.AccessibilitySettings"));
public override Size GetTapSize(PointerType type)
{
@@ -54,7 +52,7 @@ internal class Win32PlatformSettings : DefaultPlatformSettings
// - Night sky - High Contrast #2
// Only "Desert" one can be considered a "light" preference.
using var highContrastScheme = new HStringInterop(accessibilitySettings.HighContrastScheme);
- return _lastColorValues = new PlatformColorValues
+ return new PlatformColorValues
{
ThemeVariant = highContrastScheme.Value?.Contains("White") == true ?
PlatformThemeVariant.Light :
@@ -67,7 +65,7 @@ internal class Win32PlatformSettings : DefaultPlatformSettings
else
{
var background = uiSettings.GetColorValue(UIColorType.Background).ToAvalonia();
- return _lastColorValues = new PlatformColorValues
+ return new PlatformColorValues
{
ThemeVariant = background.R + background.G + background.B < (255 * 3 - background.R - background.G - background.B) ?
PlatformThemeVariant.Dark :
@@ -80,12 +78,6 @@ internal class Win32PlatformSettings : DefaultPlatformSettings
internal void OnColorValuesChanged()
{
- var oldColorValues = _lastColorValues;
- var colorValues = GetColorValues();
-
- if (oldColorValues != colorValues)
- {
- OnColorValuesChanged(colorValues);
- }
+ OnColorValuesChanged(GetColorValues());
}
}
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs
index a39fa9c1cb..e07454a8ee 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.cs
@@ -948,12 +948,12 @@ namespace Avalonia.Win32
}
}
- public unsafe void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
+ public unsafe void SetFrameThemeVariant(PlatformThemeVariant? themeVariant)
{
- _currentThemeVariant = themeVariant;
+ _currentThemeVariant = themeVariant ?? Win32Platform.Instance.PlatformSettings.GetColorValues().ThemeVariant;
if (Win32Platform.WindowsVersion.Build >= 22000)
{
- var pvUseBackdropBrush = themeVariant == PlatformThemeVariant.Dark ? 1 : 0;
+ var pvUseBackdropBrush = _currentThemeVariant == PlatformThemeVariant.Dark ? 1 : 0;
DwmSetWindowAttribute(
_hwnd,
(int)DwmWindowAttribute.DWMWA_USE_IMMERSIVE_DARK_MODE,
@@ -964,6 +964,8 @@ namespace Avalonia.Win32
SetTransparencyMica();
}
}
+
+ (Win32Platform.Instance.PlatformSettings as Win32PlatformSettings)?.OnColorValuesChanged();
}
protected virtual IntPtr CreateWindowOverride(ushort atom)
diff --git a/src/iOS/Avalonia.iOS/AvaloniaView.cs b/src/iOS/Avalonia.iOS/AvaloniaView.cs
index 29f848925d..98c4d1ff2f 100644
--- a/src/iOS/Avalonia.iOS/AvaloniaView.cs
+++ b/src/iOS/Avalonia.iOS/AvaloniaView.cs
@@ -254,7 +254,7 @@ namespace Avalonia.iOS
public WindowTransparencyLevel TransparencyLevel => WindowTransparencyLevel.None;
- public void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
+ public void SetFrameThemeVariant(PlatformThemeVariant? themeVariant)
{
#if !TVOS
// TODO adjust status bar depending on full screen mode.
diff --git a/tests/Avalonia.UnitTests/CompositorTestServices.cs b/tests/Avalonia.UnitTests/CompositorTestServices.cs
index 14320ee4da..1ba03f5974 100644
--- a/tests/Avalonia.UnitTests/CompositorTestServices.cs
+++ b/tests/Avalonia.UnitTests/CompositorTestServices.cs
@@ -205,7 +205,7 @@ public class CompositorTestServices : IDisposable
public WindowTransparencyLevel TransparencyLevel => WindowTransparencyLevel.None;
- public void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
+ public void SetFrameThemeVariant(PlatformThemeVariant? themeVariant)
{
}