From 1bc8ebb8bd7cf6872cac51cd6b8af9a022cf1108 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 30 Aug 2026 08:44:02 +0200 Subject: [PATCH] Re-resolve the right-click target while waiting for its hit test Right_Clicking_A_Second_Row_Moves_The_Context_Highlight_To_It still timed out on the Windows Debug CI job, now in the hit-test wait added for the second right-click: for the full 60s no hit at the precomputed point matched the captured row container. A light-dismiss overlay that survives one frame cannot explain that many rendered frames; a container that is no longer the one on screen can. The test only waits for three assemblies, so the rest of the list keeps loading on the slow agent while the test runs, and every insertion reshuffles the rows - re-realising containers and moving them - after the row and point were captured. The wait now resolves the row container and the click point on every poll and matches the hit by node instead of by container identity, and a timeout reports the point and what was hit instead so a further failure is diagnosable from the log. Assisted-by: Claude:claude-fable-5:Claude Code --- .../ContextMenus/DecompileInNewViewTests.cs | 45 ++++++++++++++----- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/ILSpy.Tests/ContextMenus/DecompileInNewViewTests.cs b/ILSpy.Tests/ContextMenus/DecompileInNewViewTests.cs index 562c3f1f8..1072bb5a9 100644 --- a/ILSpy.Tests/ContextMenus/DecompileInNewViewTests.cs +++ b/ILSpy.Tests/ContextMenus/DecompileInNewViewTests.cs @@ -252,20 +252,41 @@ public class DecompileInNewViewTests var menu = grid.ContextMenu!; - async Task RightClick(SharpTreeNode node) + Point? ClickPoint(SharpTreeNode node) { - var row = Row(node); + var row = grid.GetVisualDescendants() + .OfType() + .FirstOrDefault(r => RowNodeEquals(r, node)); + if (row == null) + return null; var clickX = System.Math.Min(row.Bounds.Width, grid.Bounds.Width) / 2; - var pt = row.TranslatePoint(new Point(clickX, row.Bounds.Height / 2), window); - // A popup's light-dismiss overlay keeps answering hit tests for as long as the frame - // that still shows it, and it swallows a press without raising ContextRequested - so - // nothing becomes the context target. Hit testing the point is the same question the - // context-request handler asks, so waiting for it to reach the row is exactly the - // precondition for this click, however many frames the overlay takes to disappear. - await Waiters.WaitForAsync( - () => window.InputHitTest(pt!.Value) is Visual hit - && ReferenceEquals(hit.FindAncestorOfType(includeSelf: true), row), - description: "the row to answer hit tests at the point about to be right-clicked"); + return row.TranslatePoint(new Point(clickX, row.Bounds.Height / 2), window); + } + + async Task RightClick(SharpTreeNode node) + { + // A press only becomes a context request for the row when the hit test at the press + // point answers with that row - the same question OnTreeContextRequested asks - and it + // takes an unknown number of frames for that to hold: a closed popup's light-dismiss + // overlay keeps answering hit tests until the scene is rendered again, and assemblies + // still loading in the background reshuffle the rows, which re-realises containers and + // shifts them. So the row container and the point are resolved afresh on every poll and + // the hit is matched by node, not by container identity. + Point? pt = null; + Visual? lastHit = null; + try + { + await Waiters.WaitForAsync( + () => (pt = ClickPoint(node)) is { } p + && (lastHit = window.InputHitTest(p) as Visual) is { } hit + && hit.FindAncestorOfType(includeSelf: true) is { } hitRow + && RowNodeEquals(hitRow, node), + description: "the row to answer hit tests at the point about to be right-clicked"); + } + catch (System.TimeoutException ex) + { + throw new System.TimeoutException($"{ex.Message} (point: {pt?.ToString() ?? "row not realised"}, hit: {lastHit?.GetType().Name ?? "nothing"})", ex); + } HeadlessWindowExtensions.MouseDown(window, pt!.Value, MouseButton.Right); HeadlessWindowExtensions.MouseUp(window, pt.Value, MouseButton.Right); // The highlight is scoped to the popup - set while the menu is being requested, dropped