Skip to content

Fix setting WindowState before showing Window. #9221

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Nov 18, 2022
6 changes: 6 additions & 0 deletions samples/IntegrationTestApp/MainWindow.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,12 @@
<ComboBoxItem>CenterScreen</ComboBoxItem>
<ComboBoxItem>CenterOwner</ComboBoxItem>
</ComboBox>
<ComboBox Name="ShowWindowState" SelectedIndex="0">
<ComboBoxItem>Normal</ComboBoxItem>
<ComboBoxItem>Minimized</ComboBoxItem>
<ComboBoxItem>Maximized</ComboBoxItem>
<ComboBoxItem>FullScreen</ComboBoxItem>
</ComboBox>
<Button Name="ShowWindow">Show Window</Button>
<Button Name="SendToBack">Send to Back</Button>
<Button Name="ExitFullscreen">Exit Fullscreen</Button>
Expand Down
2 changes: 2 additions & 0 deletions samples/IntegrationTestApp/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ private void ShowWindow()
var sizeTextBox = this.GetControl<TextBox>("ShowWindowSize");
var modeComboBox = this.GetControl<ComboBox>("ShowWindowMode");
var locationComboBox = this.GetControl<ComboBox>("ShowWindowLocation");
var stateComboBox = this.GetControl<ComboBox>("ShowWindowState");
var size = !string.IsNullOrWhiteSpace(sizeTextBox.Text) ? Size.Parse(sizeTextBox.Text) : (Size?)null;
var owner = (Window)this.GetVisualRoot()!;

Expand All @@ -85,6 +86,7 @@ private void ShowWindow()
}

sizeTextBox.Text = string.Empty;
window.WindowState = (WindowState)stateComboBox.SelectedIndex;

switch (modeComboBox.SelectedIndex)
{
Expand Down
2 changes: 1 addition & 1 deletion samples/IntegrationTestApp/ShowWindowTest.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
<ComboBoxItem>Normal</ComboBoxItem>
<ComboBoxItem>Minimized</ComboBoxItem>
<ComboBoxItem>Maximized</ComboBoxItem>
<ComboBoxItem>Fullscreen</ComboBoxItem>
<ComboBoxItem>FullScreen</ComboBoxItem>
</ComboBox>

<Label Grid.Column="0" Grid.Row="8">Order (mac)</Label>
Expand Down
10 changes: 7 additions & 3 deletions src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -588,9 +588,13 @@ protected virtual unsafe IntPtr AppWndProc(IntPtr hWnd, uint msg, IntPtr wParam,
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)
{
Expand Down
11 changes: 9 additions & 2 deletions src/Windows/Avalonia.Win32/WindowImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,11 @@ public WindowState WindowState
{
get
{
if (!IsWindowVisible(_hwnd))
{
return _showWindowState;
}

if (_isFullScreenActive)
{
return WindowState.FullScreen;
Expand Down Expand Up @@ -562,6 +567,9 @@ public IRenderer CreateRenderer(IRenderRoot root)

public void Resize(Size value, PlatformResizeReason reason)
{
if (WindowState is WindowState.Maximized or WindowState.FullScreen)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or minimized?

!= Normal?

return;

int requestedClientWidth = (int)(value.Width * RenderScaling);
int requestedClientHeight = (int)(value.Height * RenderScaling);

Expand Down Expand Up @@ -902,11 +910,10 @@ private void SetFullScreen(bool fullscreen)

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
{
Expand Down
17 changes: 12 additions & 5 deletions tests/Avalonia.IntegrationTests.Appium/ElementExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
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;
Expand Down Expand Up @@ -110,24 +111,30 @@ public static IDisposable OpenWindowWithClick(this AppiumWebElement element)
{
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
Expand Down
99 changes: 92 additions & 7 deletions tests/Avalonia.IntegrationTests.Appium/WindowTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,53 @@ public void StartupLocation(Size? size, ShowWindowMode mode, WindowStartupLocati
}
}
}


[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()
{
Expand Down Expand Up @@ -100,7 +146,7 @@ public void OnWindows_Docked_Windows_Retain_Size_Position_When_Restored()
[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");
Expand All @@ -123,8 +169,8 @@ public void WindowState(ShowWindowMode mode)
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);
Expand Down Expand Up @@ -163,6 +209,35 @@ public void WindowState(ShowWindowMode mode)
return data;
}

public static TheoryData<Size?, ShowWindowMode, WindowState> WindowStateData()
{
var sizes = new Size?[] { null, new Size(400, 300) };
var data = new TheoryData<Size?, ShowWindowMode, WindowState>();

foreach (var size in sizes)
{
foreach (var mode in Enum.GetValues<ShowWindowMode>())
{
foreach (var state in Enum.GetValues<WindowState>())
{
// 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))
Expand All @@ -189,11 +264,16 @@ private static void AssertCloseEnough(PixelPoint expected, PixelPoint actual)
}
}

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)
Expand All @@ -205,6 +285,9 @@ private IDisposable OpenWindow(Size? size, ShowWindowMode mode, WindowStartupLoc
locationComboBox.Click();
_session.FindElementByName(location.ToString()).SendClick();

stateComboBox.Click();
_session.FindElementByName(state.ToString()).SendClick();

return showButton.OpenWindowWithClick();
}

Expand All @@ -228,7 +311,8 @@ private WindowInfo GetWindowInfo()
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<WindowState>(_session.FindElementByAccessibilityId("WindowState").Text));
}
catch (OpenQA.Selenium.NoSuchElementException) when (retry++ < 3)
{
Expand All @@ -252,6 +336,7 @@ private record WindowInfo(
PixelPoint Position,
PixelRect? OwnerRect,
PixelRect ScreenRect,
double Scaling);
double Scaling,
WindowState WindowState);
}
}