Skip to content

Commit fc26fb6

Browse files
authored
Win32 - Ensure owner topmost flag is set if its topmost when showing 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
1 parent 24914cc commit fc26fb6

File tree

7 files changed

+104
-0
lines changed

7 files changed

+104
-0
lines changed

samples/IntegrationTestApp/IntegrationTestApp.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@
2626
<ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
2727
<ProjectReference Include="..\..\src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj" />
2828
</ItemGroup>
29+
30+
<ItemGroup>
31+
<Compile Update="TopmostWindowTest.axaml.cs">
32+
<DependentUpon>TopmostWindowTest.axaml</DependentUpon>
33+
</Compile>
34+
</ItemGroup>
2935

3036
<Import Project="..\..\build\BuildTargets.targets" />
3137
<Import Project="..\..\build\SampleApp.props" />

samples/IntegrationTestApp/MainWindow.axaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@
170170
<Button Name="EnterFullscreen">Enter Fullscreen</Button>
171171
<Button Name="ExitFullscreen">Exit Fullscreen</Button>
172172
<Button Name="RestoreAll">Restore All</Button>
173+
<Button Name="ShowTopmostWindow">Show Topmost Window</Button>
173174
</StackPanel>
174175
<StackPanel Grid.Column="2">
175176
<Button Name="ShowTransparentWindow">Transparent Window</Button>

samples/IntegrationTestApp/MainWindow.axaml.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,15 @@ private void RestoreAll()
217217
window.WindowState = WindowState.Normal;
218218
}
219219
}
220+
221+
private void ShowTopmostWindow()
222+
{
223+
var mainWindow = new TopmostWindowTest("OwnerWindow") { Topmost = true, Title = "Owner Window"};
224+
var ownedWindow = new TopmostWindowTest("OwnedWindow") { WindowStartupLocation = WindowStartupLocation.CenterOwner, Title = "Owned Window"};
225+
mainWindow.Show();
226+
227+
ownedWindow.Show(mainWindow);
228+
}
220229

221230
private void InitializeGesturesTab()
222231
{
@@ -284,6 +293,8 @@ private void OnButtonClick(object? sender, RoutedEventArgs e)
284293
WindowState = WindowState.Normal;
285294
if (source?.Name == "RestoreAll")
286295
RestoreAll();
296+
if (source?.Name == "ShowTopmostWindow")
297+
ShowTopmostWindow();
287298
}
288299
}
289300
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Window xmlns="https://github.com/avaloniaui"
2+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
4+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5+
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
6+
x:Class="IntegrationTestApp.TopmostWindowTest"
7+
Title="TopmostWindowTest"
8+
Width="640"
9+
Height="480">
10+
<Grid>
11+
<TextBox Name="CurrentPosition"
12+
Grid.Column="1"
13+
Grid.Row="3"
14+
IsReadOnly="True" />
15+
<Button HorizontalAlignment="Center" Name="MoveButton" VerticalAlignment="Center" Click="Button_OnClick">Move</Button>
16+
</Grid>
17+
</Window>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using Avalonia;
2+
using Avalonia.Controls;
3+
using Avalonia.Interactivity;
4+
using Avalonia.Markup.Xaml;
5+
6+
namespace IntegrationTestApp;
7+
8+
public class TopmostWindowTest : Window
9+
{
10+
public TopmostWindowTest(string name)
11+
{
12+
Name = name;
13+
InitializeComponent();
14+
PositionChanged += (s, e) => this.GetControl<TextBox>("CurrentPosition").Text = $"{Position}";
15+
}
16+
private void InitializeComponent()
17+
{
18+
AvaloniaXamlLoader.Load(this);
19+
}
20+
21+
private void Button_OnClick(object? sender, RoutedEventArgs e)
22+
{
23+
Position += new PixelPoint(100, 100);
24+
}
25+
}

src/Windows/Avalonia.Win32/WindowImpl.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,10 @@ public void SetParent(IWindowImpl? parent)
708708
_hiddenWindowIsParent = parentHwnd == OffscreenParentWindow.Handle;
709709

710710
SetWindowLongPtr(_hwnd, (int)WindowLongParam.GWL_HWNDPARENT, parentHwnd);
711+
712+
// Windows doesn't seem to respect the HWND_TOPMOST flag of a window when showing an owned window for the first time.
713+
// So we set the HWND_TOPMOST again before the owned window is shown. This only needs to be done once.
714+
(parent as WindowImpl)?.EnsureTopmost();
711715
}
712716

713717
public void SetEnabled(bool enable) => EnableWindow(_hwnd, enable);
@@ -860,6 +864,17 @@ public void SetTopmost(bool value)
860864
_topmost = value;
861865
}
862866

867+
private void EnsureTopmost()
868+
{
869+
if(_topmost)
870+
{
871+
SetWindowPos(_hwnd,
872+
WindowPosZOrder.HWND_TOPMOST,
873+
0, 0, 0, 0,
874+
SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE);
875+
}
876+
}
877+
863878
public unsafe void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
864879
{
865880
_currentThemeVariant = themeVariant;

tests/Avalonia.IntegrationTests.Appium/WindowTests.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,35 @@ public void TransparentPopup()
257257
Assert.Equal(new Rgba32(255, 0, 0), centerColor);
258258
}
259259

260+
[PlatformFact(TestPlatforms.Windows)]
261+
public void Owned_Window_Should_Appear_Above_Topmost_Owner()
262+
{
263+
var showTopmostWindow = _session.FindElementByAccessibilityId("ShowTopmostWindow");
264+
using var window = showTopmostWindow.OpenWindowWithClick();
265+
Thread.Sleep(1000);
266+
var ownerWindow = GetWindow("OwnerWindow");
267+
var ownedWindow = GetWindow("OwnedWindow");
268+
269+
Assert.NotNull(ownerWindow);
270+
Assert.NotNull(ownedWindow);
271+
272+
var ownerPosition = GetPosition(ownerWindow);
273+
var ownedPosition = GetPosition(ownedWindow);
274+
275+
// Owned Window moves
276+
var moveButton = ownedWindow.FindElementByAccessibilityId("MoveButton");
277+
moveButton.Click();
278+
Thread.Sleep(1000);
279+
280+
Assert.Equal(GetPosition(ownerWindow), ownerPosition);
281+
Assert.NotEqual(GetPosition(ownedWindow), ownedPosition);
282+
283+
PixelPoint GetPosition(AppiumWebElement window)
284+
{
285+
return PixelPoint.Parse(window.FindElementByAccessibilityId("CurrentPosition").Text);
286+
}
287+
}
288+
260289
[Theory]
261290
[InlineData(ShowWindowMode.NonOwned, true)]
262291
[InlineData(ShowWindowMode.Owned, true)]

0 commit comments

Comments
 (0)