diff --git a/samples/IntegrationTestApp/MainWindow.axaml b/samples/IntegrationTestApp/MainWindow.axaml index 493a3c6567..038ced4e5c 100644 --- a/samples/IntegrationTestApp/MainWindow.axaml +++ b/samples/IntegrationTestApp/MainWindow.axaml @@ -128,6 +128,12 @@ CenterScreen CenterOwner + + Normal + Minimized + Maximized + FullScreen + diff --git a/samples/IntegrationTestApp/MainWindow.axaml.cs b/samples/IntegrationTestApp/MainWindow.axaml.cs index e8fb455c35..c1acc7ca88 100644 --- a/samples/IntegrationTestApp/MainWindow.axaml.cs +++ b/samples/IntegrationTestApp/MainWindow.axaml.cs @@ -59,6 +59,7 @@ namespace IntegrationTestApp var sizeTextBox = this.GetControl("ShowWindowSize"); var modeComboBox = this.GetControl("ShowWindowMode"); var locationComboBox = this.GetControl("ShowWindowLocation"); + var stateComboBox = this.GetControl("ShowWindowState"); var size = !string.IsNullOrWhiteSpace(sizeTextBox.Text) ? Size.Parse(sizeTextBox.Text) : (Size?)null; var owner = (Window)this.GetVisualRoot()!; @@ -85,6 +86,7 @@ namespace IntegrationTestApp } sizeTextBox.Text = string.Empty; + window.WindowState = (WindowState)stateComboBox.SelectedIndex; switch (modeComboBox.SelectedIndex) { diff --git a/samples/IntegrationTestApp/ShowWindowTest.axaml b/samples/IntegrationTestApp/ShowWindowTest.axaml index 68d1f3cf2f..c3a0d8d2e2 100644 --- a/samples/IntegrationTestApp/ShowWindowTest.axaml +++ b/samples/IntegrationTestApp/ShowWindowTest.axaml @@ -30,7 +30,7 @@ Normal Minimized Maximized - Fullscreen + FullScreen diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs index 8aa4746b5a..4ce13ae0e0 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs @@ -588,9 +588,13 @@ namespace Avalonia.Win32 Resized(clientSize / RenderScaling, _resizeReason); } - var windowState = size == SizeCommand.Maximized ? - WindowState.Maximized : - (size == SizeCommand.Minimized ? WindowState.Minimized : WindowState.Normal); + var windowState = size switch + { + SizeCommand.Maximized => WindowState.Maximized, + SizeCommand.Minimized => WindowState.Minimized, + _ when _isFullScreenActive => WindowState.FullScreen, + _ => WindowState.Normal, + }; if (windowState != _lastWindowState) { diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 9ed1a50ff2..42c576d01c 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -269,6 +269,11 @@ namespace Avalonia.Win32 { get { + if (!IsWindowVisible(_hwnd)) + { + return _showWindowState; + } + if (_isFullScreenActive) { return WindowState.FullScreen; @@ -562,6 +567,9 @@ namespace Avalonia.Win32 public void Resize(Size value, PlatformResizeReason reason) { + if (WindowState != WindowState.Normal) + return; + int requestedClientWidth = (int)(value.Width * RenderScaling); int requestedClientHeight = (int)(value.Height * RenderScaling); @@ -902,11 +910,10 @@ namespace Avalonia.Win32 var window_rect = monitor_info.rcMonitor.ToPixelRect(); + _isFullScreenActive = true; SetWindowPos(_hwnd, IntPtr.Zero, window_rect.X, window_rect.Y, window_rect.Width, window_rect.Height, SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_FRAMECHANGED); - - _isFullScreenActive = true; } else { diff --git a/tests/Avalonia.IntegrationTests.Appium/ElementExtensions.cs b/tests/Avalonia.IntegrationTests.Appium/ElementExtensions.cs index 4b361c6716..e7837a6971 100644 --- a/tests/Avalonia.IntegrationTests.Appium/ElementExtensions.cs +++ b/tests/Avalonia.IntegrationTests.Appium/ElementExtensions.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Reactive.Disposables; using System.Runtime.InteropServices; +using System.Threading; using OpenQA.Selenium; using OpenQA.Selenium.Appium; using OpenQA.Selenium.Interactions; @@ -110,24 +111,30 @@ namespace Avalonia.IntegrationTests.Appium { var oldWindows = session.FindElements(By.XPath("/XCUIElementTypeApplication/XCUIElementTypeWindow")); var oldWindowTitles = oldWindows.ToDictionary(x => x.Text); - + element.Click(); + + // Wait for animations to run. + Thread.Sleep(1000); var newWindows = session.FindElements(By.XPath("/XCUIElementTypeApplication/XCUIElementTypeWindow")); var newWindowTitles = newWindows.ToDictionary(x => x.Text); var newWindowTitle = Assert.Single(newWindowTitles.Keys.Except(oldWindowTitles.Keys)); - var newWindow = (AppiumWebElement)newWindowTitles[newWindowTitle]; - + return Disposable.Create(() => { // TODO: We should be able to use Cmd+W here but Avalonia apps don't seem to have this shortcut // set up by default. - var (close, _, _) = newWindow.GetChromeButtons(); + var windows = session.FindElements(By.XPath("/XCUIElementTypeApplication/XCUIElementTypeWindow")); + 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(); close!.Click(); }); } } - + public static void SendClick(this AppiumWebElement element) { // The Click() method seems to correspond to accessibilityPerformPress on macOS but certain controls diff --git a/tests/Avalonia.IntegrationTests.Appium/WindowTests.cs b/tests/Avalonia.IntegrationTests.Appium/WindowTests.cs index 9cce169744..b6ac75b78e 100644 --- a/tests/Avalonia.IntegrationTests.Appium/WindowTests.cs +++ b/tests/Avalonia.IntegrationTests.Appium/WindowTests.cs @@ -57,7 +57,53 @@ namespace Avalonia.IntegrationTests.Appium } } } - + + [Theory] + [MemberData(nameof(WindowStateData))] + public void WindowState(Size? size, ShowWindowMode mode, WindowState state) + { + using var window = OpenWindow(size, mode, state: state); + + try + { + var info = GetWindowInfo(); + + Assert.Equal(state, info.WindowState); + + switch (state) + { + case Controls.WindowState.Normal: + Assert.True(info.FrameSize.Width * info.Scaling < info.ScreenRect.Size.Width); + Assert.True(info.FrameSize.Height * info.Scaling < info.ScreenRect.Size.Height); + break; + case Controls.WindowState.Maximized: + case Controls.WindowState.FullScreen: + Assert.True(info.FrameSize.Width * info.Scaling >= info.ScreenRect.Size.Width); + Assert.True(info.FrameSize.Height * info.Scaling >= info.ScreenRect.Size.Height); + break; + } + } + finally + { + if (state == Controls.WindowState.FullScreen) + { + try + { + _session.FindElementByAccessibilityId("WindowState").SendClick(); + _session.FindElementByName("Normal").SendClick(); + + // Wait for animations to run. + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + Thread.Sleep(1000); + } + catch + { + /* Ignore errors in cleanup */ + } + } + } + } + [PlatformFact(TestPlatforms.Windows)] public void OnWindows_Docked_Windows_Retain_Size_Position_When_Restored() { @@ -100,7 +146,7 @@ namespace Avalonia.IntegrationTests.Appium [InlineData(ShowWindowMode.NonOwned)] [InlineData(ShowWindowMode.Owned)] [InlineData(ShowWindowMode.Modal)] - public void WindowState(ShowWindowMode mode) + public void ShowMode(ShowWindowMode mode) { using var window = OpenWindow(null, mode, WindowStartupLocation.Manual); var windowState = _session.FindElementByAccessibilityId("WindowState"); @@ -123,8 +169,8 @@ namespace Avalonia.IntegrationTests.Appium if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || mode == ShowWindowMode.NonOwned) { windowState.Click(); - _session.FindElementByName("Fullscreen").SendClick(); - Assert.Equal("Fullscreen", windowState.GetComboBoxValue()); + _session.FindElementByName("FullScreen").SendClick(); + Assert.Equal("FullScreen", windowState.GetComboBoxValue()); current = GetWindowInfo(); var clientSize = PixelSize.FromSize(current.ClientSize, current.Scaling); @@ -163,6 +209,35 @@ namespace Avalonia.IntegrationTests.Appium return data; } + public static TheoryData WindowStateData() + { + var sizes = new Size?[] { null, new Size(400, 300) }; + var data = new TheoryData(); + + foreach (var size in sizes) + { + foreach (var mode in Enum.GetValues()) + { + foreach (var state in Enum.GetValues()) + { + // Not sure how to handle testing minimized windows currently. + if (state == Controls.WindowState.Minimized) + continue; + + // Child/Modal windows cannot be fullscreen on macOS. + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && + state == Controls.WindowState.FullScreen && + mode != ShowWindowMode.NonOwned) + continue; + + data.Add(size, mode, state); + } + } + } + + return data; + } + private static void AssertCloseEnough(PixelPoint expected, PixelPoint actual) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -189,11 +264,16 @@ namespace Avalonia.IntegrationTests.Appium } } - private IDisposable OpenWindow(Size? size, ShowWindowMode mode, WindowStartupLocation location) + private IDisposable OpenWindow( + Size? size, + ShowWindowMode mode, + WindowStartupLocation location = WindowStartupLocation.Manual, + WindowState state = Controls.WindowState.Normal) { var sizeTextBox = _session.FindElementByAccessibilityId("ShowWindowSize"); var modeComboBox = _session.FindElementByAccessibilityId("ShowWindowMode"); var locationComboBox = _session.FindElementByAccessibilityId("ShowWindowLocation"); + var stateComboBox = _session.FindElementByAccessibilityId("ShowWindowState"); var showButton = _session.FindElementByAccessibilityId("ShowWindow"); if (size.HasValue) @@ -205,6 +285,9 @@ namespace Avalonia.IntegrationTests.Appium locationComboBox.Click(); _session.FindElementByName(location.ToString()).SendClick(); + stateComboBox.Click(); + _session.FindElementByName(state.ToString()).SendClick(); + return showButton.OpenWindowWithClick(); } @@ -228,7 +311,8 @@ namespace Avalonia.IntegrationTests.Appium PixelPoint.Parse(_session.FindElementByAccessibilityId("Position").Text), ReadOwnerRect(), PixelRect.Parse(_session.FindElementByAccessibilityId("ScreenRect").Text), - double.Parse(_session.FindElementByAccessibilityId("Scaling").Text)); + double.Parse(_session.FindElementByAccessibilityId("Scaling").Text), + Enum.Parse(_session.FindElementByAccessibilityId("WindowState").Text)); } catch (OpenQA.Selenium.NoSuchElementException) when (retry++ < 3) { @@ -252,6 +336,7 @@ namespace Avalonia.IntegrationTests.Appium PixelPoint Position, PixelRect? OwnerRect, PixelRect ScreenRect, - double Scaling); + double Scaling, + WindowState WindowState); } }