]> git.mdlowis.com Git - proto/labwc.git/commitdiff
desktop: give focus to a modal dialog rather than its parent
authorJohn Lindgren <john@jlindgren.net>
Fri, 13 Jun 2025 15:00:26 +0000 (11:00 -0400)
committerJohan Malm <johanmalm@users.noreply.github.com>
Fri, 13 Jun 2025 17:40:29 +0000 (18:40 +0100)
Fixes: #2722
include/view.h
src/desktop.c
src/view.c
src/xwayland.c

index f3a0a9c2187870494a12492a69224be84cc14ef8..2ea83cd7d96b8fc613fd1059e86f4f0b402b8e2d 100644 (file)
@@ -156,6 +156,7 @@ struct view_impl {
        void (*minimize)(struct view *view, bool minimize);
        struct view *(*get_root)(struct view *self);
        void (*append_children)(struct view *self, struct wl_array *children);
+       bool (*is_modal_dialog)(struct view *self);
        struct view_size_hints (*get_size_hints)(struct view *self);
        /* if not implemented, VIEW_WANTS_FOCUS_ALWAYS is assumed */
        enum view_wants_focus (*wants_focus)(struct view *self);
@@ -612,6 +613,14 @@ void view_move_to_front(struct view *view);
 void view_move_to_back(struct view *view);
 struct view *view_get_root(struct view *view);
 void view_append_children(struct view *view, struct wl_array *children);
+
+/**
+ * view_get_modal_dialog() - returns any modal dialog found among this
+ * view's children or siblings (or possibly this view itself). Applies
+ * only to xwayland views and always returns NULL for xdg-shell views.
+ */
+struct view *view_get_modal_dialog(struct view *view);
+
 bool view_on_output(struct view *view, struct output *output);
 
 /**
index df179eb262a9d133df2fbc42be8a11ba465d8f88..28af6cc64170700510bea93651bfab18ae0fc33a 100644 (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? */
index 2c4560be5ce3994d70b62af4c4e84311b84a0fb4..27a1d535258270f59b5d469aae610e6cea31c6e0 100644 (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)
 {
index dfc38add642c284414a22e53b6c958d92658f2fd..0185e61ddfc11e06c5017eac4b5fd08e04764da3 100644 (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,