Emmanuel Hansen 4 weeks ago
committed by GitHub
parent
commit
2bbc2fed3b
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 36
      api/Avalonia.nupkg.xml
  2. 2
      samples/SafeAreaDemo/App.xaml
  3. 31
      samples/SafeAreaDemo/ViewModels/MainViewModel.cs
  4. 9
      samples/SafeAreaDemo/Views/MainView.xaml
  5. 6
      src/Android/Avalonia.Android/AvaloniaView.Input.cs
  6. 66
      src/Android/Avalonia.Android/AvaloniaView.cs
  7. 5
      src/Android/Avalonia.Android/Platform/AndroidInsetsManager.cs
  8. 8
      src/Android/Avalonia.Android/Platform/AndroidPlatformSettings.cs
  9. 23
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  10. 6
      src/Avalonia.Base/Styling/ThemeVariant.cs
  11. 30
      src/Avalonia.Controls/Application.cs
  12. 2
      src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
  13. 2
      src/Avalonia.Controls/Platform/ITopLevelImpl.cs
  14. 15
      src/Avalonia.Controls/TopLevel.cs
  15. 2
      src/Avalonia.DesignerSupport/Remote/Stubs.cs
  16. 5
      src/Avalonia.FreeDesktop/DBusPlatformSettings.cs
  17. 15
      src/Avalonia.Native/NativePlatformSettings.cs
  18. 2
      src/Avalonia.Native/TopLevelImpl.cs
  19. 3
      src/Avalonia.Native/WindowImplBase.cs
  20. 10
      src/Avalonia.Wayland/WindowImplBase.cs
  21. 8
      src/Avalonia.X11/X11Window.cs
  22. 5
      src/Browser/Avalonia.Browser/BrowserPlatformSettings.cs
  23. 6
      src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs
  24. 2
      src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs
  25. 2
      src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
  26. 2
      src/Windows/Avalonia.Win32/SwapChainTopLevelImpl.cs
  27. 16
      src/Windows/Avalonia.Win32/Win32PlatformSettings.cs
  28. 8
      src/Windows/Avalonia.Win32/WindowImpl.cs
  29. 2
      src/iOS/Avalonia.iOS/AvaloniaView.cs
  30. 2
      tests/Avalonia.UnitTests/CompositorTestServices.cs

36
api/Avalonia.nupkg.xml

@ -43,6 +43,18 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetFrameThemeVariant(Avalonia.Platform.PlatformThemeVariant)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Platform.ITopLevelImpl.SetFrameThemeVariant(Avalonia.Platform.PlatformThemeVariant)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.OpenGL.Egl.EglPlatformSurfaceRenderTargetBase.BeginDraw(Avalonia.OpenGL.Egl.EglSurface,Avalonia.PixelSize,System.Double,System.Action,System.Boolean)</Target>
@ -91,6 +103,18 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetFrameThemeVariant(Avalonia.Platform.PlatformThemeVariant)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Platform.ITopLevelImpl.SetFrameThemeVariant(Avalonia.Platform.PlatformThemeVariant)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.OpenGL.Egl.EglPlatformSurfaceRenderTargetBase.BeginDraw(Avalonia.OpenGL.Egl.EglSurface,Avalonia.PixelSize,System.Double,System.Action,System.Boolean)</Target>
@ -103,10 +127,22 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Platform.ITopLevelImpl.SetFrameThemeVariant(System.Nullable{Avalonia.Platform.PlatformThemeVariant})</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Controls.Platform.IWin32OptionsTopLevelImpl.SetWindowCornerPreference(Avalonia.Controls.Win32Properties.WindowCornerPreference)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Platform.ITopLevelImpl.SetFrameThemeVariant(System.Nullable{Avalonia.Platform.PlatformThemeVariant})</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
</Suppressions>

2
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">
<Application.DataTemplates>
<local:ViewLocator/>
</Application.DataTemplates>

31
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;
}
}
}
}

