Browse Source

Merge pull request #10851 from AvaloniaUI/fixes/custom-chrome-canresize-maximize

Don't allow window zoom when CanResize=false and custom window chrome
pull/11002/head
Steven Kirk 3 years ago
committed by GitHub
parent
commit
5455048a9d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
  2. 29
      src/Avalonia.Controls/Chrome/CaptionButtons.cs
  3. 12
      src/Avalonia.Native/WindowImpl.cs
  4. 12
      src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml
  5. 12
      src/Avalonia.Themes.Simple/Controls/CaptionButtons.xaml
  6. 58
      tests/Avalonia.IntegrationTests.Appium/WindowTests.cs
  7. 16
      tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs

4
src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs

@ -88,10 +88,10 @@ namespace Avalonia.Automation.Peers
if (string.IsNullOrWhiteSpace(result) && GetLabeledBy() is AutomationPeer labeledBy)
{
return labeledBy.GetName();
result = labeledBy.GetName();
}
return null;
return result;
}
protected override AutomationPeer? GetParentCore()

29
src/Avalonia.Controls/Chrome/CaptionButtons.cs

@ -15,6 +15,7 @@ namespace Avalonia.Controls.Chrome
[PseudoClasses(":minimized", ":normal", ":maximized", ":fullscreen")]
public class CaptionButtons : TemplatedControl
{
private Button? _restoreButton;
private IDisposable? _disposables;
/// <summary>
@ -28,14 +29,23 @@ namespace Avalonia.Controls.Chrome
{
HostWindow = hostWindow;
_disposables = HostWindow.GetObservable(Window.WindowStateProperty)
.Subscribe(x =>
{
PseudoClasses.Set(":minimized", x == WindowState.Minimized);
PseudoClasses.Set(":normal", x == WindowState.Normal);
PseudoClasses.Set(":maximized", x == WindowState.Maximized);
PseudoClasses.Set(":fullscreen", x == WindowState.FullScreen);
});
_disposables = new CompositeDisposable
{
HostWindow.GetObservable(Window.CanResizeProperty)
.Subscribe(x =>
{
if (_restoreButton is not null)
_restoreButton.IsEnabled = x;
}),
HostWindow.GetObservable(Window.WindowStateProperty)
.Subscribe(x =>
{
PseudoClasses.Set(":minimized", x == WindowState.Minimized);
PseudoClasses.Set(":normal", x == WindowState.Normal);
PseudoClasses.Set(":maximized", x == WindowState.Maximized);
PseudoClasses.Set(":fullscreen", x == WindowState.FullScreen);
}),
};
}
}
@ -94,6 +104,9 @@ namespace Avalonia.Controls.Chrome
restoreButton.Click += (sender, e) => OnRestore();
minimiseButton.Click += (sender, e) => OnMinimize();
fullScreenButton.Click += (sender, e) => OnToggleFullScreen();
restoreButton.IsEnabled = HostWindow?.CanResize ?? true;
_restoreButton = restoreButton;
}
}
}

12
src/Avalonia.Native/WindowImpl.cs

