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
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with
104 additions and
0 deletions
samples/IntegrationTestApp/IntegrationTestApp.csproj
samples/IntegrationTestApp/MainWindow.axaml
samples/IntegrationTestApp/MainWindow.axaml.cs
samples/IntegrationTestApp/TopmostWindowTest.axaml
samples/IntegrationTestApp/TopmostWindowTest.axaml.cs
src/Windows/Avalonia.Win32/WindowImpl.cs
tests/Avalonia.IntegrationTests.Appium/WindowTests.cs
@ -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" />
@ -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>
@ -217,6 +217,15 @@ namespace IntegrationTestApp
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 ( )
{
@ -284,6 +293,8 @@ namespace IntegrationTestApp
WindowState = WindowState . Normal ;
if ( source ? . Name = = "RestoreAll" )
RestoreAll ( ) ;
if ( source ? . Name = = "ShowTopmostWindow" )
ShowTopmostWindow ( ) ;
}
}
}
@ -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>
@ -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 ( 1 0 0 , 1 0 0 ) ;
}
}
@ -708,6 +708,10 @@ namespace Avalonia.Win32
_ 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 ) ;
@ -860,6 +864,17 @@ namespace Avalonia.Win32
_ 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 ;
@ -257,6 +257,35 @@ namespace Avalonia.IntegrationTests.Appium
Assert . Equal ( new Rgba32 ( 2 5 5 , 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 ( 1 0 0 0 ) ;
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 ( 1 0 0 0 ) ;
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)]