]> git.mdlowis.com Git - proto/labwc.git/commitdiff
ssd: allow ssd to be smaller than minimal size by hiding buttons
authortokyo4j <hrak1529@gmail.com>
Sun, 25 Aug 2024 07:33:41 +0000 (16:33 +0900)
committerAndrew J. Hesford <ajh@sideband.org>
Tue, 3 Sep 2024 12:12:25 +0000 (08:12 -0400)
This fixes the ugly look of SSD for tiny windows (e.g. "xterm -geometry
1x1") due to the early return in `ssd_update_geometry()`. Now SSDs are
rendered correctly for those windows by hiding some buttons when the
window width is smaller than the total width of buttons. Additionally for
windows smaller than (button width)*2, the corners are un-rounded so a
small titlebar can be rendered with a scene-rect.

include/ssd-internal.h
src/ssd/ssd-border.c
src/ssd/ssd-extents.c
src/ssd/ssd-part.c
src/ssd/ssd-titlebar.c
src/ssd/ssd.c

index 3d9733552efe120206afc49e9471978e7718277c..3c0fe38b4d469addf1ed19deb7d80dc64ff03e43 100644 (file)
@@ -53,12 +53,19 @@ struct ssd {
                bool was_omnipresent;
 
                /*
-                * Corners need to be (un)rounded when toggling tiling or
-                * maximization, and the button needs to be swapped on
+                * Corners need to be (un)rounded and borders need be shown/hidden
+                * when toggling maximization, and the button needs to be swapped on
                 * maximization toggles.
                 */
                bool was_maximized;
-               bool was_tiled_not_maximized;
+
+               /*
+                * Corners need to be (un)rounded but borders should be kept shown when
+                * the window is (un)tiled and notified about it or when the window may
+                * become so small that only a squared scene-rect can be used to render
+                * such a small titlebar.
+                */
+               bool was_squared;
 
                struct wlr_box geometry;
                struct ssd_state_title {
@@ -109,9 +116,6 @@ struct ssd_part {
        /* This part represented in scene graph */
        struct wlr_scene_node *node;
 
-       /* Targeted geometry. May be NULL */
-       struct wlr_box *geometry;
-
        struct wl_list link;
 };
 
@@ -151,6 +155,7 @@ void ssd_destroy_parts(struct wl_list *list);
 void ssd_titlebar_create(struct ssd *ssd);
 void ssd_titlebar_update(struct ssd *ssd);
 void ssd_titlebar_destroy(struct ssd *ssd);
+bool ssd_should_be_squared(struct ssd *ssd);
 
 void ssd_border_create(struct ssd *ssd);
 void ssd_border_update(struct ssd *ssd);
index fb1ac28389dd6bcb7a8fe876a00b3a748f56eee6..3b7b26cb6f26fcd38590ccafe6e966051ab67254 100644 (file)
@@ -113,16 +113,16 @@ ssd_border_update(struct ssd *ssd)
         *  |_______________|
         */
 
-       int side_height = ssd->state.was_tiled_not_maximized
+       int side_height = ssd->state.was_squared
                ? height + ssd->titlebar.height
                : height;
-       int side_y = ssd->state.was_tiled_not_maximized
+       int side_y = ssd->state.was_squared
                ? -ssd->titlebar.height
                : 0;
-       int top_width = ssd->titlebar.height <= 0 || ssd->state.was_tiled_not_maximized
+       int top_width = ssd->titlebar.height <= 0 || ssd->state.was_squared
                ? full_width
                : width - 2 * theme->window_button_width;
-       int top_x = ssd->titlebar.height <= 0 || ssd->state.was_tiled_not_maximized
+       int top_x = ssd->titlebar.height <= 0 || ssd->state.was_squared
                ? 0
                : theme->border_width + theme->window_button_width;
 
index 274b5f506a3132cd96e0f8fa25dfc560288e1a96..b91cf94f9ef2efce85f603e99076713d49925cd2 100644 (file)
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 
+#include <assert.h>
 #include <pixman.h>
 #include "common/mem.h"
 #include "common/scene-helpers.h"
@@ -14,14 +15,7 @@ add_extent(struct wl_list *part_list, enum ssd_part_type type,
 {
        float invisible[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
        struct ssd_part *part = add_scene_part(part_list, type);
-       /*
-        * Extents need additional geometry to enable dynamic
-        * resize based on position and output->usable_area.
-        *
-        * part->geometry will get free'd automatically in ssd_destroy_parts().
-        */
        part->node = &wlr_scene_rect_create(parent, 0, 0, invisible)->node;
-       part->geometry = znew(struct wlr_box);
        return part;
 }
 
@@ -32,8 +26,6 @@ ssd_extents_create(struct ssd *ssd)
        struct theme *theme = view->server->theme;
        struct wl_list *part_list = &ssd->extents.parts;
        int extended_area = SSD_EXTENDED_AREA;
-       int corner_size = extended_area + theme->border_width +
-               theme->window_button_width / 2;
 
        ssd->extents.tree = wlr_scene_tree_create(ssd->tree);
        struct wlr_scene_tree *parent = ssd->extents.tree;
@@ -45,43 +37,17 @@ ssd_extents_create(struct ssd *ssd)
                -(theme->border_width + extended_area),
                -(ssd->titlebar.height + theme->border_width + extended_area));
 
-       /* Initialize parts and set constant values for targeted geometry */
-       struct ssd_part *p;
-
        /* Top */
-       p = add_extent(part_list, LAB_SSD_PART_CORNER_TOP_LEFT, parent);
-       p->geometry->width = corner_size;
-       p->geometry->height = corner_size;
-
-       p = add_extent(part_list, LAB_SSD_PART_TOP, parent);
-       p->geometry->x = corner_size;
-       p->geometry->height = extended_area;
-
-       p = add_extent(part_list, LAB_SSD_PART_CORNER_TOP_RIGHT, parent);
-       p->geometry->width = corner_size;
-       p->geometry->height = corner_size;
-
+       add_extent(part_list, LAB_SSD_PART_CORNER_TOP_LEFT, parent);
+       add_extent(part_list, LAB_SSD_PART_TOP, parent);
+       add_extent(part_list, LAB_SSD_PART_CORNER_TOP_RIGHT, parent);
        /* Sides */
-       p = add_extent(part_list, LAB_SSD_PART_LEFT, parent);
-       p->geometry->y = corner_size;
-       p->geometry->width = extended_area;
-
-       p = add_extent(part_list, LAB_SSD_PART_RIGHT, parent);
-       p->geometry->y = corner_size;
-       p->geometry->width = extended_area;
-
+       add_extent(part_list, LAB_SSD_PART_LEFT, parent);
+       add_extent(part_list, LAB_SSD_PART_RIGHT, parent);
        /* Bottom */
-       p = add_extent(part_list, LAB_SSD_PART_CORNER_BOTTOM_LEFT, parent);
-       p->geometry->width = corner_size;
-       p->geometry->height = corner_size;
-
-       p = add_extent(part_list, LAB_SSD_PART_BOTTOM, parent);
-       p->geometry->x = corner_size;
-       p->geometry->height = extended_area;
-
-       p = add_extent(part_list, LAB_SSD_PART_CORNER_BOTTOM_RIGHT, parent);
-       p->geometry->width = corner_size;
-       p->geometry->height = corner_size;
+       add_extent(part_list, LAB_SSD_PART_CORNER_BOTTOM_LEFT, parent);
+       add_extent(part_list, LAB_SSD_PART_BOTTOM, parent);
+       add_extent(part_list, LAB_SSD_PART_CORNER_BOTTOM_RIGHT, parent);
 
        /* Initial manual update to keep X11 applications happy */
        ssd_extents_update(ssd);
@@ -111,7 +77,7 @@ ssd_extents_update(struct ssd *ssd)
        int full_width = width + 2 * theme->border_width;
        int extended_area = SSD_EXTENDED_AREA;
        int corner_size = extended_area + theme->border_width +
-               theme->window_button_width / 2;
+               MIN(theme->window_button_width, width) / 2;
        int side_width = full_width + extended_area * 2 - corner_size * 2;
        int side_height = full_height + extended_area * 2 - corner_size * 2;
 
@@ -119,6 +85,7 @@ ssd_extents_update(struct ssd *ssd)
        struct wlr_box result_box;
        struct ssd_part *part;
        struct wlr_scene_rect *rect;
+       struct wlr_box target;
 
        /* Make sure we update the y offset based on titlebar shown / hidden */
        wlr_scene_node_set_position(&ssd->extents.tree->node,
@@ -149,44 +116,69 @@ ssd_extents_update(struct ssd *ssd)
        int base_x, base_y;
        wlr_scene_node_coords(&ssd->extents.tree->node, &base_x, &base_y);
 
-       struct wlr_box *target;
        wl_list_for_each(part, &ssd->extents.parts, link) {
                rect = wlr_scene_rect_from_node(part->node);
-               target = part->geometry;
                switch (part->type) {
+               case LAB_SSD_PART_CORNER_TOP_LEFT:
+                       target.x = 0;
+                       target.y = 0;
+                       target.width = corner_size;
+                       target.height = corner_size;
+                       break;
                case LAB_SSD_PART_TOP:
-                       target->width = side_width;
+                       target.x = corner_size;
+                       target.y = 0;
+                       target.width = side_width;
+                       target.height = extended_area;
                        break;
                case LAB_SSD_PART_CORNER_TOP_RIGHT:
-                       target->x = corner_size + side_width;
+                       target.x = corner_size + side_width;
+                       target.y = 0;
+                       target.width = corner_size;
+                       target.height = corner_size;
                        break;
                case LAB_SSD_PART_LEFT:
-                       target->height = side_height;
+                       target.x = 0;
+                       target.y = corner_size;
+                       target.width = extended_area;
+                       target.height = side_height;
                        break;
                case LAB_SSD_PART_RIGHT:
-                       target->x = extended_area + full_width;
-                       target->height = side_height;
+                       target.x = extended_area + full_width;
+                       target.y = corner_size;
+                       target.width = extended_area;
+                       target.height = side_height;
                        break;
                case LAB_SSD_PART_CORNER_BOTTOM_LEFT:
-                       target->y = corner_size + side_height;
+                       target.x = 0;
+                       target.y = corner_size + side_height;
+                       target.width = corner_size;
+                       target.height = corner_size;
                        break;
                case LAB_SSD_PART_BOTTOM:
-                       target->width = side_width;
-                       target->y = extended_area + full_height;
+                       target.x = corner_size;
+                       target.y = extended_area + full_height;
+                       target.width = side_width;
+                       target.height = extended_area;
                        break;
                case LAB_SSD_PART_CORNER_BOTTOM_RIGHT:
-                       target->x = corner_size + side_width;
-                       target->y = corner_size + side_height;
+                       target.x = corner_size + side_width;
+                       target.y = corner_size + side_height;
+                       target.width = corner_size;
+                       target.height = corner_size;
                        break;
                default:
-                       break;
+                       /* not reached */
+                       assert(false);
+                       /* suppress warnings with NDEBUG */
+                       target = (struct wlr_box){0};
                }
 
                /* Get layout geometry of what the part *should* be */
-               part_box.x = base_x + target->x;
-               part_box.y = base_y + target->y;
-               part_box.width = target->width;
-               part_box.height = target->height;
+               part_box.x = base_x + target.x;
+               part_box.y = base_y + target.y;
+               part_box.width = target.width;
+               part_box.height = target.height;
 
                /* Constrain part to output->usable_area */
                pixman_region32_clear(&intersection);
@@ -232,18 +224,12 @@ ssd_extents_update(struct ssd *ssd)
                        wlr_scene_rect_set_size(rect, result_box.width,
                                result_box.height);
                        wlr_scene_node_set_position(part->node,
-                               target->x + (result_box.x - part_box.x),
-                               target->y + (result_box.y - part_box.y));
-                       continue;
-               }
-
-               /* Fully visible */
-               if (target->x != part->node->x
-                               || target->y != part->node->y) {
-                       wlr_scene_node_set_position(part->node, target->x, target->y);
-               }
-               if (target->width != rect->width || target->height != rect->height) {
-                       wlr_scene_rect_set_size(rect, target->width, target->height);
+                               target.x + (result_box.x - part_box.x),
+                               target.y + (result_box.y - part_box.y));
+               } else {
+                       /* Fully visible */
+                       wlr_scene_node_set_position(part->node, target.x, target.y);
+                       wlr_scene_rect_set_size(rect, target.width, target.height);
                }
        }
        pixman_region32_fini(&intersection);
index 9c3294e2ef0d977eae09ca412c2a1de0723eec28..4a5eda72776fc76cfca0a8699e715b4fa0ad828b 100644 (file)
@@ -213,10 +213,6 @@ ssd_destroy_parts(struct wl_list *list)
                }
                /* part->buffer will free itself along the scene_buffer node */
                part->buffer = NULL;
-               if (part->geometry) {
-                       free(part->geometry);
-                       part->geometry = NULL;
-               }
                wl_list_remove(&part->link);
                free(part);
        }
index 407753bb7e1dc8f166de7caaaa7949c42fb29c9e..71864cb00ecbfd0dcc38653f5188506231de05b4 100644 (file)
@@ -20,6 +20,7 @@
 
 static void set_squared_corners(struct ssd *ssd, bool enable);
 static void set_alt_button_icon(struct ssd *ssd, enum ssd_part_type type, bool enable);
+static void update_visible_buttons(struct ssd *ssd);
 
 static void
 add_button(struct ssd *ssd, struct ssd_sub_tree *subtree, enum ssd_part_type type, int x)
@@ -166,6 +167,8 @@ ssd_titlebar_create(struct ssd *ssd)
                }
        } FOR_EACH_END
 
