diff --git a/include/view.h b/include/view.h index 6076d672..79963b4b 100644 --- a/include/view.h +++ b/include/view.h @@ -55,6 +55,8 @@ struct view_impl { void (*minimize)(struct view *view, bool minimize); void (*move_to_front)(struct view *view); void (*move_to_back)(struct view *view); + struct view *(*get_root)(struct view *self); + void (*append_children)(struct view *self, struct wl_array *children); }; struct view { @@ -212,6 +214,8 @@ void view_snap_to_region(struct view *view, struct region *region, bool store_na 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); const char *view_get_string_prop(struct view *view, const char *prop); void view_update_title(struct view *view); diff --git a/src/view.c b/src/view.c index 8b1f5743..4c72e13f 100644 --- a/src/view.c +++ b/src/view.c @@ -238,8 +238,8 @@ view_adjust_size(struct view *view, int *w, int *h) *h = MAX(*h, LAB_MIN_VIEW_HEIGHT); } -void -view_minimize(struct view *view, bool minimized) +static void +_minimize(struct view *view, bool minimized) { assert(view); if (view->minimized == minimized) { @@ -272,6 +272,40 @@ view_minimize(struct view *view, bool minimized) } } +static void +minimize_sub_views(struct view *view, bool minimized) +{ + struct view **child; + struct wl_array children; + + wl_array_init(&children); + view_append_children(view, &children); + wl_array_for_each(child, &children) { + _minimize(*child, minimized); + minimize_sub_views(*child, minimized); + } + wl_array_release(&children); +} + +/* + * Minimize the whole view-hierarchy from top to bottom regardless of which one + * in the hierarchy requested the minimize. For example, if an 'About' or + * 'Open File' dialog is minimized, its toplevel is minimized also. And vice + * versa. + */ +void +view_minimize(struct view *view, bool minimized) +{ + /* + * Minimize the root window first because some xwayland clients send a + * request-unmap to sub-windows at this point (for example gimp and its + * 'open file' dialog), so it saves trying to unmap them twice + */ + struct view *root = view_get_root(view); + _minimize(root, minimized); + minimize_sub_views(root, minimized); +} + static bool view_compute_centered_position(struct view *view, const struct wlr_box *ref, int w, int h, int *x, int *y) @@ -1042,6 +1076,25 @@ view_move_to_back(struct view *view) } } +struct view * +view_get_root(struct view *view) +{ + assert(view); + if (view->impl->get_root) { + return view->impl->get_root(view); + } + return view; +} + +void +view_append_children(struct view *view, struct wl_array *children) +{ + assert(view); + if (view->impl->append_children) { + view->impl->append_children(view, children); + } +} + const char * view_get_string_prop(struct view *view, const char *prop) { diff --git a/src/xdg.c b/src/xdg.c index c54796c5..7e557f50 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -335,6 +335,10 @@ enum z_direction { LAB_TO_BACK, }; +/* + * TODO: Combine append_children() and move_sub_views() as much as possible. + * https://github.com/labwc/labwc/pull/998#discussion_r1284085575 + */ static void move_sub_views(struct view *parent, enum z_direction z_direction) { @@ -371,7 +375,7 @@ move_sub_views(struct view *parent, enum z_direction z_direction) /* Return the most senior parent (=root) view */ static struct view * -root_view_of_view(struct view *view) +xdg_toplevel_view_get_root(struct view *view) { struct wlr_xdg_toplevel *root = top_parent_of(view); struct wlr_xdg_surface *surface = (struct wlr_xdg_surface *)root->base; @@ -387,7 +391,7 @@ root_view_of_view(struct view *view) static void xdg_toplevel_view_move_to_front(struct view *view) { - struct view *root = root_view_of_view(view); + struct view *root = xdg_toplevel_view_get_root(view); view_impl_move_to_front(root); move_sub_views(root, LAB_TO_FRONT); } @@ -395,11 +399,36 @@ xdg_toplevel_view_move_to_front(struct view *view) static void xdg_toplevel_view_move_to_back(struct view *view) { - struct view *root = root_view_of_view(view); + struct view *root = xdg_toplevel_view_get_root(view); view_impl_move_to_back(root); move_sub_views(root, LAB_TO_BACK); } +static void +xdg_toplevel_view_append_children(struct view *self, struct wl_array *children) +{ + struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(self); + struct view *view; + + wl_list_for_each_reverse(view, &self->server->views, link) + { + if (view == self) { + continue; + } + if (view->type != LAB_XDG_SHELL_VIEW) { + continue; + } + if (!view->mapped && !view->minimized) { + continue; + } + if (top_parent_of(view) != toplevel) { + continue; + } + struct view **child = wl_array_add(children, sizeof(*child)); + *child = view; + } +} + static void xdg_toplevel_view_set_activated(struct view *view, bool activated) { @@ -554,6 +583,8 @@ static const struct view_impl xdg_toplevel_view_impl = { .minimize = xdg_toplevel_view_minimize, .move_to_front = xdg_toplevel_view_move_to_front, .move_to_back = xdg_toplevel_view_move_to_back, + .get_root = xdg_toplevel_view_get_root, + .append_children = xdg_toplevel_view_append_children, }; void diff --git a/src/xwayland.c b/src/xwayland.c index 46e411b6..7aef5762 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -527,6 +527,10 @@ enum z_direction { LAB_TO_BACK, }; +/* + * TODO: Combine append_children() and move_sub_views() as much as possible. + * https://github.com/labwc/labwc/pull/998#discussion_r1284085575 + */ static void move_sub_views(struct view *parent, enum z_direction z_direction) { @@ -563,7 +567,7 @@ move_sub_views(struct view *parent, enum z_direction z_direction) } static struct view * -root_view_of_view(struct view *view) +xwayland_view_get_root(struct view *view) { struct wlr_xwayland_surface *root = top_parent_of(view); return (struct view *)root->data; @@ -572,7 +576,7 @@ root_view_of_view(struct view *view) static void xwayland_view_move_to_front(struct view *view) { - struct view *root = root_view_of_view(view); + struct view *root = xwayland_view_get_root(view); view_impl_move_to_front(root); move_sub_views(root, LAB_TO_FRONT); } @@ -580,11 +584,43 @@ xwayland_view_move_to_front(struct view *view) static void xwayland_view_move_to_back(struct view *view) { - struct view *root = root_view_of_view(view); + struct view *root = xwayland_view_get_root(view); view_impl_move_to_back(root); move_sub_views(root, LAB_TO_BACK); } +static void +xwayland_view_append_children(struct view *self, struct wl_array *children) +{ + struct wlr_xwayland_surface *surface = xwayland_surface_from_view(self); + struct view *view; + + wl_list_for_each_reverse(view, &self->server->views, link) + { + if (view == self) { + continue; + } + if (view->type != LAB_XWAYLAND_VIEW) { + continue; + } + /* + * This happens when a view has never been mapped or when a + * client has requested a `handle_unmap`. + */ + if (!view->surface) { + continue; + } + if (!view->mapped && !view->minimized) { + continue; + } + if (top_parent_of(view) != surface) { + continue; + } + struct view **child = wl_array_add(children, sizeof(*child)); + *child = view; + } +} + static void xwayland_view_set_activated(struct view *view, bool activated) { @@ -628,6 +664,8 @@ static const struct view_impl xwayland_view_impl = { .minimize = xwayland_view_minimize, .move_to_front = xwayland_view_move_to_front, .move_to_back = xwayland_view_move_to_back, + .get_root = xwayland_view_get_root, + .append_children = xwayland_view_append_children, }; void