Browse Source

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
pull/13006/head
Tom Edwards 2 years ago
committed by GitHub
parent
commit
745b579e8f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 37
      src/Avalonia.Controls/TopLevel.cs
  2. 45
      src/Avalonia.Controls/Window.cs
  3. 19
      src/Avalonia.Controls/WindowBase.cs
  4. 25
      tests/Avalonia.Controls.UnitTests/WindowTests.cs

37
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<AvaloniaProperty, Action> _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<WindowTransparencyLevel>()));
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
/// </returns>
public IPlatformHandle? TryGetPlatformHandle() => (PlatformImpl as IWindowBaseImpl)?.Handle;
private protected void CreatePlatformImplBinding<TValue>(StyledProperty<TValue> property, Action<TValue> 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));
}
}
}
/// <summary>
/// Gets the renderer for the window.
/// </summary>
@ -561,18 +585,9 @@ namespace Avalonia.Controls
{
base.OnPropertyChanged(change);
if (change.Property == TransparencyLevelHintProperty)
{
if (PlatformImpl != null)
{
PlatformImpl.SetTransparencyLevelHint(
change.GetNewValue<IReadOnlyList<WindowTransparencyLevel>>() ?? Array.Empty<WindowTransparencyLevel>());
}
}
else if (change.Property == ActualThemeVariantProperty)
if (_platformImplBindings.TryGetValue(change.Property, out var bindingAction))
{
var newThemeVariant = change.GetNewValue<ThemeVariant?>() ?? ThemeVariant.Default;
PlatformImpl?.SetFrameThemeVariant((PlatformThemeVariant?)newThemeVariant ?? PlatformThemeVariant.Light);
bindingAction();
}
}

45
src/Avalonia.Controls/Window.cs

@ -177,35 +177,6 @@ namespace Avalonia.Controls
static Window()
{
BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White);
TitleProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl?.SetTitle((string?)e.NewValue));
ShowInTaskbarProperty.Changed.AddClassHandler<Window>((w, e) => w.PlatformImpl?.ShowTaskbarIcon((bool)e.NewValue!));
IconProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl?.SetIcon(((WindowIcon?)e.NewValue)?.PlatformImpl));
CanResizeProperty.Changed.AddClassHandler<Window>((w, e) => w.PlatformImpl?.CanResize((bool)e.NewValue!));
WindowStateProperty.Changed.AddClassHandler<Window>(
(w, e) => { if (w.PlatformImpl != null) w.PlatformImpl.WindowState = (WindowState)e.NewValue!; });
ExtendClientAreaToDecorationsHintProperty.Changed.AddClassHandler<Window>(
(w, e) => { if (w.PlatformImpl != null) w.PlatformImpl.SetExtendClientAreaToDecorationsHint((bool)e.NewValue!); });
ExtendClientAreaChromeHintsProperty.Changed.AddClassHandler<Window>(
(w, e) =>
{
if (w.PlatformImpl != null)
{
w.PlatformImpl.SetExtendClientAreaChromeHints((ExtendClientAreaChromeHints)e.NewValue!);
}
});
ExtendClientAreaTitleBarHeightHintProperty.Changed.AddClassHandler<Window>(
(w, e) => { if (w.PlatformImpl != null) w.PlatformImpl.SetExtendClientAreaTitleBarHeightHint((double)e.NewValue!); });
MinWidthProperty.Changed.AddClassHandler<Window>((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size((double)e.NewValue!, w.MinHeight), new Size(w.MaxWidth, w.MaxHeight)));
MinHeightProperty.Changed.AddClassHandler<Window>((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, (double)e.NewValue!), new Size(w.MaxWidth, w.MaxHeight)));
MaxWidthProperty.Changed.AddClassHandler<Window>((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, w.MinHeight), new Size((double)e.NewValue!, w.MaxHeight)));
MaxHeightProperty.Changed.AddClassHandler<Window>((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, w.MinHeight), new Size(w.MaxWidth, (double)e.NewValue!)));
}
/// <summary>
@ -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));
}
/// <summary>

19
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<WindowBase>(false);
IsVisibleProperty.Changed.AddClassHandler<WindowBase>((x,e) => x.IsVisibleChanged(e));
TopmostProperty.Changed.AddClassHandler<WindowBase>((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);
}
}
/// <inheritdoc/>
protected override void OnClosed(EventArgs e)
{

25
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<TopmostWindow>(true);
}
}
}
}

Loading…
Cancel
Save