RFC: Better handling for non-focusable containers in st_widget_navigate_focus

Jonas Dreßler requested to merge verdre/gnome-shell:st-focus-fixes into main

Not sure if this is a fix, a new feature or a workaround which allows for setting key focus to non-focusable containers.

This basically emerged from !277, where I tried to get rid of setting focus to the source actor after opening the popup menu and hooking into the source actors events for opening the menu. Since I also didn't want to set focus to the first menu entry (it probably looks weird after opening the menu with a mouse), I added an invisible dummy actor that receives focus by default. That wasn't the cleanest solution and so I tried setting focus to the menu container by default, which broke arrow key navigation because st_widget_real_navigate_focus will only try to focus members of the focus chain that are located completely above (arrow-up key) or below (arrow-down key) the currently focused actor (menu entries are obviously all located inside the container).

As a fix for that, I basically changed st_widget_real_navigate_focus to treat focus changes coming from non-focusable actors as if they were coming from outside.

From bd25d220:

st/widget: Only use "from" when navigating focus if actor can be focused

The purpose of the "from" argument when navigating focus is to find the next focusable element in the direction of the pressed key that is inside the focus group.

If st_widget_real_navigate_focus is called with a "from" argument that is not a focusable widget, we might end up not being able to navigate with the arrow keys. A case where this happens would be setting key focus to the menu actor of a popup menu ie. BoxPointer.actor or to one of it's children like PopupMenu.box (BoxPointer.actor -> BoxPointer.bin -> PopupMenu.box). st_widget_real_navigate_focus assumes that "from" is a focusable widget and therefore tries to find the next focusable widget in its focus chain, but since all members of that chain are children allocated inside the box, filter_by_position will filter out all members of the focus chain and key focus remains stuck.

As a fix, simply set "from" to NULL in case the actor is not a widget or not focusable, this makes pressing an arrow key in situations where key focus is set to a non-focusable actor behave similar to pressing the tab key and will always result in a new, valid focus.

In the end what this change allows is setting key focus more freely, reducing the chances of focus getting stuck with an actor. Also it's now easier to set default focus for containers like menus, allowing to make the behaviour of popupMenu more similar to traditional popup menus.

Merge request reports