dvui icon indicating copy to clipboard operation
dvui copied to clipboard

Ability to set the default button focus for dialogs

Open phatchman opened this issue 6 months ago • 3 comments

I find myself writing custom dialog display functions, just so I can have the "OK" or "Cancel" button have default focus in the dialog.

It would be good to add an option to set the default focus for the buttons so that the user can close a dialog by pressing enter.

phatchman avatar Jun 02 '25 11:06 phatchman

Just pushed 9d63918d98ae3fbb53c6082d020d89c76c5fbc22 for this - how does that look?

david-vanderson avatar Jun 02 '25 20:06 david-vanderson

Works well. Thanks.

Do you think it's worth makinging the focus part a button init option or as a dvui.buttonFocused? Just thinking of people with a custom dialog being able to use the code in "local".

phatchman avatar Jun 04 '25 07:06 phatchman

I don't know. Putting it in the button init options seems like we should have it on all widgets. But then do we want to add something to Options?

The code in local was an easy copy of the dvui.button contents, so not too bad, but not great either.

Another option I thought about was using the tag stuff. But I just tried to do that, and it's pretty awkward since you don't get the tag until after the button runs, and at that point firstFrame returns false.

Maybe Options is the way to go, something like an enum .focus = .always, .focus = .first_frame?

david-vanderson avatar Jun 04 '25 12:06 david-vanderson

What if you make default focus a property of windows / floating windows, rather than individual widgets? That guarantees that only 1 widget can request default focus? Though it would be hard to identify which widget needed focus through an init option.

I think most UI libraries set the default focus to the widget with the lowest tab order. Could the floating window deinit find the child widget with the lowest tab order and set focus to that widget in the next frame?

phatchman avatar Jun 05 '25 01:06 phatchman

I think most UI libraries set the default focus to the widget with the lowest tab order. Could the floating window deinit find the child widget with the lowest tab order and set focus to that widget in the next frame?

This is an interesting idea.

One part is that dvui right now starts subwindows in a state where no widget has focus. I don't know if that is the best vs. starting with the first (in tab order). On one hand it make stuff like this harder, but on the other it means you can have a dialog where an accidental key press won't dismiss it.

If we did some kind of optional thing on floatingWindow to focus the first widget, I'm still not sure that would be great. You'd have to adjust the tab order of the dialog to make the default option first, but then the tab order wouldn't match the visual order.

Best guess so far is to do something to make focusing individual widgets better. I'm going to try the tag again, it should be able to work similar to the floating window deinit(). Will report back.

david-vanderson avatar Jun 05 '25 02:06 david-vanderson

My thought here is that tab order should always(?) follow focus order, which follows the visual order. I.e. if focused on a dialog with 2 or 3 buttons, one of them gets default focus and the others are always consecutive in both visual and tab order. After tabbing out of the buttons, we might then tab into the other widgets before eventually retuning to the default focused button.

Edited to clarify. So if the middle button is default-focus, the order is 3rd button, first button. Though I understand Mac and dvui do this opposite to windows, so the right-most button is usually default. Then it does violate the visual left-to right, but you'd typically still tab the buttons before any other widgets.

I think it would be strange for a window to open with focus on a certain widget, and then not be able to get back to that widget via tabbing, or for tab to return focus to that widget before tabbing through all other "tab indexed" widgets within in the window.

phatchman avatar Jun 05 '25 04:06 phatchman

We largely agree. I'm unsure if in a dialog the focus order should move logically through the dialog, so the buttons come last, or whether in a dialog the buttons come tab first, even though visually they are usually at the bottom.

But I think this discussion has moved me more towards "don't try to reuse the tab order for default focus" view. It seems like we could easily agree which button should have default focus, but not necessarily where that button should be in the tab order.

I'm still experimenting with a better way to set the default focus.

david-vanderson avatar Jun 05 '25 18:06 david-vanderson

Does 2a2778e558b3679bc0b775e03f50afafffb9584d look better?

david-vanderson avatar Jun 05 '25 20:06 david-vanderson

OK. It took me some time to work out how it worked, and why it was necessary, but I understand it now.

Basically, the tag is being used as a way to extract the widget id from the Dialog function. And dialog can't return something like WidgetData because the widgets aren't created until dialogDisplay() is called.

Yeah, I think this solves the issue of being able to access the widgets created internally by these functions. I guess it will only work for the first widget created (or whichever widget is chosen to put the tag on) in the function, but that's a definite improvement over the current situation.

phatchman avatar Jun 08 '25 03:06 phatchman

Good - it's also possible this gets overhauled due to #347 because that gives us a place to put a .focus = true parameter for functions that don't return a widget.

david-vanderson avatar Jun 08 '25 12:06 david-vanderson

Happy to close?

phatchman avatar Jun 13 '25 23:06 phatchman

Yes - thanks!

david-vanderson avatar Jun 13 '25 23:06 david-vanderson