@ -21,6 +21,7 @@ namespace Avalonia.Native
private DoubleClickHelper _doubleClickHelper;
private readonly ITopLevelNativeMenuExporter _nativeMenuExporter;
private readonly AvaloniaNativeTextInputMethod _inputMethod;
private bool _canResize = true;
internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts,
AvaloniaNativeGlPlatformGraphics glFeature) : base(factory, opts, glFeature)
@ -75,6 +76,7 @@ namespace Avalonia.Native
public void CanResize(bool value)
{
_canResize = value;
_native.SetCanResize(value.AsComBool());
}
@ -137,14 +139,10 @@ namespace Avalonia.Native
{
if (_doubleClickHelper.IsDoubleClick(e.Timestamp, e.Position))
{
// TOGGLE WINDOW STATE.
if (WindowState == WindowState.Maximized || WindowState == WindowState.FullScreen)
if (_canResize)
{
WindowState = WindowState.Normal;
}
else
{
WindowState = WindowState.Maximized;
WindowState = WindowState is WindowState.Maximized or WindowState.FullScreen ?
WindowState.Normal : WindowState.Maximized;
}
}
else

12
src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml

@ -48,7 +48,8 @@
</Viewbox>
</Button>
<Button x:Name="PART_MinimiseButton"
Theme="{StaticResource FluentCaptionButton}">
Theme="{StaticResource FluentCaptionButton}"
AutomationProperties.Name="Minimise">
<Viewbox Width="11" Margin="2">
<Path Stretch="UniformToFill"
Fill="{TemplateBinding Foreground}"
@ -56,7 +57,8 @@
</Viewbox>
</Button>
<Button x:Name="PART_RestoreButton"
Theme="{StaticResource FluentCaptionButton}">
Theme="{StaticResource FluentCaptionButton}"
AutomationProperties.Name="Maximise">
<Viewbox Width="11" Margin="2">
<Viewbox.RenderTransform>
<RotateTransform Angle="-90" />
@ -70,7 +72,8 @@
<Button x:Name="PART_CloseButton"
Background="#ffe81123"
BorderBrush="#fff1707a"
Theme="{StaticResource FluentCaptionButton}">
Theme="{StaticResource FluentCaptionButton}"
AutomationProperties.Name="Close">
<Viewbox Width="11" Margin="2">
<Path Stretch="UniformToFill"
Fill="{TemplateBinding Foreground}"
@ -93,5 +96,8 @@
<Style Selector="^:fullscreen /template/ Button#PART_MinimiseButton">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="^ /template/ Button#PART_RestoreButton:disabled">
<Setter Property="Opacity" Value="0.2"/>
</Style>
</ControlTheme>
</ResourceDictionary>

12
src/Avalonia.Themes.Simple/Controls/CaptionButtons.xaml

@ -54,7 +54,8 @@
</Viewbox>
</Button>
<Button x:Name="PART_MinimiseButton"
Theme="{StaticResource SimpleCaptionButton}">
Theme="{StaticResource SimpleCaptionButton}"
AutomationProperties.Name="Minimise">
<Viewbox Width="11"
Margin="2">
<Path Data="M2048 1229v-205h-2048v205h2048z"
@ -63,7 +64,8 @@
</Viewbox>
</Button>
<Button x:Name="PART_RestoreButton"
Theme="{StaticResource SimpleCaptionButton}">
Theme="{StaticResource SimpleCaptionButton}"
AutomationProperties.Name="Maximise">
<Viewbox Width="11"
Margin="2">
<Viewbox.RenderTransform>
@ -78,7 +80,8 @@
<Button x:Name="PART_CloseButton"
Background="#ffe81123"
BorderBrush="#fff1707a"
Theme="{StaticResource SimpleCaptionButton}">
Theme="{StaticResource SimpleCaptionButton}"
AutomationProperties.Name="Close">
<Viewbox Width="11"
Margin="2">
<Path Data="M1169 1024l879 -879l-145 -145l-879 879l-879 -879l-145 145l879 879l-879 879l145 145l879 -879l879 879l145 -145z"
@ -102,5 +105,8 @@
<Style Selector="^:fullscreen /template/ Button#PART_MinimiseButton">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="^ /template/ Button#PART_RestoreButton:disabled">
<Setter Property="Opacity" Value="0.2"/>
</Style>
</ControlTheme>
</ResourceDictionary>

58
tests/Avalonia.IntegrationTests.Appium/WindowTests.cs

@ -1,11 +1,8 @@
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using Avalonia.Controls;
using Avalonia.Utilities;
using Avalonia.Media.Imaging;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Interactions;
@ -249,6 +246,37 @@ namespace Avalonia.IntegrationTests.Appium
Assert.Equal(new Rgba32(255, 0, 0), centerColor);
}
[Theory]
[InlineData(ShowWindowMode.NonOwned, true)]
[InlineData(ShowWindowMode.Owned, true)]
[InlineData(ShowWindowMode.Modal, true)]
[InlineData(ShowWindowMode.NonOwned, false)]
[InlineData(ShowWindowMode.Owned, false)]
[InlineData(ShowWindowMode.Modal, false)]
public void Window_Has_Disabled_Maximize_Button_When_CanResize_Is_False(ShowWindowMode mode, bool extendClientArea)
{
using (OpenWindow(null, mode, WindowStartupLocation.Manual, canResize: false, extendClientArea: extendClientArea))
{
var secondaryWindow = GetWindow("SecondaryWindow");
AppiumWebElement? maximizeButton;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
maximizeButton = extendClientArea ?
secondaryWindow.FindElementByXPath("//Button[@Name='Maximise']") :
secondaryWindow.FindElementByXPath("//TitleBar/Button[2]");
}
else
{
maximizeButton = mode == ShowWindowMode.NonOwned ?
secondaryWindow.FindElementByAccessibilityId("_XCUI:FullScreenWindow") :
secondaryWindow.FindElementByAccessibilityId("_XCUI:ZoomWindow");
}
Assert.False(maximizeButton.Enabled);
}
}
public static TheoryData<Size?, ShowWindowMode, WindowStartupLocation, bool> StartupLocationData()
{
var sizes = new Size?[] { null, new Size(400, 300) };
@ -333,7 +361,8 @@ namespace Avalonia.IntegrationTests.Appium
ShowWindowMode mode,
WindowStartupLocation location = WindowStartupLocation.Manual,
WindowState state = Controls.WindowState.Normal,
bool canResize = true)
bool canResize = true,
bool extendClientArea = false)
{
var sizeTextBox = _session.FindElementByAccessibilityId("ShowWindowSize");
var modeComboBox = _session.FindElementByAccessibilityId("ShowWindowMode");
@ -341,7 +370,8 @@ namespace Avalonia.IntegrationTests.Appium
var stateComboBox = _session.FindElementByAccessibilityId("ShowWindowState");
var canResizeCheckBox = _session.FindElementByAccessibilityId("ShowWindowCanResize");
var showButton = _session.FindElementByAccessibilityId("ShowWindow");
var extendClientAreaCheckBox = _session.FindElementByAccessibilityId("ShowWindowExtendClientAreaToDecorationsHint");
if (size.HasValue)
sizeTextBox.SendKeys($"{size.Value.Width}, {size.Value.Height}");
@ -366,9 +396,27 @@ namespace Avalonia.IntegrationTests.Appium
if (canResizeCheckBox.GetIsChecked() != canResize)
canResizeCheckBox.Click();
if (extendClientAreaCheckBox.GetIsChecked() != extendClientArea)
extendClientAreaCheckBox.Click();
return showButton.OpenWindowWithClick();
}
private AppiumWebElement GetWindow(string identifier)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
// The Avalonia a11y tree currently exposes two nested Window elements, this is a bug and should be fixed
// but in the meantime use the `parent::' selector to return the parent "real" window.
return _session.FindElementByXPath(
$"XCUIElementTypeWindow//*[@identifier='{identifier}']/parent::XCUIElementTypeWindow");
}
else
{
return _session.FindElementByXPath($"//Window[@AutomationId='{identifier}']");
}
}
private WindowInfo GetWindowInfo()
{
PixelRect? ReadOwnerRect()

16
tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs

@ -336,22 +336,6 @@ namespace Avalonia.IntegrationTests.Appium
secondaryWindow.FindElementByAccessibilityId("_XCUI:CloseWindow").Click();
}
[PlatformTheory(TestPlatforms.MacOS)]
[InlineData(ShowWindowMode.NonOwned)]
[InlineData(ShowWindowMode.Owned)]
[InlineData(ShowWindowMode.Modal)]
public void Window_Has_Disabled_Zoom_Button_When_CanResize_Is_False(ShowWindowMode mode)
{
using (OpenWindow(null, mode, WindowStartupLocation.Manual, canResize: false))
{
var secondaryWindow = GetWindow("SecondaryWindow");
var zoomButton = mode == ShowWindowMode.NonOwned ?
secondaryWindow.FindElementByAccessibilityId("_XCUI:FullScreenWindow") :
secondaryWindow.FindElementByAccessibilityId("_XCUI:ZoomWindow");
Assert.False(zoomButton.Enabled);
}
}
[PlatformFact(TestPlatforms.MacOS)]
public void Toggling_SystemDecorations_Should_Preserve_ExtendClientArea()
{

Loading…
Cancel
Save