mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2026-02-10 04:03:41 +01:00
Use CachedViewsUnderMouse in HandleAutoGrabPress; skip disabled SubViews
Replace the redundant GetViewsUnderLocation call with a read from the already-populated CachedViewsUnderMouse. Walk the cache backwards to find the deepest *enabled* view, so disabled SubViews don't block the SuperView from grabbing the mouse. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -572,21 +572,26 @@ public partial class View // Mouse APIs
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't grab if a SubView at the mouse position can handle the event
|
||||
// This ensures that SubViews receive their own mouse events even when the SuperView has MouseHighlightStates set
|
||||
if (mouse.Position is { } pos && Viewport.Contains (pos))
|
||||
// Don't grab if an enabled SubView at the mouse position can handle the event.
|
||||
// CachedViewsUnderMouse is already updated by RaiseMouseEnterLeaveEvents before NewMouseEvent runs.
|
||||
// Disabled views are included in the cache, so we must find the deepest enabled view.
|
||||
if (App?.Mouse.CachedViewsUnderMouse is { Count: > 0 } cached)
|
||||
{
|
||||
// Convert viewport-relative position to screen coordinates
|
||||
Point screenPos = ViewportToScreen (pos);
|
||||
View? deepestEnabledView = null;
|
||||
|
||||
// Get all views under this screen position - the deepest view is at the end of the list
|
||||
List<View?> viewsUnderMouse = GetViewsUnderLocation (screenPos, ViewportSettingsFlags.TransparentMouse);
|
||||
View? deepestView = viewsUnderMouse.LastOrDefault ();
|
||||
|
||||
// If the deepest view is a SubView of this view (not this view itself), don't grab
|
||||
if (deepestView is { } && deepestView != this)
|
||||
for (int i = cached.Count - 1; i >= 0; i--)
|
||||
{
|
||||
// A SubView is under the cursor - let it handle its own events
|
||||
if (cached [i] is { Enabled: true } candidate)
|
||||
{
|
||||
deepestEnabledView = candidate;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (deepestEnabledView is { } && deepestEnabledView != this)
|
||||
{
|
||||
// An enabled SubView is under the cursor - let it handle its own events
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,6 +225,68 @@ public class MouseHighlightStatesSubViewTests
|
||||
runnable.Dispose ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that when a SubView is disabled, clicking on it routes the event to the SuperView.
|
||||
/// A disabled SubView should not prevent the SuperView from grabbing the mouse.
|
||||
/// </summary>
|
||||
|
||||
// Claude - Opus 4.6
|
||||
[Theory]
|
||||
[InlineData (MouseState.In)]
|
||||
[InlineData (MouseState.Pressed)]
|
||||
[InlineData (MouseState.In | MouseState.Pressed)]
|
||||
public void MouseHighlightStates_DisabledSubView_DoesNotPreventSuperViewGrab (MouseState highlightState)
|
||||
{
|
||||
// Arrange
|
||||
VirtualTimeProvider time = new ();
|
||||
using IApplication app = Application.Create (time);
|
||||
app.Init (DriverRegistry.Names.ANSI);
|
||||
|
||||
var superViewActivateCount = 0;
|
||||
var subViewActivateCount = 0;
|
||||
|
||||
Runnable runnable = new ();
|
||||
|
||||
View superView = new ()
|
||||
{
|
||||
Id = "superView",
|
||||
X = 0,
|
||||
Y = 0,
|
||||
Width = 10,
|
||||
Height = 10,
|
||||
MouseHighlightStates = highlightState
|
||||
};
|
||||
|
||||
superView.Activating += (_, _) => { superViewActivateCount++; };
|
||||
|
||||
View subView = new ()
|
||||
{
|
||||
Id = "disabledSubView",
|
||||
X = 2,
|
||||
Y = 2,
|
||||
Width = 5,
|
||||
Height = 5,
|
||||
CanFocus = true,
|
||||
Enabled = false
|
||||
};
|
||||
|
||||
subView.Activating += (_, _) => { subViewActivateCount++; };
|
||||
|
||||
superView.Add (subView);
|
||||
runnable.Add (superView);
|
||||
app.Begin (runnable);
|
||||
|
||||
// Act: Click on the disabled SubView at screen position (3, 3)
|
||||
app.InjectMouse (new Mouse { ScreenPosition = new Point (3, 3), Flags = MouseFlags.LeftButtonPressed });
|
||||
app.InjectMouse (new Mouse { ScreenPosition = new Point (3, 3), Flags = MouseFlags.LeftButtonReleased });
|
||||
|
||||
// Assert: SuperView should receive the event since SubView is disabled
|
||||
Assert.Equal (1, superViewActivateCount);
|
||||
Assert.Equal (0, subViewActivateCount);
|
||||
|
||||
runnable.Dispose ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that nested views (SuperView with MouseHighlightStates, SubView with MouseHighlightStates)
|
||||
/// route events to the deepest view under the mouse.
|
||||
|
||||
Reference in New Issue
Block a user