Skip to content

Commit

Permalink
Win32 - Ensure owner topmost flag is set if its topmost when showing …
Browse files Browse the repository at this point in the history
…a owned window (#16104)

* ensure owner topmost flag is set if its topmost when showing a owned window

* add comments on why HWND_TOPMOST is set again

* add Topmost with owned window integration tests

* fix tests
  • Loading branch information
emmauss authored Jun 26, 2024
1 parent 24914cc commit fc26fb6
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 0 deletions.
6 changes: 6 additions & 0 deletions samples/IntegrationTestApp/IntegrationTestApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@
<ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj" />
</ItemGroup>

<ItemGroup>
<Compile Update="TopmostWindowTest.axaml.cs">
<DependentUpon>TopmostWindowTest.axaml</DependentUpon>
</Compile>
</ItemGroup>

<Import Project="..\..\build\BuildTargets.targets" />
<Import Project="..\..\build\SampleApp.props" />
Expand Down
1 change: 1 addition & 0 deletions samples/IntegrationTestApp/MainWindow.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@
<Button Name="EnterFullscreen">Enter Fullscreen</Button>
<Button Name="ExitFullscreen">Exit Fullscreen</Button>
<Button Name="RestoreAll">Restore All</Button>
<Button Name="ShowTopmostWindow">Show Topmost Window</Button>
</StackPanel>
<StackPanel Grid.Column="2">
<Button Name="ShowTransparentWindow">Transparent Window</Button>
Expand Down
11 changes: 11 additions & 0 deletions samples/IntegrationTestApp/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,15 @@ private void RestoreAll()
window.WindowState = WindowState.Normal;
}
}

private void ShowTopmostWindow()
{
var mainWindow = new TopmostWindowTest("OwnerWindow") { Topmost = true, Title = "Owner Window"};
var ownedWindow = new TopmostWindowTest("OwnedWindow") { WindowStartupLocation = WindowStartupLocation.CenterOwner, Title = "Owned Window"};
mainWindow.Show();

ownedWindow.Show(mainWindow);
}

private void InitializeGesturesTab()
{
Expand Down Expand Up @@ -284,6 +293,8 @@ private void OnButtonClick(object? sender, RoutedEventArgs e)
WindowState = WindowState.Normal;
if (source?.Name == "RestoreAll")
RestoreAll();
if (source?.Name == "ShowTopmostWindow")
ShowTopmostWindow();
}
}
}
17 changes: 17 additions & 0 deletions samples/IntegrationTestApp/TopmostWindowTest.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="IntegrationTestApp.TopmostWindowTest"
Title="TopmostWindowTest"
Width="640"
Height="480">
<Grid>
<TextBox Name="CurrentPosition"
Grid.Column="1"
Grid.Row="3"
IsReadOnly="True" />
<Button HorizontalAlignment="Center" Name="MoveButton" VerticalAlignment="Center" Click="Button_OnClick">Move</Button>
</Grid>
</Window>
25 changes: 25 additions & 0 deletions samples/IntegrationTestApp/TopmostWindowTest.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;

namespace IntegrationTestApp;

public class TopmostWindowTest : Window
{
public TopmostWindowTest(string name)
{
Name = name;
InitializeComponent();
PositionChanged += (s, e) => this.GetControl<TextBox>("CurrentPosition").Text = $"{Position}";
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}

private void Button_OnClick(object? sender, RoutedEventArgs e)
{
Position += new PixelPoint(100, 100);
}
}
15 changes: 15 additions & 0 deletions src/Windows/Avalonia.Win32/WindowImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,10 @@ public void SetParent(IWindowImpl? parent)
_hiddenWindowIsParent = parentHwnd == OffscreenParentWindow.Handle;

SetWindowLongPtr(_hwnd, (int)WindowLongParam.GWL_HWNDPARENT, parentHwnd);

// Windows doesn't seem to respect the HWND_TOPMOST flag of a window when showing an owned window for the first time.
// So we set the HWND_TOPMOST again before the owned window is shown. This only needs to be done once.
(parent as WindowImpl)?.EnsureTopmost();
}

public void SetEnabled(bool enable) => EnableWindow(_hwnd, enable);
Expand Down Expand Up @@ -860,6 +864,17 @@ public void SetTopmost(bool value)
_topmost = value;
}

private void EnsureTopmost()
{
if(_topmost)
{
SetWindowPos(_hwnd,
WindowPosZOrder.HWND_TOPMOST,
0, 0, 0, 0,
SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE);
}
}

public unsafe void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
{
_currentThemeVariant = themeVariant;
Expand Down
29 changes: 29 additions & 0 deletions tests/Avalonia.IntegrationTests.Appium/WindowTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,35 @@ public void TransparentPopup()
Assert.Equal(new Rgba32(255, 0, 0), centerColor);
}

[PlatformFact(TestPlatforms.Windows)]
public void Owned_Window_Should_Appear_Above_Topmost_Owner()
{
var showTopmostWindow = _session.FindElementByAccessibilityId("ShowTopmostWindow");
using var window = showTopmostWindow.OpenWindowWithClick();
Thread.Sleep(1000);
var ownerWindow = GetWindow("OwnerWindow");
var ownedWindow = GetWindow("OwnedWindow");

Assert.NotNull(ownerWindow);
Assert.NotNull(ownedWindow);

var ownerPosition = GetPosition(ownerWindow);
var ownedPosition = GetPosition(ownedWindow);

// Owned Window moves
var moveButton = ownedWindow.FindElementByAccessibilityId("MoveButton");
moveButton.Click();
Thread.Sleep(1000);

Assert.Equal(GetPosition(ownerWindow), ownerPosition);
Assert.NotEqual(GetPosition(ownedWindow), ownedPosition);

PixelPoint GetPosition(AppiumWebElement window)
{
return PixelPoint.Parse(window.FindElementByAccessibilityId("CurrentPosition").Text);
}
}

[Theory]
[InlineData(ShowWindowMode.NonOwned, true)]
[InlineData(ShowWindowMode.Owned, true)]
Expand Down

0 comments on commit fc26fb6

Please sign in to comment.