Skip to content

Commit b3f92fa

Browse files
committed
Check the snap layouts with a real hover (#70)
tools/gui/core_window_snap_layout_test.ps1 rests the cursor on an app-drawn maximize button (hover only): Windows 11 opens the snap layouts there, the content still gets the moves and the leave, and the flyout closes again.
1 parent f56add5 commit b3f92fa

3 files changed

Lines changed: 103 additions & 1 deletion

File tree

‎core‎

‎tools/gui/README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ runner) lives in [`.agents/skills/`](../../.agents/skills); read
1717
| `core_window_content_protection_test.ps1` | core content protection regression | Windows | No-input test: native display affinity, shared wrappers, C ABI and actual capture exclusion/restoration of a pixel inside the test windows. Override the executable with `CORE_CONTENT_PROTECTION_TEST_EXE`. |
1818
| `core_window_title_bar_double_click_macos.py` | core title-bar double-click regression | macOS | Guarded real double clicks on bare hidden title bars: zoom/restore, Minimize, None and native Fill. Preferences are overridden only in the fixture process. Build `window_title_bar_double_click_gui_macos_test` in `core/build` first. |
1919
| `core_window_system_menu_test.ps1` | core system menu regression | Windows | Native popup states and guarded selections of custom, maximize, restore, minimize and close commands; content coordinates scale once. Override the executable with `CORE_SYSTEM_MENU_TEST_EXE`. |
20+
| `core_window_snap_layout_test.ps1` | core `window_maximize_button_test --serve` (#70) | Windows 11 | Real hover, no clicks: a hidden-title-bar window marks its own maximize button with `SetMaximizeButtonBounds`; resting the cursor there opens the snap layouts (another process's window appears below the button), the content window still receives the moves inside the button, and leaving closes the flyout and reaches the content as `WM_MOUSELEAVE`. Saves a screenshot. Override the executable with `CORE_SNAP_LAYOUT_TEST_EXE`. |
2021
| `core_window_shadow_layout_test_linux.cpp` | core shadow | Linux Wayland / X11 | No-input regression: stable shadow geometry, contour pixels inside transparent content, input pass-through, disable and decoration restoration |
2122
| `flutter_window_shape_smoke_macos.py` / `flutter_window_shape_test_windows.ps1` | `shaped_window_example` | macOS / Windows | macOS: no-input shape and pixel-alpha smoke test; Windows: native regions, real button clicks/drag, resize, clear/reapply, click-through to the underlying app window |
2223
| `flutter_detachable_window_test.py` / `.ps1` | `detachable_window_example` | macOS / Windows | tear a panel off, exact content size, header stays under the cursor, dock into the other window, `State` preserved |
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Real-pointer check of Window::SetMaximizeButtonBounds (#70) on Windows 11:
2+
# resting the cursor on an app-drawn maximize button opens the snap layouts,
3+
# and the content window under it still receives the moves. Hover only - no
4+
# clicks, no keyboard. Build core/tests/window_maximize_button_test first.
5+
$ErrorActionPreference = 'Stop'
6+
. "$PSScriptRoot\env.ps1"
7+
. "$PSScriptRoot\winput.ps1"
8+
. "$PSScriptRoot\guiapp.ps1"
9+
Add-Type -AssemblyName System.Drawing
10+
Add-Type -AssemblyName System.Windows.Forms
11+
12+
$exe = $env:CORE_SNAP_LAYOUT_TEST_EXE
13+
if (-not $exe) { $exe = "$RemoteScratch\core-build\tests\Debug\window_maximize_button_test.exe" }
14+
if (-not (Test-Path $exe)) { throw "Missing executable: $exe" }
15+
Start-Result "$RemoteScratch\core_window_snap_layout_test.result.txt"
16+
$build = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion').CurrentBuild
17+
$flyout = (Get-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced' -ErrorAction SilentlyContinue).EnableSnapAssistFlyout
18+
Say "Windows build $build, EnableSnapAssistFlyout=$(if ($null -eq $flyout) { 'default (on)' } else { $flyout })"
19+
Assert-Idle
20+
21+
$info = New-Object System.Diagnostics.ProcessStartInfo $exe
22+
$info.Arguments = '--serve'
23+
$info.UseShellExecute = $false
24+
$info.CreateNoWindow = $true
25+
$info.RedirectStandardOutput = $true
26+
$proc = [System.Diagnostics.Process]::Start($info)
27+
$lines = New-Object System.Collections.Generic.List[string]
28+
$script:read = $proc.StandardOutput.ReadLineAsync()
29+
function Read-AppLines {
30+
while ($script:read.IsCompleted) {
31+
$line = $script:read.Result
32+
if ($null -eq $line) { break }
33+
$lines.Add($line)
34+
$script:read = $proc.StandardOutput.ReadLineAsync()
35+
}
36+
}
37+
# Waits while draining the app's output: a full pipe would block its message loop.
38+
function Wait-Reading([double]$Seconds) {
39+
$end = (Get-Date).AddSeconds($Seconds)
40+
do { Read-AppLines; Start-Sleep -Milliseconds 20 } while ((Get-Date) -lt $end)
41+
Read-AppLines
42+
}
43+
$app = @{ Proc = $proc; Log = $null; Stdout = $null }
44+
$rest = $null
45+
try {
46+
$deadline = (Get-Date).AddSeconds(20)
47+
do { Pause 0.1; Read-AppLines } until (@($lines | ? { $_ -like 'READY *' -or $_ -like 'FAIL*' }).Count -gt 0 -or (Get-Date) -gt $deadline)
48+
$ready = $lines | ? { $_ -like 'READY *' } | Select-Object -First 1
49+
if (-not $ready) { throw "The app did not get ready: $($lines -join '; ')" }
50+
$b = $ready.Split(' ')[1..4] | % { [int]$_ }
51+
$center = @(($b[0] + [int]($b[2] / 2)), ($b[1] + [int]($b[3] / 2)))
52+
$win = Get-Win $app 'nativeapi snap layout'
53+
if (-not $win) { throw 'The window is missing' }
54+
Raise-AppWindows $proc.Id
55+
# Approach over the window's own content, below the title band.
56+
$approach = @(($win.Left + 120), ($win.Top + 200))
57+
# Leave sideways along the title band, away from the flyout below the button.
58+
$rest = $null
59+
Assert-Owner $proc.Id $approach[0] $approach[1]
60+
Move-Cursor $approach 400
61+
Wait-Reading 0.5
62+
# The flyout opens below the button, over this window. The window under
63+
# that point tells: ours before, the shell's while the flyout is up.
64+
$probe = @($center[0], ($center[1] + [int]($b[3] * 2)))
65+
$ownerBefore = [WInput]::Owner($probe[0], $probe[1])
66+
Assert-Owner $proc.Id $center[0] $center[1]
67+
$countBefore = $lines.Count
68+
Move-Cursor $center 500
69+
Wait-Reading 2.0 # The flyout opens after a short rest.
70+
$ownerDuring = [WInput]::Owner($probe[0], $probe[1])
71+
$process = (Get-Process -Id $ownerDuring -ErrorAction SilentlyContinue).ProcessName
72+
# Evidence: the screen around the button while the flyout is up.
73+
$bmp = New-Object System.Drawing.Bitmap 900, 500
74+
$g = [System.Drawing.Graphics]::FromImage($bmp)
75+
$g.CopyFromScreen([Math]::Max(0, $center[0] - 600), [Math]::Max(0, $center[1] - 60), 0, 0, $bmp.Size)
76+
$bmp.Save("$RemoteScratch\core_window_snap_layout_test.png", [System.Drawing.Imaging.ImageFormat]::Png)
77+
$g.Dispose(); $bmp.Dispose()
78+
Check 'below the button is this window before the hover' ($ownerBefore -eq $proc.Id) "pid $ownerBefore"
79+
Check 'resting on the button opens a window of another process over it (the snap layouts)' `
80+
($ownerDuring -ne $proc.Id -and $ownerDuring -ne 0) "pid $ownerDuring ($process)"
81+
# Natively the content gets no move over the button (it steps aside in the
82+
# hit test), so a move it logs inside the button was forwarded.
83+
$c = ($lines | ? { $_ -like 'BUTTON_CONTENT *' } | Select-Object -First 1).Split(' ')[1..4] | % { [int]$_ }
84+
$moves = @($lines | Select-Object -Skip $countBefore | ? { $_ -like 'CONTENT WM_MOUSEMOVE*' } | ? {
85+
$f = $_.Split(' '); $x = [int]$f[2]; $y = [int]$f[3]
86+
$x -ge $c[0] -and $x -lt $c[0] + $c[2] -and $y -ge $c[1] -and $y -lt $c[1] + $c[3]
87+
})
88+
Check 'the content window received the moves over the button' ($moves.Count -gt 0) "$($moves.Count) moves, last: $($moves | Select-Object -Last 1)"
89+
# Leave the button: the flyout closes and the content hears it.
90+
$rest = @(($center[0] - [int]($b[2] * 5)), $center[1])
91+
Assert-Owner $proc.Id $rest[0] $rest[1]
92+
Move-Cursor $rest 400
93+
Wait-Reading 1.5
94+
$ownerAfter = [WInput]::Owner($probe[0], $probe[1])
95+
Check 'the snap layouts closed after leaving' ($ownerAfter -eq $proc.Id) "pid $ownerAfter"
96+
Check 'the content window heard the pointer leave the button' (@($lines | ? { $_ -like 'CONTENT WM_MOUSELEAVE*' }).Count -gt 0)
97+
Check 'no click was sent' (@($lines | ? { $_ -like 'CONTENT WM_LBUTTON*' }).Count -eq 0)
98+
} finally {
99+
if (-not $proc.HasExited) { Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue }
100+
$lines | Set-Content "$RemoteScratch\core_window_snap_layout_test.app.log" -Encoding utf8
101+
}

0 commit comments

Comments
 (0)