9
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}">
<Grid HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
@ -40,6 +39,7 @@
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<StackPanel Orientation="Vertical"
Spacing="5"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Label HorizontalAlignment="Left">Options:</Label>
@ -47,7 +47,12 @@
<CheckBox IsChecked="{Binding UseSafeArea}" IsEnabled="{Binding !AutoSafeAreaPadding}">Use Safe Area</CheckBox>
<CheckBox IsChecked="{Binding AutoSafeAreaPadding}">Automatic Paddings</CheckBox>
<CheckBox IsChecked="{Binding HideSystemBars}">Hide System Bars</CheckBox>
<TextBox Width="200" PlaceholderText="Tap to Show Keyboard"/>
<TextBox Width="200" PlaceholderText="Tap to Show Keyboard"/>
<StackPanel Orientation="Horizontal" Spacing="10">
<Button Command="{Binding SetLight}">Light</Button>
<Button Command="{Binding SetDark}">Dark</Button>
<Button Command="{Binding SetDefault}">Default</Button>
</StackPanel>
</StackPanel>
</Grid>
</DockPanel>

6
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);

66
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<IPlatformSettings>() as AndroidPlatformSettings;
settings?.OnViewConfigurationChanged(context);
settings?.OnViewConfigurationChanged(context, config);
((AndroidScreens)_view.TryGetFeature<IScreenImpl>()!).OnChanged();
}
}
@ -146,7 +186,7 @@ namespace Avalonia.Android
private void ViewImpl_FocusChange(object? sender, FocusChangeEventArgs e)
{
if(!e.HasFocus)
if (!e.HasFocus)
LostFocus?.Invoke();
}
}

5
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)));

8
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();
}
}
}

23
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<IPlatformSettings>() 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<TopLevelImpl> KeyboardHelper => _keyboardHelper;
internal AndroidMotionEventsHelper PointerHelper => _pointerHelper;
public void SetTransparencyLevelHint(IReadOnlyList<WindowTransparencyLevel> transparencyLevels)

6
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;
}

30
src/Avalonia.Controls/Application.cs

@ -53,7 +53,7 @@ namespace Avalonia
/// <inheritdoc cref="ThemeVariantScope.ActualThemeVariantProperty" />
public static readonly StyledProperty<ThemeVariant> ActualThemeVariantProperty =
ThemeVariantScope.ActualThemeVariantProperty.AddOwner<Application>();
/// <inheritdoc cref="ThemeVariantScope.RequestedThemeVariantProperty" />
public static readonly StyledProperty<ThemeVariant?> RequestedThemeVariantProperty =
ThemeVariantScope.RequestedThemeVariantProperty.AddOwner<Application>();
@ -91,7 +91,7 @@ namespace Avalonia
get => GetValue(RequestedThemeVariantProperty);
set => SetValue(RequestedThemeVariantProperty, value);
}
/// <inheritdoc />
[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.
/// </remarks>
public IPlatformSettings? PlatformSettings => this.TryGetFeature<IPlatformSettings>();
event Action<IReadOnlyList<IStyle>>? IGlobalStyles.GlobalStylesAdded
{
add => _stylesAdded += value;
@ -218,7 +218,7 @@ namespace Avalonia
/// Initializes the application by loading XAML etc.
/// </summary>
public virtual void Initialize() { }
/// <inheritdoc/>
public bool TryGetResource(object key, ThemeVariant? theme, out object? value)
{
@ -262,7 +262,7 @@ namespace Avalonia
.Bind<IGlobalStyles>().ToConstant(this)
.Bind<IThemeVariantHost>().ToConstant(this)
.Bind<IInputManager>().ToConstant(InputManager)
.Bind< IToolTipService>().ToConstant(new ToolTipService(InputManager))
.Bind<IToolTipService>().ToConstant(new ToolTipService(InputManager))
.Bind<IKeyboardNavigationHandler>().ToTransient<KeyboardNavigationHandler>()
.Bind<IDragDropDevice>().ToConstant(DragDropDevice.Instance);
@ -330,20 +330,28 @@ namespace Avalonia
if (change.Property == RequestedThemeVariantProperty)
{
if (change.GetNewValue<ThemeVariant>() is {} themeVariant && themeVariant != ThemeVariant.Default)
SetValue(ActualThemeVariantProperty, themeVariant);
else
ClearValue(ActualThemeVariantProperty);
if (change.GetNewValue<ThemeVariant>() 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);
}
}
}

2
src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs

@ -63,7 +63,7 @@ namespace Avalonia.Controls.Embedding.Offscreen
public Action<WindowTransparencyLevel>? TransparencyLevelChanged { get; set; }
public void SetFrameThemeVariant(PlatformThemeVariant themeVariant) { }
public void SetFrameThemeVariant(PlatformThemeVariant? themeVariant) { }
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 1, 1);