+       update_visible_buttons(ssd);
+
        ssd_update_title(ssd);
 
        bool maximized = view->maximized == VIEW_AXIS_BOTH;
@@ -183,9 +186,9 @@ ssd_titlebar_create(struct ssd *ssd)
                set_alt_button_icon(ssd, LAB_SSD_BUTTON_OMNIPRESENT, true);
        }
 
-       if (view_is_tiled_and_notify_tiled(view) && !maximized) {
+       if (ssd_should_be_squared(ssd)) {
                set_squared_corners(ssd, true);
-               ssd->state.was_tiled_not_maximized = true;
+               ssd->state.was_squared = true;
        }
 }
 
@@ -241,6 +244,56 @@ set_alt_button_icon(struct ssd *ssd, enum ssd_part_type type, bool enable)
        } FOR_EACH_END
 }
 
+/*
+ * Usually this function just enables all the nodes for buttons, but some
+ * buttons can be hidden for small windows (e.g. xterm -geometry 1x1).
+ */
+static void
+update_visible_buttons(struct ssd *ssd)
+{
+       struct view *view = ssd->view;
+       int width = view->current.width;
+       int button_width = view->server->theme->window_button_width;
+       int button_count_left = wl_list_length(&rc.title_buttons_left);
+       int button_count_right = wl_list_length(&rc.title_buttons_right);
+
+       /* Make sure infinite loop never occurs */
+       assert(button_width > 0);
+       /*
+        * The corner-left button is lastly removed as it's usually a window
+        * menu button (or an app icon button in the future).
+        */
+       while (width < button_width * (button_count_left + button_count_right)) {
+               if (button_count_left > button_count_right) {
+                       button_count_left--;
+               } else {
+                       button_count_right--;
+               }
+       }
+
+       int button_count;
+       struct ssd_part *part;
+       struct ssd_sub_tree *subtree;
+       struct title_button *b;
+       FOR_EACH_STATE(ssd, subtree) {
+               button_count = 0;
+               wl_list_for_each(b, &rc.title_buttons_left, link) {
+                       part = ssd_get_part(&subtree->parts, b->type);
+                       wlr_scene_node_set_enabled(part->node,
+                               button_count < button_count_left);
+                       button_count++;
+               }
+
+               button_count = 0;
+               wl_list_for_each_reverse(b, &rc.title_buttons_right, link) {
+                       part = ssd_get_part(&subtree->parts, b->type);
+                       wlr_scene_node_set_enabled(part->node,
+                               button_count < button_count_right);
+                       button_count++;
+               }
+       } FOR_EACH_END
+}
+
 void
 ssd_titlebar_update(struct ssd *ssd)
 {
@@ -249,17 +302,16 @@ ssd_titlebar_update(struct ssd *ssd)
        struct theme *theme = view->server->theme;
 
        bool maximized = view->maximized == VIEW_AXIS_BOTH;
-       bool tiled_not_maximized =
-               view_is_tiled_and_notify_tiled(ssd->view) && !maximized;
+       bool squared = ssd_should_be_squared(ssd);
 
        if (ssd->state.was_maximized != maximized
-                       || ssd->state.was_tiled_not_maximized != tiled_not_maximized) {
-               set_squared_corners(ssd, maximized || tiled_not_maximized);
+                       || ssd->state.was_squared != squared) {
+               set_squared_corners(ssd, maximized || squared);
                if (ssd->state.was_maximized != maximized) {
                        set_alt_button_icon(ssd, LAB_SSD_BUTTON_MAXIMIZE, maximized);
                }
                ssd->state.was_maximized = maximized;
-               ssd->state.was_tiled_not_maximized = tiled_not_maximized;
+               ssd->state.was_squared = squared;
        }
 
        if (ssd->state.was_shaded != view->shaded) {
@@ -277,11 +329,13 @@ ssd_titlebar_update(struct ssd *ssd)
                return;
        }
 
+       update_visible_buttons(ssd);
+
        int x;
        struct ssd_part *part;
        struct ssd_sub_tree *subtree;
        struct title_button *b;
-       int bg_offset = maximized || tiled_not_maximized ? 0 : theme->window_button_width;
+       int bg_offset = maximized || squared ? 0 : theme->window_button_width;
        FOR_EACH_STATE(ssd, subtree) {
                part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR);
                wlr_scene_rect_set_size(
@@ -343,13 +397,11 @@ ssd_titlebar_destroy(struct ssd *ssd)
  */
 
 static void
-ssd_update_title_positions(struct ssd *ssd)
+ssd_update_title_positions(struct ssd *ssd, int offset_left, int offset_right)
 {
        struct view *view = ssd->view;
        struct theme *theme = view->server->theme;
        int width = view->current.width;
-       int offset_left = theme->window_button_width * wl_list_length(&rc.title_buttons_left);
-       int offset_right = theme->window_button_width * wl_list_length(&rc.title_buttons_right);
        int title_bg_width = width - offset_left - offset_right;
 
        int x, y;
@@ -396,6 +448,33 @@ ssd_update_title_positions(struct ssd *ssd)
        } FOR_EACH_END
 }
 
+/*
+ * Get left/right offsets of the title area based on visible/hidden states of
+ * buttons set in update_visible_buttons().
+ */
+static void
+get_title_offsets(struct ssd *ssd, int *offset_left, int *offset_right)
+{
+       struct ssd_sub_tree *subtree = &ssd->titlebar.active;
+       int button_width = ssd->view->server->theme->window_button_width;
+       *offset_left = 0;
+       *offset_right = 0;
+
+       struct title_button *b;
+       wl_list_for_each(b, &rc.title_buttons_left, link) {
+               struct ssd_part *part = ssd_get_part(&subtree->parts, b->type);
+               if (part->node->enabled) {
+                       *offset_left += button_width;
+               }
+       }
+       wl_list_for_each_reverse(b, &rc.title_buttons_right, link) {
+               struct ssd_part *part = ssd_get_part(&subtree->parts, b->type);
+               if (part->node->enabled) {
+                       *offset_right += button_width;
+               }
+       }
+}
+
 void
 ssd_update_title(struct ssd *ssd)
 {
@@ -419,8 +498,9 @@ ssd_update_title(struct ssd *ssd)
        struct ssd_part *part;
        struct ssd_sub_tree *subtree;
        struct ssd_state_title_width *dstate;
-       int offset_left = theme->window_button_width * wl_list_length(&rc.title_buttons_left);
-       int offset_right = theme->window_button_width * wl_list_length(&rc.title_buttons_right);
+
+       int offset_left, offset_right;
+       get_title_offsets(ssd, &offset_left, &offset_right);
        int title_bg_width = view->current.width - offset_left - offset_right;
 
        FOR_EACH_STATE(ssd, subtree) {
@@ -477,7 +557,7 @@ ssd_update_title(struct ssd *ssd)
                }
                state->text = xstrdup(title);
        }
-       ssd_update_title_positions(ssd);
+       ssd_update_title_positions(ssd, offset_left, offset_right);
 }
 
 static void
