Browse Source

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
pull/16153/head
Emmanuel Hansen 2 years ago
committed by GitHub
parent
commit
fc26fb6608
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 6
      samples/IntegrationTestApp/IntegrationTestApp.csproj
  2. 1
      samples/IntegrationTestApp/MainWindow.axaml
  3. 11
      samples/IntegrationTestApp/MainWindow.axaml.cs
  4. 17
      samples/IntegrationTestApp/TopmostWindowTest.axaml
  5. 25
      samples/IntegrationTestApp/TopmostWindowTest.axaml.cs
  6. 15
      src/Windows/Avalonia.Win32/WindowImpl.cs
  7. 29
      tests/Avalonia.IntegrationTests.Appium/WindowTests.cs

6
samples/IntegrationTestApp/IntegrationTestApp.csproj

@ -26,6 +26,12 @@
<ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" /> <ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj" /> <ProjectReference Include="..\..\src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Compile Update="TopmostWindowTest.axaml.cs">
<DependentUpon>TopmostWindowTest.axaml</DependentUpon>
</Compile>
</ItemGroup>
<Import Project="..\..\build\BuildTargets.targets" /> <Import Project="..\..\build\BuildTargets.targets" />
<Import Project="..\..\build\SampleApp.props" /> <Import Project="..\..\build\SampleApp.props" />

1
samples/IntegrationTestApp/MainWindow.axaml

@ -170,6 +170,7 @@
<Button Name="EnterFullscreen">Enter Fullscreen</Button> <Button Name="EnterFullscreen">Enter Fullscreen</Button>
<Button Name="ExitFullscreen">Exit Fullscreen</Button> <Button Name="ExitFullscreen">Exit Fullscreen</Button>
<Button Name="RestoreAll">Restore All</Button> <Button Name="RestoreAll">Restore All</Button>
<Button Name="ShowTopmostWindow">Show Topmost Window</Button>
</StackPanel> </StackPanel>
<StackPanel Grid.Column="2"> <StackPanel Grid.Column="2">
<Button Name="ShowTransparentWindow">Transparent Window</Button> <Button Name="ShowTransparentWindow">Transparent Window</Button>

11
samples/IntegrationTestApp/MainWindow.axaml.cs

@ -217,6 +217,15 @@ namespace IntegrationTestApp
window.WindowState = WindowState.Normal; 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() private void InitializeGesturesTab()
{ {
@ -284,6 +293,8 @@ namespace IntegrationTestApp
WindowState = WindowState.Normal; WindowState = WindowState.Normal;
if (source?.Name == "RestoreAll") if (source?.Name == "RestoreAll")
RestoreAll(); RestoreAll();
if (source?.Name == "ShowTopmostWindow")
ShowTopmostWindow();
} }
} }
} }

17
samples/IntegrationTestApp/TopmostWindowTest.axaml

@ -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
samples/IntegrationTestApp/TopmostWindowTest.axaml.cs

@ -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
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -708,6 +708,10 @@ namespace Avalonia.Win32
_hiddenWindowIsParent = parentHwnd == OffscreenParentWindow.Handle; _hiddenWindowIsParent = parentHwnd == OffscreenParentWindow.Handle;
SetWindowLongPtr(_hwnd, (int)WindowLongParam.GWL_HWNDPARENT, parentHwnd); 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); public void SetEnabled(bool enable) => EnableWindow(_hwnd, enable);
@ -860,6 +864,17 @@ namespace Avalonia.Win32
_topmost = 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) public unsafe void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
{ {
_currentThemeVariant = themeVariant; _currentThemeVariant = themeVariant;

29
tests/Avalonia.IntegrationTests.Appium/WindowTests.cs

@ -257,6 +257,35 @@ namespace Avalonia.IntegrationTests.Appium
Assert.Equal(new Rgba32(255, 0, 0), centerColor); 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] [Theory]
[InlineData(ShowWindowMode.NonOwned, true)] [InlineData(ShowWindowMode.NonOwned, true)]
[InlineData(ShowWindowMode.Owned, true)] [InlineData(ShowWindowMode.Owned, true)]

Loading…
Cancel
Save