desktop: give focus to a modal dialog rather than its parent

Fixes: #2722
This commit is contained in:
John Lindgren 2025-06-13 11:00:26 -04:00 committed by Johan Malm
parent ea1df930e9
commit 9782ffa868
4 changed files with 73 additions and 15 deletions

View file

@ -39,6 +39,25 @@ desktop_arrange_all_views(struct server *server)
}
}
static void
set_or_offer_focus(struct view *view)
{
struct seat *seat = &view->server->seat;
switch (view_wants_focus(view)) {
case VIEW_WANTS_FOCUS_ALWAYS:
if (view->surface != seat->seat->keyboard_state.focused_surface) {
seat_focus_surface(seat, view->surface);
}
break;
case VIEW_WANTS_FOCUS_LIKELY:
case VIEW_WANTS_FOCUS_UNLIKELY:
view_offer_focus(view);
break;
case VIEW_WANTS_FOCUS_NEVER:
break;
}
}
void
desktop_focus_view(struct view *view, bool raise)
{
@ -77,24 +96,17 @@ desktop_focus_view(struct view *view, bool raise)
workspaces_switch_to(view->workspace, /*update_focus*/ false);
}
struct seat *seat = &view->server->seat;
switch (view_wants_focus(view)) {
case VIEW_WANTS_FOCUS_ALWAYS:
if (view->surface != seat->seat->keyboard_state.focused_surface) {
seat_focus_surface(seat, view->surface);
}
break;
case VIEW_WANTS_FOCUS_LIKELY:
case VIEW_WANTS_FOCUS_UNLIKELY:
view_offer_focus(view);
break;
case VIEW_WANTS_FOCUS_NEVER:
break;
}
if (raise) {
view_move_to_front(view);
}
/*
* If any child/sibling of the view is a modal dialog, focus
* the dialog instead. It does not need to be raised separately
* since view_move_to_front() raises all sibling views together.
*/
struct view *dialog = view_get_modal_dialog(view);
set_or_offer_focus(dialog ? dialog : view);
}
/* TODO: focus layer-shell surfaces also? */

View file

@ -2343,6 +2343,36 @@ view_append_children(struct view *view, struct wl_array *children)
}
}
struct view *
view_get_modal_dialog(struct view *view)
{
assert(view);
if (!view->impl->is_modal_dialog) {
return NULL;
}
/* check view itself first */
if (view->impl->is_modal_dialog(view)) {
return view;
}
/* check sibling views */
struct view *dialog = NULL;
struct view *root = view_get_root(view);
struct wl_array children;
struct view **child;
wl_array_init(&children);
view_append_children(root, &children);
wl_array_for_each(child, &children) {
if (view->impl->is_modal_dialog(*child)) {
dialog = *child;
break;
}
}
wl_array_release(&children);
return dialog;
}
bool
view_has_strut_partial(struct view *view)
{

View file

@ -933,6 +933,12 @@ xwayland_view_append_children(struct view *self, struct wl_array *children)
}
}
static bool
xwayland_view_is_modal_dialog(struct view *self)
{
return xwayland_surface_from_view(self)->modal;
}
static void
xwayland_view_set_activated(struct view *view, bool activated)
{
@ -978,6 +984,7 @@ static const struct view_impl xwayland_view_impl = {
.minimize = xwayland_view_minimize,
.get_root = xwayland_view_get_root,
.append_children = xwayland_view_append_children,
.is_modal_dialog = xwayland_view_is_modal_dialog,
.get_size_hints = xwayland_view_get_size_hints,
.wants_focus = xwayland_view_wants_focus,
.offer_focus = xwayland_view_offer_focus,