Skip to content

Commit ce07864

Browse files
committed
Fix focus routing for mouse clicks and ClearContents in ScrollablePanel
- HandleClickFocus: use deep leaf (via _lastDeepFocusedControl) as FocusService target instead of the container, matching SwitchFocus behaviour. Previously, SetFocus(container) cleared the leaf child's HasFocus, causing it to ignore key events after a mouse click. - ScrollablePanelControl.ClearContents: remove the SetFocus(false) call that fired when _hasFocus && !CanReceiveFocus (after children cleared). The panel now retains _hasFocus=true so focus is restored automatically when new controls are added. - ScrollablePanelControl.AddControl: auto-focus the first focusable control added while the panel has focus but no focused child. Restores key routing after ClearContents without losing the panel's focus. - HasActiveInteractiveContent: add a second fallback that checks _lastDeepFocusedControl.HasFocus when the container's HasFocus was cleared by FocusService.SetFocus(leaf) in NotifyControlGainedFocus.
1 parent cb8c143 commit ce07864

3 files changed

Lines changed: 29 additions & 4 deletions

File tree

SharpConsoleUI/Controls/ScrollablePanelControl.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,18 @@ public void AddControl(IWindowControl control)
10001000
{
10011001
_children.Add(control);
10021002
control.Container = this;
1003+
// If the panel has focus but no focused child yet, focus the new control
1004+
// if it's focusable. Restores focus routing after ClearContents.
1005+
if (_hasFocus && _focusedChild == null &&
1006+
control.Visible &&
1007+
control is IFocusableControl fc && fc.CanReceiveFocus)
1008+
{
1009+
_focusedChild = control as IInteractiveControl;
1010+
if (control is IDirectionalFocusControl dfc)
1011+
dfc.SetFocusWithDirection(true, false);
1012+
else
1013+
fc.SetFocus(true, FocusReason.Programmatic);
1014+
}
10031015
Invalidate(true);
10041016
}
10051017

@@ -1052,9 +1064,6 @@ public void ClearContents()
10521064
}
10531065
_children.Clear();
10541066

1055-
if (_hasFocus && !CanReceiveFocus)
1056-
SetFocus(false, FocusReason.Programmatic);
1057-
10581067
Invalidate(true);
10591068
}
10601069

SharpConsoleUI/Window.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1813,6 +1813,7 @@ public void NotifyControlGainedFocus(IInteractiveControl control,
18131813
// is NOT in _interactiveContents, so isTopLevel would be false, but the leaf still needs tracking.
18141814
bool isIntermediateContainer = actualFocusedControl is Controls.IFocusTrackingContainer;
18151815
bool hasValidLeaf = _lastDeepFocusedControl is Controls.IFocusableControl leafFc && leafFc.HasFocus;
1816+
var prevDeep = _lastDeepFocusedControl;
18161817
if (!isIntermediateContainer)
18171818
{
18181819
// True leaf (ListControl, ButtonControl, etc.) — always prefer it

SharpConsoleUI/Windows/WindowEventDispatcher.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,13 @@ private void HandleClickFocus(IWindowControl? clickedControl)
351351
if (newFocusTarget is IInteractiveControl interactive)
352352
{
353353
_window._lastFocusedControl = interactive;
354-
_window.FocusService?.SetFocus(_window, interactive, FocusChangeReason.Mouse);
354+
// Prefer the deep leaf tracked by NCGF (same pattern as SwitchFocus).
355+
// SetFocus(container) would clear the leaf child's HasFocus, causing
356+
// it to ignore key events even though it's the actual focused control.
357+
var leafForFocus = (_window._lastDeepFocusedControl is Controls.IFocusableControl leafFc && leafFc.HasFocus)
358+
? _window._lastDeepFocusedControl
359+
: interactive;
360+
_window.FocusService?.SetFocus(_window, leafForFocus, FocusChangeReason.Mouse);
355361
}
356362
}
357363
else
@@ -625,6 +631,15 @@ public bool HasActiveInteractiveContent(out IInteractiveControl? interactiveCont
625631
interactiveContent = _window._lastFocusedControl;
626632
}
627633

634+
// Fallback: container's HasFocus was cleared by FocusService.SetFocus(leaf) in NCGF,
635+
// but the leaf itself still has focus. Use _lastFocusedControl as the routing target.
636+
if (interactiveContent == null &&
637+
_window._lastFocusedControl != null && _window._lastFocusedControl.IsEnabled &&
638+
_window._lastDeepFocusedControl is Controls.IFocusableControl deepFc && deepFc.HasFocus)
639+
{
640+
interactiveContent = _window._lastFocusedControl;
641+
}
642+
628643
return interactiveContent != null;
629644
}
630645

0 commit comments

Comments
 (0)