@@ -519,4 +599,15 @@ disable_old_hover:
        }
 }
 
+bool
+ssd_should_be_squared(struct ssd *ssd)
+{
+       struct view *view = ssd->view;
+       int button_width = view->server->theme->window_button_width;
+
+       return (view_is_tiled_and_notify_tiled(view)
+                       || view->current.width < button_width * 2)
+               && view->maximized != VIEW_AXIS_BOTH;
+}
+
 #undef FOR_EACH_STATE
index 98d40d4f66e69f975202c726572696faa70fc5d8..0085fe3db4f0800e78cd6901544caed1cdb13a37 100644 (file)
@@ -181,9 +181,9 @@ ssd_create(struct view *view, bool active)
        ssd_extents_create(ssd);
        /*
         * We need to create the borders after the titlebar because it sets
-        * ssd->state.was_tiled_not_maximized which ssd_border_create()
-        * reacts to. TODO: Set the state here instead so the order does
-        * not matter anymore.
+        * ssd->state.squared which ssd_border_create() reacts to.
+        * TODO: Set the state here instead so the order does not matter
+        * anymore.
         */
        ssd_titlebar_create(ssd);
        ssd_border_create(ssd);
@@ -227,33 +227,19 @@ ssd_update_geometry(struct ssd *ssd)
        struct wlr_box cached = ssd->state.geometry;
        struct wlr_box current = view->current;
 
