Skip to content

tabIndexDirection: directional focus navigation - #933

Merged
david-vanderson merged 15 commits into
mainfrom
tab_dir
Aug 11, 2026
Merged

tabIndexDirection: directional focus navigation#933
david-vanderson merged 15 commits into
mainfrom
tab_dir

Conversation

@david-vanderson

@david-vanderson david-vanderson commented Jul 16, 2026

Copy link
Copy Markdown
Owner

fixes #857

The strategy is (edited):

  • start from the center of the focused widget
  • look in the given direction (arbitrary angle)
  • find closest widget in that direction
    • distance in given direction weighted normally
    • distance in perpendicular direction weighted low when moving vertically, high when moving horizontally
  • otherwise do nothing

I've added up/down/left/right key handling by default to do this. But I ran into some issues that I'm unsure about:

Scroll areas already use up/down/left/right to scroll. I modified it slightly so ScrollContainer ignores up/down/left/right if no scrolling is possible (all content fits in the viewport).

Focus Groups also use up/down/left/right for focus movement. I modified it to use tabIndexDirection if nav_key_dir is null. But you still can't escape a focus group via this.

We'll probably need to get more real usage experience to know how to proceed.

@david-vanderson

Copy link
Copy Markdown
Owner Author

From discussion with @MichaelBelousov one usecase is a floatingMenu that has a bunch of expanders that each inside have a flexbox with some differently-sized widgets. Need a way to navigate everything by arrow keys directionally.

This supports a floating menu that works like a popup, where the arrow
keys will move focus like in a normal window, but escape still closes
it.
Act like the focus is coming into the window from outside in the given
direction.
This was only used by SuggestionWidget, and is replaced by checking if
any menuItem has .submenu = true.

This makes floatingMenu more usable in a popup scenario where there
might not be any menuItems at all.
This makes floatingMenu more usable as a popup.  When .from == null it
will center itself on the screen/subwindow the same way a new
floatingWindow does.

Also remove dvui.minSize - it was only used by 2 widgets and both were
old and not taking into account max_size_content.
No practical difference but guards against the case where
max_size_content is smaller than min_size_content.

In that case max_size_content wins, so this helps make that more
obvious.
When .style == .popup:
* floatingMenu will be modal
* any click outside the popup will close it
* escape closes it
* internal menu processes arrow keys like a normal window
@david-vanderson

Copy link
Copy Markdown
Owner Author

@MichaelBelousov I think this is ready for merging. It also includes a bunch of stuff to make floatingMenu work like a popup (when passed .style == .popup), after I looked at what you were doing to achieve this. Hopefully after this you shouldn't have to explicitly handle the mouse clicks or checking for escape.

Can you try in this branch to use the demo > Layout > Directional Navigation > Popup and let me know if that is what you were thinking?

@MichaelBelousov

Copy link
Copy Markdown
Collaborator

Can you try in this branch to use the demo > Layout > Directional Navigation > Popup and let me know if that is what you were thinking?

Will do later today!

@MichaelBelousov

Copy link
Copy Markdown
Collaborator

Superb work! Works intuitively and meets all criteria I was hoping to have!
I'm excited to integrate it!

The only thing I'd wonder is, and it's not even super relevant for me, is it trivial to customize the exact target for a given cardinal (left/right/up/down/next(tab)/prev(shift-tab)) direction?

One thing I found possibly unintuitive (although correct for my use case) was the four direction buttons (up/down/left/right) where settling on up and then clicking "down" brought you to right or left since they are vertically before down. Visually though they are not really "down".

So of course that is a different strategy which I think is more akin to maybe games, but I think could be solved with precise tab index control. So as long as that's easy, I think it works great.

Let me know if you have questions about that but I think this is a great feature, good default, and perfect for my case! Thank you!

@david-vanderson

Copy link
Copy Markdown
Owner Author

Let me know if you have questions about that but I think this is a great feature, good default, and perfect for my case! Thank you!

Awesome thank you!

One thing I found possibly unintuitive (although correct for my use case) was the four direction buttons (up/down/left/right) where settling on up and then clicking "down" brought you to right or left since they are vertically before down. Visually though they are not really "down".

I agree. My plan here is to wait for someone to be using it that kind of context and explore the options then. I suspect it will involve both some tweaking/customization to the algorithm plus some way to directly control edge cases.

Anyone reading this - if you are in this situation please file an issue!

@david-vanderson
david-vanderson merged commit d852ddd into main Aug 11, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gamepad Navigation Support

2 participants