diff --git a/include/view.h b/include/view.h index 12b5d109..b3cc0368 100644 --- a/include/view.h +++ b/include/view.h @@ -290,6 +290,14 @@ void view_array_append(struct server *server, struct wl_array *views, enum view_wants_focus view_wants_focus(struct view *view); +/** + * view_is_focusable_from() - variant of view_is_focusable() + * that takes into account the previously focused surface + * @view: view to be checked + * @prev_surface: previously focused surface + */ +bool view_is_focusable_from(struct view *view, struct wlr_surface *prev); + /** * view_is_focusable() - Check whether or not a view can be focused * @view: view to be checked @@ -302,7 +310,10 @@ enum view_wants_focus view_wants_focus(struct view *view); * The only views that are allowed to be focusd are those that have a surface * and have been mapped at some point since creation. */ -bool view_is_focusable(struct view *view); +static inline bool +view_is_focusable(struct view *view) { + return view_is_focusable_from(view, NULL); +} void view_toggle_keybinds(struct view *view); diff --git a/src/desktop.c b/src/desktop.c index 607da49c..ab057fbe 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -192,6 +192,8 @@ desktop_cycle_view(struct server *server, struct view *start_view, struct view * desktop_topmost_focusable_view(struct server *server) { + struct wlr_surface *prev = + server->seat.seat->keyboard_state.focused_surface; struct view *view; struct wl_list *node_list; struct wlr_scene_node *node; @@ -202,7 +204,7 @@ desktop_topmost_focusable_view(struct server *server) continue; } view = node_view_from_node(node); - if (view->mapped && view_is_focusable(view)) { + if (view->mapped && view_is_focusable_from(view, prev)) { return view; } } diff --git a/src/view.c b/src/view.c index dcca4bcd..813566bd 100644 --- a/src/view.c +++ b/src/view.c @@ -154,16 +154,28 @@ view_wants_focus(struct view *view) } bool -view_is_focusable(struct view *view) +view_is_focusable_from(struct view *view, struct wlr_surface *prev) { assert(view); if (!view->surface) { return false; } - if (view_wants_focus(view) != VIEW_WANTS_FOCUS_ALWAYS) { + if (!view->mapped && !view->minimized) { return false; } - return (view->mapped || view->minimized); + enum view_wants_focus wants_focus = view_wants_focus(view); + /* + * Consider "offer focus" (Globally Active) views as focusable + * only if another surface from the same application already had + * focus. The goal is to allow focusing a parent window when a + * dialog/popup is closed, but still avoid focusing standalone + * panels/toolbars/notifications. Note that we are basically + * guessing whether Globally Active views want focus, and will + * probably be wrong some of the time. + */ + return (wants_focus == VIEW_WANTS_FOCUS_ALWAYS + || (wants_focus == VIEW_WANTS_FOCUS_OFFER + && prev && view_is_related(view, prev))); } /**