diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm
index 104611eabc0..925880534ad 100644
--- a/native/Avalonia.Native/src/OSX/WindowImpl.mm
+++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm
@@ -281,10 +281,13 @@
case SystemDecorationsFull:
[Window setHasShadow:YES];
- [Window setTitleVisibility:NSWindowTitleVisible];
- [Window setTitlebarAppearsTransparent:NO];
[Window setTitle:_lastTitle];
+ if (!_isClientAreaExtended) {
+ [Window setTitleVisibility:NSWindowTitleVisible];
+ [Window setTitlebarAppearsTransparent:NO];
+ }
+
if (currentWindowState == Maximized) {
auto newFrame = [Window contentRectForFrameRect:[Window frame]].size;
@@ -611,7 +614,8 @@
}
bool wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome);
- bool hasTrafficLights = _isClientAreaExtended ? wantsChrome : _decorations == SystemDecorationsFull;
+ bool hasTrafficLights = (_decorations == SystemDecorationsFull) &&
+ (_isClientAreaExtended ? wantsChrome : true);
NSButton* closeButton = [Window standardWindowButton:NSWindowCloseButton];
NSButton* miniaturizeButton = [Window standardWindowButton:NSWindowMiniaturizeButton];
diff --git a/samples/IntegrationTestApp/MainWindow.axaml b/samples/IntegrationTestApp/MainWindow.axaml
index d4a96c5f175..bd840c03a7e 100644
--- a/samples/IntegrationTestApp/MainWindow.axaml
+++ b/samples/IntegrationTestApp/MainWindow.axaml
@@ -151,6 +151,12 @@
Maximized
FullScreen
+
+ None
+ BorderOnly
+ Full
+
+ ExtendClientAreaToDecorationsHint
Can Resize
diff --git a/samples/IntegrationTestApp/MainWindow.axaml.cs b/samples/IntegrationTestApp/MainWindow.axaml.cs
index 19eb1d64b0f..e9b86b3bdd3 100644
--- a/samples/IntegrationTestApp/MainWindow.axaml.cs
+++ b/samples/IntegrationTestApp/MainWindow.axaml.cs
@@ -68,6 +68,8 @@ private void ShowWindow()
var locationComboBox = this.GetControl("ShowWindowLocation");
var stateComboBox = this.GetControl("ShowWindowState");
var size = !string.IsNullOrWhiteSpace(sizeTextBox.Text) ? Size.Parse(sizeTextBox.Text) : (Size?)null;
+ var systemDecorations = this.GetControl("ShowWindowSystemDecorations");
+ var extendClientArea = this.GetControl("ShowWindowExtendClientAreaToDecorationsHint");
var canResizeCheckBox = this.GetControl("ShowWindowCanResize");
var owner = (Window)this.GetVisualRoot()!;
@@ -95,6 +97,8 @@ private void ShowWindow()
}
sizeTextBox.Text = string.Empty;
+ window.ExtendClientAreaToDecorationsHint = extendClientArea.IsChecked ?? false;
+ window.SystemDecorations = (SystemDecorations)systemDecorations.SelectedIndex;
window.WindowState = (WindowState)stateComboBox.SelectedIndex;
switch (modeComboBox.SelectedIndex)
diff --git a/samples/IntegrationTestApp/ShowWindowTest.axaml b/samples/IntegrationTestApp/ShowWindowTest.axaml
index bd6910dd4de..720ff6c344d 100644
--- a/samples/IntegrationTestApp/ShowWindowTest.axaml
+++ b/samples/IntegrationTestApp/ShowWindowTest.axaml
@@ -6,7 +6,7 @@
x:DataType="Window"
Title="Show Window Test">
-
+
@@ -35,13 +35,25 @@
FullScreen
-
-
+
+
+ None
+ BorderOnly
+ Full
+
+
+
+ ExtendClientAreaToDecorationsHint
+
+
+
+
-
-
+
+
-
+
diff --git a/tests/Avalonia.IntegrationTests.Appium/ElementExtensions.cs b/tests/Avalonia.IntegrationTests.Appium/ElementExtensions.cs
index 2184c5c155c..99c30085d6e 100644
--- a/tests/Avalonia.IntegrationTests.Appium/ElementExtensions.cs
+++ b/tests/Avalonia.IntegrationTests.Appium/ElementExtensions.cs
@@ -11,19 +11,26 @@
namespace Avalonia.IntegrationTests.Appium
{
+ public record class WindowChrome(
+ AppiumWebElement? Close,
+ AppiumWebElement? Minimize,
+ AppiumWebElement? Maximize,
+ AppiumWebElement? FullScreen);
+
internal static class ElementExtensions
{
public static IReadOnlyList GetChildren(this AppiumWebElement element) =>
element.FindElementsByXPath("*/*");
- public static (AppiumWebElement close, AppiumWebElement minimize, AppiumWebElement maximize) GetChromeButtons(this AppiumWebElement window)
+ public static WindowChrome GetChromeButtons(this AppiumWebElement window)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
- var closeButton = window.FindElementByXPath("//XCUIElementTypeButton[1]");
- var fullscreenButton = window.FindElementByXPath("//XCUIElementTypeButton[2]");
- var minimizeButton = window.FindElementByXPath("//XCUIElementTypeButton[3]");
- return (closeButton, minimizeButton, fullscreenButton);
+ var closeButton = window.FindElementsByAccessibilityId("_XCUI:CloseWindow").FirstOrDefault();
+ var fullscreenButton = window.FindElementsByAccessibilityId("_XCUI:FullScreenWindow").FirstOrDefault();
+ var minimizeButton = window.FindElementsByAccessibilityId("_XCUI:MinimizeWindow").FirstOrDefault();
+ var zoomButton = window.FindElementsByAccessibilityId("_XCUI:ZoomWindow").FirstOrDefault();
+ return new(closeButton, minimizeButton, zoomButton, fullscreenButton);
}
throw new NotSupportedException("GetChromeButtons not supported on this platform.");
@@ -138,7 +145,7 @@ public static IDisposable OpenWindowWithClick(this AppiumWebElement element)
var text = windows.Select(x => x.Text).ToList();
var newWindow = session.FindElements(By.XPath("/XCUIElementTypeApplication/XCUIElementTypeWindow"))
.First(x => x.Text == newWindowTitle);
- var (close, _, _) = ((AppiumWebElement)newWindow).GetChromeButtons();
+ var close = ((AppiumWebElement)newWindow).FindElementByAccessibilityId("_XCUI:CloseWindow");
close!.Click();
Thread.Sleep(1000);
});
diff --git a/tests/Avalonia.IntegrationTests.Appium/PlatformFactAttribute.cs b/tests/Avalonia.IntegrationTests.Appium/PlatformFactAttribute.cs
index 3acf9fd5e2a..17c42216e9b 100644
--- a/tests/Avalonia.IntegrationTests.Appium/PlatformFactAttribute.cs
+++ b/tests/Avalonia.IntegrationTests.Appium/PlatformFactAttribute.cs
@@ -17,6 +17,7 @@ internal enum TestPlatforms
internal class PlatformFactAttribute : FactAttribute
{
private readonly string? _reason;
+ private string? _skip;
public PlatformFactAttribute(TestPlatforms platforms, string? reason = null)
{
@@ -28,8 +29,16 @@ public PlatformFactAttribute(TestPlatforms platforms, string? reason = null)
public override string? Skip
{
- get => IsSupported() ? null : $"Ignored on {RuntimeInformation.OSDescription}" + (_reason is not null ? $" reason: \"{_reason}\"" : "");
- set => throw new NotSupportedException();
+ get
+ {
+ if (_skip is not null)
+ return _skip;
+ if (!IsSupported())
+ return $"Ignored on {RuntimeInformation.OSDescription}" +
+ (_reason is not null ? $" reason: '{_reason}'" : "");
+ return null;
+ }
+ set => _skip = value;
}
private bool IsSupported()
diff --git a/tests/Avalonia.IntegrationTests.Appium/PlatformTheoryAttribute.cs b/tests/Avalonia.IntegrationTests.Appium/PlatformTheoryAttribute.cs
index 7ac30ee11ba..46a8e1af987 100644
--- a/tests/Avalonia.IntegrationTests.Appium/PlatformTheoryAttribute.cs
+++ b/tests/Avalonia.IntegrationTests.Appium/PlatformTheoryAttribute.cs
@@ -7,14 +7,21 @@ namespace Avalonia.IntegrationTests.Appium
{
internal class PlatformTheoryAttribute : TheoryAttribute
{
+ private string? _skip;
+
public PlatformTheoryAttribute(TestPlatforms platforms = TestPlatforms.All) => Platforms = platforms;
public TestPlatforms Platforms { get; }
public override string? Skip
{
- get => IsSupported() ? null : $"Ignored on {RuntimeInformation.OSDescription}";
- set => throw new NotSupportedException();
+ get
+ {
+ if (_skip is not null)
+ return _skip;
+ return !IsSupported() ? $"Ignored on {RuntimeInformation.OSDescription}" : null;
+ }
+ set => _skip = value;
}
private bool IsSupported()
diff --git a/tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs b/tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs
index 039d30bbc12..68a31c7ccb2 100644
--- a/tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs
+++ b/tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs
@@ -83,9 +83,9 @@ public void WindowOrder_Modal_Dialog_Stays_InFront_Of_Parent_When_Clicking_Resiz
public void WindowOrder_Modal_Dialog_Stays_InFront_Of_Parent_When_In_Fullscreen()
{
var mainWindow = GetWindow("MainWindow");
- var buttons = mainWindow.GetChromeButtons();
+ var fullScreen = mainWindow.FindElementByAccessibilityId("_XCUI:FullScreenWindow");
- buttons.maximize.Click();
+ fullScreen.Click();
Thread.Sleep(500);
@@ -239,17 +239,18 @@ public void WindowOrder_Owned_Is_Correct_After_Closing_Window()
public void Parent_Window_Has_Disabled_ChromeButtons_When_Modal_Dialog_Shown()
{
var window = GetWindow("MainWindow");
- var (closeButton, miniaturizeButton, zoomButton) = window.GetChromeButtons();
+ var windowChrome = window.GetChromeButtons();
- Assert.True(closeButton.Enabled);
- Assert.True(zoomButton.Enabled);
- Assert.True(miniaturizeButton.Enabled);
+ Assert.True(windowChrome.Close!.Enabled);
+ Assert.True(windowChrome.FullScreen!.Enabled);
+ Assert.True(windowChrome.Minimize!.Enabled);
+ Assert.Null(windowChrome.Maximize);
using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Modal, WindowStartupLocation.CenterOwner))
{
- Assert.False(closeButton.Enabled);
- Assert.False(zoomButton.Enabled);
- Assert.False(miniaturizeButton.Enabled);
+ Assert.False(windowChrome.Close!.Enabled);
+ Assert.False(windowChrome.FullScreen!.Enabled);
+ Assert.False(windowChrome.Minimize!.Enabled);
}
}
@@ -259,11 +260,11 @@ public void Minimize_Button_Is_Disabled_On_Modal_Dialog()
using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Modal, WindowStartupLocation.CenterOwner))
{
var secondaryWindow = GetWindow("SecondaryWindow");
- var (closeButton, miniaturizeButton, zoomButton) = secondaryWindow.GetChromeButtons();
+ var windowChrome = secondaryWindow.GetChromeButtons();
- Assert.True(closeButton.Enabled);
- Assert.True(zoomButton.Enabled);
- Assert.False(miniaturizeButton.Enabled);
+ Assert.True(windowChrome.Close!.Enabled);
+ Assert.True(windowChrome.Maximize!.Enabled);
+ Assert.False(windowChrome.Minimize!.Enabled);
}
}
@@ -274,7 +275,7 @@ public void Minimize_Button_Disabled_Owned_Window(ShowWindowMode mode)
using (OpenWindow(new PixelSize(200, 100), mode, WindowStartupLocation.Manual))
{
var secondaryWindow = GetWindow("SecondaryWindow");
- var (_, miniaturizeButton, _) = secondaryWindow.GetChromeButtons();
+ var miniaturizeButton = secondaryWindow.FindElementByAccessibilityId("_XCUI:MinimizeWindow");
Assert.False(miniaturizeButton.Enabled);
}
@@ -288,7 +289,7 @@ public void Minimize_Button_Minimizes_Window(ShowWindowMode mode)
using (OpenWindow(new PixelSize(200, 100), mode, WindowStartupLocation.Manual))
{
var secondaryWindow = GetWindow("SecondaryWindow");
- var (_, miniaturizeButton, _) = secondaryWindow.GetChromeButtons();
+ var miniaturizeButton = secondaryWindow.FindElementByAccessibilityId("_XCUI:MinimizeWindow");
miniaturizeButton.Click();
Thread.Sleep(1000);
@@ -332,7 +333,7 @@ public void Hidden_Child_Window_Is_Not_Reshown_When_Parent_Clicked()
// Close the window manually.
secondaryWindow = GetWindow("SecondaryWindow");
- secondaryWindow.GetChromeButtons().close.Click();
+ secondaryWindow.FindElementByAccessibilityId("_XCUI:CloseWindow").Click();
}
[PlatformTheory(TestPlatforms.MacOS)]
@@ -344,22 +345,92 @@ public void Window_Has_Disabled_Zoom_Button_When_CanResize_Is_False(ShowWindowMo
using (OpenWindow(null, mode, WindowStartupLocation.Manual, canResize: false))
{
var secondaryWindow = GetWindow("SecondaryWindow");
- var (_, _, zoomButton) = secondaryWindow.GetChromeButtons();
+ 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()
+ {
+ // #10650
+ using (OpenWindow(extendClientArea: true))
+ {
+ var secondaryWindow = GetWindow("SecondaryWindow");
+
+ // The XPath of the title bar text _should_ be "XCUIElementTypeStaticText"
+ // but Appium seems to put a fake node between the window and the title bar
+ // https://stackoverflow.com/a/71914227/6448
+ var titleBar = secondaryWindow.FindElementsByXPath("/*/XCUIElementTypeStaticText").Count;
+
+ Assert.Equal(0, titleBar);
+
+ secondaryWindow.FindElementByAccessibilityId("CurrentSystemDecorations").Click();
+ _session.FindElementByAccessibilityId("SystemDecorationsNone").SendClick();
+ secondaryWindow.FindElementByAccessibilityId("CurrentSystemDecorations").Click();
+ _session.FindElementByAccessibilityId("SystemDecorationsFull").SendClick();
+
+ titleBar = secondaryWindow.FindElementsByXPath("/*/XCUIElementTypeStaticText").Count;
+ Assert.Equal(0, titleBar);
+ }
+ }
+
+ [PlatformTheory(TestPlatforms.MacOS)]
+ [InlineData(SystemDecorations.None)]
+ [InlineData(SystemDecorations.BorderOnly)]
+ [InlineData(SystemDecorations.Full)]
+ public void ExtendClientArea_SystemDecorations_Shows_Correct_Buttons(SystemDecorations decorations)
+ {
+ // #10650
+ using (OpenWindow(extendClientArea: true, systemDecorations: decorations))
+ {
+ var secondaryWindow = GetWindow("SecondaryWindow");
+
+ try
+ {
+ var chrome = secondaryWindow.GetChromeButtons();
+
+ if (decorations == SystemDecorations.Full)
+ {
+ Assert.NotNull(chrome.Close);
+ Assert.NotNull(chrome.Minimize);
+ Assert.NotNull(chrome.FullScreen);
+ }
+ else
+ {
+ Assert.Null(chrome.Close);
+ Assert.Null(chrome.Minimize);
+ Assert.Null(chrome.FullScreen);
+ }
+ }
+ finally
+ {
+ if (decorations != SystemDecorations.Full)
+ {
+ secondaryWindow.FindElementByAccessibilityId("CurrentSystemDecorations").Click();
+ _session.FindElementByAccessibilityId("SystemDecorationsFull").SendClick();
+ }
+ }
+ }
+ }
+
private IDisposable OpenWindow(
- PixelSize? size,
- ShowWindowMode mode,
- WindowStartupLocation location,
- bool canResize = true)
+ PixelSize? size = null,
+ ShowWindowMode mode = ShowWindowMode.NonOwned,
+ WindowStartupLocation location = WindowStartupLocation.Manual,
+ bool canResize = true,
+ SystemDecorations systemDecorations = SystemDecorations.Full,
+ bool extendClientArea = false)
{
var sizeTextBox = _session.FindElementByAccessibilityId("ShowWindowSize");
var modeComboBox = _session.FindElementByAccessibilityId("ShowWindowMode");
var locationComboBox = _session.FindElementByAccessibilityId("ShowWindowLocation");
var canResizeCheckBox = _session.FindElementByAccessibilityId("ShowWindowCanResize");
var showButton = _session.FindElementByAccessibilityId("ShowWindow");
+ var systemDecorationsComboBox = _session.FindElementByAccessibilityId("ShowWindowSystemDecorations");
+ var extendClientAreaCheckBox = _session.FindElementByAccessibilityId("ShowWindowExtendClientAreaToDecorationsHint");
if (size.HasValue)
sizeTextBox.SendKeys($"{size.Value.Width}, {size.Value.Height}");
@@ -379,6 +450,15 @@ private IDisposable OpenWindow(
if (canResizeCheckBox.GetIsChecked() != canResize)
canResizeCheckBox.Click();
+ if (systemDecorationsComboBox.GetComboBoxValue() != systemDecorations.ToString())
+ {
+ systemDecorationsComboBox.Click();
+ _session.FindElementByName(systemDecorations.ToString()).SendClick();
+ }
+
+ if (extendClientAreaCheckBox.GetIsChecked() != extendClientArea)
+ extendClientAreaCheckBox.Click();
+
return showButton.OpenWindowWithClick();
}