From 745b579e8f5b7729ea4a7140ed905bd8becaa8d7 Mon Sep 17 00:00:00 2001 From: Tom Edwards <109803929+TomEdwardsEnscape@users.noreply.github.com> Date: Sun, 24 Sep 2023 04:42:09 +0200 Subject: [PATCH] Fix default values of window properties not being sent to `IWindowImpl` (#12656) * Added failing test for Topmost platform configuration * Added CreatePlatformImplBinding system to transfer data to TopLevel platform implementations --- src/Avalonia.Controls/TopLevel.cs | 37 ++++++++++----- src/Avalonia.Controls/Window.cs | 45 +++++++------------ src/Avalonia.Controls/WindowBase.cs | 19 +++++--- .../WindowTests.cs | 25 +++++++++++ 4 files changed, 79 insertions(+), 47 deletions(-) diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 158469d231..2c945fc481 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -121,6 +121,7 @@ namespace Avalonia.Controls private readonly PointerOverPreProcessor? _pointerOverPreProcessor; private readonly IDisposable? _pointerOverPreProcessorSubscription; private readonly IDisposable? _backGestureSubscription; + private readonly Dictionary _platformImplBindings = new(); private Size _clientSize; private Size? _frameSize; private WindowTransparencyLevel _actualTransparencyLevel; @@ -212,6 +213,13 @@ namespace Avalonia.Controls impl.ScalingChanged = HandleScalingChanged; 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); @@ -405,6 +413,22 @@ namespace Avalonia.Controls /// public IPlatformHandle? TryGetPlatformHandle() => (PlatformImpl as IWindowBaseImpl)?.Handle; + private protected void CreatePlatformImplBinding(StyledProperty property, Action onValue) + { + _platformImplBindings.TryGetValue(property, out var actions); + _platformImplBindings[property] = actions + UpdatePlatformImpl; + + UpdatePlatformImpl(); // execute the action now to handle the default value, which may have been overridden + + void UpdatePlatformImpl() + { + if (PlatformImpl is not null) + { + onValue(GetValue(property)); + } + } + } + /// /// Gets the renderer for the window. /// @@ -561,18 +585,9 @@ namespace Avalonia.Controls { base.OnPropertyChanged(change); - if (change.Property == TransparencyLevelHintProperty) - { - if (PlatformImpl != null) - { - PlatformImpl.SetTransparencyLevelHint( - change.GetNewValue>() ?? Array.Empty()); - } - } - else if (change.Property == ActualThemeVariantProperty) + if (_platformImplBindings.TryGetValue(change.Property, out var bindingAction)) { - var newThemeVariant = change.GetNewValue() ?? ThemeVariant.Default; - PlatformImpl?.SetFrameThemeVariant((PlatformThemeVariant?)newThemeVariant ?? PlatformThemeVariant.Light); + bindingAction(); } } diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index dc58947994..43202f10c9 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -177,35 +177,6 @@ namespace Avalonia.Controls static Window() { BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White); - TitleProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl?.SetTitle((string?)e.NewValue)); - ShowInTaskbarProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.ShowTaskbarIcon((bool)e.NewValue!)); - - IconProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl?.SetIcon(((WindowIcon?)e.NewValue)?.PlatformImpl)); - - CanResizeProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.CanResize((bool)e.NewValue!)); - - WindowStateProperty.Changed.AddClassHandler( - (w, e) => { if (w.PlatformImpl != null) w.PlatformImpl.WindowState = (WindowState)e.NewValue!; }); - - ExtendClientAreaToDecorationsHintProperty.Changed.AddClassHandler( - (w, e) => { if (w.PlatformImpl != null) w.PlatformImpl.SetExtendClientAreaToDecorationsHint((bool)e.NewValue!); }); - - ExtendClientAreaChromeHintsProperty.Changed.AddClassHandler( - (w, e) => - { - if (w.PlatformImpl != null) - { - w.PlatformImpl.SetExtendClientAreaChromeHints((ExtendClientAreaChromeHints)e.NewValue!); - } - }); - - ExtendClientAreaTitleBarHeightHintProperty.Changed.AddClassHandler( - (w, e) => { if (w.PlatformImpl != null) w.PlatformImpl.SetExtendClientAreaTitleBarHeightHint((double)e.NewValue!); }); - - MinWidthProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size((double)e.NewValue!, w.MinHeight), new Size(w.MaxWidth, w.MaxHeight))); - MinHeightProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, (double)e.NewValue!), new Size(w.MaxWidth, w.MaxHeight))); - MaxWidthProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, w.MinHeight), new Size((double)e.NewValue!, w.MaxHeight))); - MaxHeightProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, w.MinHeight), new Size(w.MaxWidth, (double)e.NewValue!))); } /// @@ -230,7 +201,21 @@ namespace Avalonia.Controls impl.ExtendClientAreaToDecorationsChanged = ExtendClientAreaToDecorationsChanged; this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl?.Resize(x, WindowResizeReason.Application)); - PlatformImpl?.ShowTaskbarIcon(ShowInTaskbar); + CreatePlatformImplBinding(TitleProperty, title => PlatformImpl!.SetTitle(title)); + CreatePlatformImplBinding(IconProperty, icon => PlatformImpl!.SetIcon(icon?.PlatformImpl)); + CreatePlatformImplBinding(CanResizeProperty, canResize => PlatformImpl!.CanResize(canResize)); + CreatePlatformImplBinding(ShowInTaskbarProperty, show => PlatformImpl!.ShowTaskbarIcon(show)); + + CreatePlatformImplBinding(WindowStateProperty, state => PlatformImpl!.WindowState = state); + CreatePlatformImplBinding(ExtendClientAreaToDecorationsHintProperty, hint => PlatformImpl!.SetExtendClientAreaToDecorationsHint(hint)); + CreatePlatformImplBinding(ExtendClientAreaChromeHintsProperty, hint => PlatformImpl!.SetExtendClientAreaChromeHints(hint)); + + CreatePlatformImplBinding(MinWidthProperty, UpdateMinMaxSize); + CreatePlatformImplBinding(MaxWidthProperty, UpdateMinMaxSize); + CreatePlatformImplBinding(MinHeightProperty, UpdateMinMaxSize); + CreatePlatformImplBinding(MaxHeightProperty, UpdateMinMaxSize); + + void UpdateMinMaxSize(double _) => PlatformImpl!.SetMinMaxSize(new Size(MinWidth, MinHeight), new Size(MaxWidth, MaxHeight)); } /// diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index 1cc235ce99..e29ee5fffc 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -36,20 +36,17 @@ namespace Avalonia.Controls private bool _isActive; private int _ignoreVisibilityChanges; private WindowBase? _owner; - - protected bool IgnoreVisibilityChanges => _ignoreVisibilityChanges > 0; + + protected bool IgnoreVisibilityChanges => _ignoreVisibilityChanges > 0; static WindowBase() { IsVisibleProperty.OverrideDefaultValue(false); - IsVisibleProperty.Changed.AddClassHandler((x,e) => x.IsVisibleChanged(e)); - - - TopmostProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.SetTopmost((bool)e.NewValue!)); } public WindowBase(IWindowBaseImpl impl) : this(impl, AvaloniaLocator.Current) { + CreatePlatformImplBinding(TopmostProperty, topmost => PlatformImpl!.SetTopmost(topmost)); } public WindowBase(IWindowBaseImpl impl, IAvaloniaDependencyResolver? dependencyResolver) : base(impl, dependencyResolver) @@ -192,6 +189,16 @@ namespace Avalonia.Controls } } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == IsVisibleProperty) + { + IsVisibleChanged(change); + } + } + /// protected override void OnClosed(EventArgs e) { diff --git a/tests/Avalonia.Controls.UnitTests/WindowTests.cs b/tests/Avalonia.Controls.UnitTests/WindowTests.cs index 186b2f6836..f30fd1d6b2 100644 --- a/tests/Avalonia.Controls.UnitTests/WindowTests.cs +++ b/tests/Avalonia.Controls.UnitTests/WindowTests.cs @@ -653,6 +653,23 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void Window_Topmost_By_Default_Should_Configure_PlatformImpl_When_Constructed() + { + var windowImpl = MockWindowingPlatform.CreateWindowMock(); + + var windowServices = TestServices.StyledWindow.With( + windowingPlatform: new MockWindowingPlatform(() => windowImpl.Object)); + + using (UnitTestApplication.Start(windowServices)) + { + var window = new TopmostWindow(); + + Assert.True(window.Topmost); + windowImpl.Verify(i => i.SetTopmost(true)); + } + } + public class SizingTests { [Fact] @@ -1099,5 +1116,13 @@ namespace Avalonia.Controls.UnitTests return base.MeasureOverride(availableSize); } } + + private class TopmostWindow : Window + { + static TopmostWindow() + { + TopmostProperty.OverrideDefaultValue(true); + } + } } }