Skip to content

Commit 7659f2b

Browse files
committed
Fix NavigationView mouse click focus and ScrollablePanel scroll-mode-first UX
NavigationView.ProcessMouseEvent now detects which side was clicked and explicitly calls FocusNavPane/FocusContentPanel before delegating to the grid. This fixes click-to-focus failures caused by ColumnContainer breaking the focus notification chain and the nav ScrollablePanel's CanReceiveFocus returning false when it has no focusable children and doesn't need scrolling. ScrollablePanel.SetFocus now enters scroll mode (arrow keys scroll, Tab focuses first child) when the viewport has been laid out and content overflows. Non-scrollable panels still delegate to the first focusable child immediately.
1 parent a9399ea commit 7659f2b

2 files changed

Lines changed: 31 additions & 11 deletions

File tree

SharpConsoleUI/Controls/NavigationView/NavigationView.Input.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ public partial class NavigationView
4444
/// <inheritdoc/>
4545
public bool ProcessMouseEvent(MouseEventArgs args)
4646
{
47+
// Determine which side was clicked and set focus accordingly.
48+
// The grid's own focus tracking can't reliably handle this because
49+
// ColumnContainer breaks the focus notification chain (see da664aa).
50+
bool clickedNavSide = args.Position.X < _navColumn.ActualWidth;
51+
if (clickedNavSide)
52+
FocusNavPane();
53+
else
54+
FocusContentPanel();
55+
_hasFocus = true;
56+
4757
// Delegate to the grid — it handles dispatching to columns and their children
4858
return _grid.ProcessMouseEvent(args);
4959
}

SharpConsoleUI/Controls/ScrollablePanelControl/ScrollablePanelControl.Input.cs

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -276,19 +276,29 @@ public void SetFocus(bool focus, FocusReason reason = FocusReason.Programmatic)
276276

277277
if (focusableChildren.Any())
278278
{
279-
// Focus first or last child based on direction
280-
_focusedChild = _focusFromBackward
281-
? focusableChildren.Last() as IInteractiveControl
282-
: focusableChildren.First() as IInteractiveControl;
279+
// If the viewport has been laid out and content overflows, enter scroll
280+
// mode first so the user can browse with arrow keys before pressing Tab
281+
// to focus the first child.
282+
if (_viewportHeight > 0 && NeedsScrolling())
283+
{
284+
log?.LogTrace("ScrollPanel.SetFocus: needs scrolling, entering scroll mode (focusable children available via Tab)", "Focus");
285+
}
286+
else
287+
{
288+
// Content fits in viewport (or layout not yet computed) — delegate focus to child immediately
289+
_focusedChild = _focusFromBackward
290+
? focusableChildren.Last() as IInteractiveControl
291+
: focusableChildren.First() as IInteractiveControl;
283292

284-
log?.LogTrace($"ScrollPanel.SetFocus: delegating to child {_focusedChild?.GetType().Name} (backward={_focusFromBackward})", "Focus");
293+
log?.LogTrace($"ScrollPanel.SetFocus: delegating to child {_focusedChild?.GetType().Name} (backward={_focusFromBackward})", "Focus");
285294

286-
if (_focusedChild is IFocusableControl fc)
287-
{
288-
if (_focusedChild is IDirectionalFocusControl dfc)
289-
dfc.SetFocusWithDirection(true, _focusFromBackward);
290-
else
291-
fc.SetFocus(true, reason);
295+
if (_focusedChild is IFocusableControl fc)
296+
{
297+
if (_focusedChild is IDirectionalFocusControl dfc)
298+
dfc.SetFocusWithDirection(true, _focusFromBackward);
299+
else
300+
fc.SetFocus(true, reason);
301+
}
292302
}
293303
}
294304
else

0 commit comments

Comments
 (0)