2
src/Avalonia.Controls/Platform/ITopLevelImpl.cs

@ -137,6 +137,6 @@ namespace Avalonia.Platform
/// Sets the <see cref="PlatformThemeVariant"/> on the frame if it should be dark or light.
/// Also applies for the mobile status bar.
/// </summary>
void SetFrameThemeVariant(PlatformThemeVariant themeVariant);
void SetFrameThemeVariant(PlatformThemeVariant? themeVariant);
}
}

15
src/Avalonia.Controls/TopLevel.cs

@ -239,11 +239,6 @@ namespace Avalonia.Controls
impl.TransparencyLevelChanged = HandleTransparencyLevelChanged;
CreatePlatformImplBinding(TransparencyLevelHintProperty, hint => PlatformImpl.SetTransparencyLevelHint(hint ?? Array.Empty<WindowTransparencyLevel>()));
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;

2
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);

5
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<PlatformThemeVariant?> TryGetThemeVariantAsync()
{
try

15
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

2
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
}

3
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<IPlatformSettings>()?.GetColorValues().ThemeVariant ?? PlatformThemeVariant.Light;
Native?.SetFrameThemeVariant((AvnPlatformThemeVariant)themeVariant);
}

10
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<WindowTransparencyLevel> transparencyLevels) { }
public void SetFrameThemeVariant(PlatformThemeVariant themeVariant) { }
public async void SetFrameThemeVariant(PlatformThemeVariant? themeVariant)
{
if (themeVariant == null && AvaloniaLocator.Current.GetService<IPlatformSettings>() is DBusPlatformSettings platformSettings)
{
platformSettings.OnRequestDefaultThemeVariant();
}
}
public Action? Closed { get; set; }
public Action? LostFocus { get; set; }

8
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<IPlatformSettings>() is DBusPlatformSettings platformSettings)
{
platformSettings.OnRequestDefaultThemeVariant();
}
}
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 0.8, 0.8);

5
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()
{

6
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<IPlatformSettings>() is BrowserPlatformSettings platformSettings)
{
platformSettings.OnValuesChanged();
}
}
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; }

2
src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs

@ -438,7 +438,7 @@ namespace Avalonia.Headless
}
public void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
public void SetFrameThemeVariant(PlatformThemeVariant? themeVariant)
{
}

2
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;

2
src/Windows/Avalonia.Win32/SwapChainTopLevelImpl.cs

@ -99,7 +99,7 @@ internal class SwapChainTopLevelImpl : ITopLevelImpl
public void SetTransparencyLevelHint(IReadOnlyList<WindowTransparencyLevel> transparencyLevels) { }
public void SetFrameThemeVariant(PlatformThemeVariant themeVariant) { }
public void SetFrameThemeVariant(PlatformThemeVariant? themeVariant) { }
/// <summary>
/// Optional IME implementation provided by the host.

16
src/Windows/Avalonia.Win32/Win32PlatformSettings.cs

@ -10,9 +10,7 @@ internal class Win32PlatformSettings : DefaultPlatformSettings
{
private static readonly Lazy<bool> 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());
}
}

8
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)

2
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.

2
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)
{
}

Loading…
Cancel
Save