-       int min_view_width = rc.theme->window_button_width * (
-               wl_list_length(&rc.title_buttons_left) + wl_list_length(&rc.title_buttons_right));
        int eff_width = current.width;
        int eff_height = view_effective_height(view, /* use_pending */ false);
 
-       if (eff_width > 0 && eff_width < min_view_width) {
-               /*
-                * Prevent negative values in calculations like
-                * `width - theme->window_button_width
-                * * (wl_list_length(&rc.title_buttons_left)
-                * + wl_list_length(&rc.title_buttons_right))`
-                */
-               wlr_log(WLR_ERROR,
-                       "view width is smaller than its minimal value");
-               return;
-       }
-
        bool update_area = eff_width != cached.width || eff_height != cached.height;
        bool update_extents = update_area
                || current.x != cached.x || current.y != cached.y;
 
        bool maximized = view->maximized == VIEW_AXIS_BOTH;
-       bool tiled_not_maximized = view_is_tiled(view) && !maximized;
+       bool squared = ssd_should_be_squared(ssd);
 
        bool state_changed = ssd->state.was_maximized != maximized
                || ssd->state.was_shaded != view->shaded
-               || ssd->state.was_tiled_not_maximized != tiled_not_maximized
+               || ssd->state.was_squared != squared
                || ssd->state.was_omnipresent != view->visible_on_all_workspaces;
 
        if (update_extents) {