]> git.mdlowis.com Git - proto/labwc.git/commitdiff
Add window resize indicator
authorConsolatis <35009135+Consolatis@users.noreply.github.com>
Thu, 17 Aug 2023 16:59:29 +0000 (18:59 +0200)
committerConsolatis <35009135+Consolatis@users.noreply.github.com>
Sat, 19 Aug 2023 16:37:16 +0000 (18:37 +0200)
12 files changed:
docs/labwc-config.5.scd
docs/rc.xml.all
include/config/rcxml.h
include/resize_indicator.h [new file with mode: 0644]
include/view.h
src/common/font.c
src/config/rcxml.c
src/interactive.c
src/server.c
src/ssd/meson.build
src/ssd/resize_indicator.c [new file with mode: 0644]
src/view.c

index 10b228428e416e026d87b1c1c8c3325b01870b9b..feddedb3a4605ef39d06d93a0147456f5e62185f 100644 (file)
@@ -249,6 +249,22 @@ Therefore, where multiple objects of the same kind are required (for example
        Specify the number of pixels to reserve at the edges of an output.
        New, maximized and tiled windows will not be placed in these areas.
 
+## RESIZE
+
+*<resize><popupShow>* [Never|Always|Nonpixel]
+       Show a small indicator on top of the window when resizing or moving.
+       When the application sets size-hints (usually X11 terminal emulators),
+       the indicator will show the dimensions divided by size hints instead.
+       In the case of terminal emulators this usually means columns x rows.
+
+       The different values mean:
+       - *Never* Do not render the indicator
+       - *Always* Render the indicator while moving and resizing windows
+       - *Nonpixel* Only render the indicator during resize for windows using
+         size-hints
+
+       Default is Never.
+
 ## KEYBOARD
 
 *<keyboard><keybind key="">*
index 445b61dc8637fb54b26168e2f2fa464b1578575b..ce7020de8c59f2432c1bcdedd50e587f9c02d090 100644 (file)
@@ -52,6 +52,9 @@
     <screenEdgeStrength>20</screenEdgeStrength>
   </resistance>
 
+  <!-- Show a simple resize and move indicator -->
+  <resize popupShow="Never" />
+
   <focus>
     <followMouse>no</followMouse>
     <followMouseRequiresMovement>yes</followMouseRequiresMovement>
index 62a06ab671258f082f0bff8dda9a5fa35574b3e3..10d74a97d836efe91218286620a3c77c6b1464f6 100644 (file)
@@ -10,6 +10,7 @@
 #include "common/buf.h"
 #include "common/font.h"
 #include "config/libinput.h"
+#include "resize_indicator.h"
 #include "theme.h"
 
 enum window_switcher_field_content {
@@ -78,6 +79,8 @@ struct rcxml {
        int snap_edge_range;
        bool snap_top_maximize;
 
+       enum resize_indicator_mode resize_indicator;
+
        struct {
                int popuptime;
                int min_nr_workspaces;
diff --git a/include/resize_indicator.h b/include/resize_indicator.h
new file mode 100644 (file)
index 0000000..36e7b0d
--- /dev/null
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef LABWC_RESIZE_INDICATOR_H
+#define LABWC_RESIZE_INDICATOR_H
+
+struct server;
+struct view;
+
+enum resize_indicator_mode {
+       LAB_RESIZE_INDICATOR_NEVER = 0,
+       LAB_RESIZE_INDICATOR_ALWAYS,
+       LAB_RESIZE_INDICATOR_NON_PIXEL
+};
+
+void resize_indicator_reconfigure(struct server *server);
+void resize_indicator_show(struct view *view);
+void resize_indicator_update(struct view *view);
+void resize_indicator_hide(struct view *view);
+
+#endif /* LABWC_RESIZE_INDICATOR_H */
index 01468745a15df83954956aebe345d9081fc6f48b..064200f860f7c5e3d6ad4fcf745410c5e20cacf9 100644 (file)
@@ -128,6 +128,13 @@ struct view {
        struct wl_event_source *pending_configure_timeout;
 
        struct ssd *ssd;
+       struct resize_indicator {
+               int width, height;
+               struct wlr_scene_tree *tree;
+               struct wlr_scene_rect *border;
+               struct wlr_scene_rect *background;
+               struct scaled_font_buffer *text;
+       } resize_indicator;
 
        struct foreign_toplevel {
                struct wlr_foreign_toplevel_handle_v1 *handle;
index 091863b6c9808d36627ab30fc74f09c34cf5acfe..e173db7a0c307d60eec11d4a86c4e7aa3e8203b9 100644 (file)
@@ -51,6 +51,7 @@ font_extents(struct font *font, const char *string)
        pango_extents_to_pixels(&rect, NULL);
 
        /* we put a 2 px edge on each side - because Openbox does it :) */
+       /* TODO: remove the 4 pixel addition and always do the padding by the caller */
        rect.width += 4;
 
        cairo_destroy(c);
index 735afeb3c34b4c9906708a6ec3bba10e3f267fa1..d0d24d33b9ac7ccba8afff26788e15a0280292a2 100644 (file)
@@ -657,6 +657,16 @@ entry(xmlNode *node, char *nodename, char *content)
                rc.workspace_config.popuptime = atoi(content);
        } else if (!strcasecmp(nodename, "number.desktops")) {
                rc.workspace_config.min_nr_workspaces = MAX(1, atoi(content));
+       } else if (!strcasecmp(nodename, "popupShow.resize")) {
+               if (!strcasecmp(content, "Always")) {
+                       rc.resize_indicator = LAB_RESIZE_INDICATOR_ALWAYS;
+               } else if (!strcasecmp(content, "Never")) {
+                       rc.resize_indicator = LAB_RESIZE_INDICATOR_NEVER;
+               } else if (!strcasecmp(content, "Nonpixel")) {
+                       rc.resize_indicator = LAB_RESIZE_INDICATOR_NON_PIXEL;
+               } else {
+                       wlr_log(WLR_ERROR, "Invalid value for <resize popupShow />");
+               }
        }
 }
 
@@ -806,6 +816,8 @@ rcxml_init(void)
        rc.window_switcher.preview = true;
        rc.window_switcher.outlines = true;
 
+       rc.resize_indicator = LAB_RESIZE_INDICATOR_NEVER;
+
        rc.workspace_config.popuptime = INT_MIN;
        rc.workspace_config.min_nr_workspaces = 1;
 }
index 9a009d93811996d4065a52110a8b302b7ece2480..3caac79e2f79526012d06ce6eaf1446ae2094209 100644 (file)
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-only
 #include "labwc.h"
 #include "regions.h"
+#include "resize_indicator.h"
 #include "view.h"
 
 static int
@@ -96,6 +97,9 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
        server->grab_y = seat->cursor->y;
        server->grab_box = geometry;
        server->resize_edges = edges;
+       if (rc.resize_indicator) {
+               resize_indicator_show(view);
+       }
 }
 
 /* Returns true if view was snapped to any edge */
@@ -172,6 +176,7 @@ interactive_finish(struct view *view)
                                }
                        }
                }
+               resize_indicator_hide(view);
 
                view->server->input_mode = LAB_INPUT_STATE_PASSTHROUGH;
                view->server->grabbed_view = NULL;
@@ -190,6 +195,7 @@ void
 interactive_cancel(struct view *view)
 {
        if (view->server->grabbed_view == view) {
+               resize_indicator_hide(view);
                view->server->input_mode = LAB_INPUT_STATE_PASSTHROUGH;
                view->server->grabbed_view = NULL;
                /* Update focus/cursor image */
index c781f53d4b7eababa1caa81cf51de625d4837855..227b641fc7ce049a5b346b61d593bb37a14bb486 100644 (file)
@@ -24,6 +24,7 @@
 #include "layers.h"
 #include "menu/menu.h"
 #include "regions.h"
+#include "resize_indicator.h"
 #include "theme.h"
 #include "view.h"
 #include "workspaces.h"
@@ -54,6 +55,7 @@ reload_config_and_theme(void)
        menu_reconfigure(g_server);
        seat_reconfigure(g_server);
        regions_reconfigure(g_server);
+       resize_indicator_reconfigure(g_server);
        kde_server_decoration_update_default();
 }
 
index 95395419255ba28a0e761970a993e4908490c0ee..b1a2a2fccaf76567f9fa2f38de3c19c783c2d01e 100644 (file)
@@ -1,4 +1,5 @@
 labwc_sources += files(
+  'resize_indicator.c',
   'ssd.c',
   'ssd_part.c',
   'ssd_titlebar.c',
diff --git a/src/ssd/resize_indicator.c b/src/ssd/resize_indicator.c
new file mode 100644 (file)
index 0000000..3a41323
--- /dev/null
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <assert.h>
+#include <wlr/types/wlr_scene.h>
+#include <wlr/util/box.h>
+#include <wlr/util/log.h>
+#include "common/scaled_font_buffer.h"
+#include "labwc.h"
+#include "resize_indicator.h"
+#include "view.h"
+
+static void
+resize_indicator_reconfigure_view(struct resize_indicator *indicator)
+{
+       assert(indicator->tree);
+
+       struct theme *theme = rc.theme;
+       indicator->height = font_height(&rc.font_osd)
+               + 2 * theme->osd_window_switcher_padding
+               + 2 * theme->osd_border_width;
+
+       /* Static positions */
+       wlr_scene_node_set_position(&indicator->background->node,
+               theme->osd_border_width, theme->osd_border_width);
+
+       wlr_scene_node_set_position(&indicator->text->scene_buffer->node,
+               theme->osd_border_width + theme->osd_window_switcher_padding,
+               theme->osd_border_width + theme->osd_window_switcher_padding);
+
+       /* Colors */
+       wlr_scene_rect_set_color(indicator->border, theme->osd_border_color);
+       wlr_scene_rect_set_color(indicator->background, theme->osd_bg_color);
+}
+
+static void
+resize_indicator_init(struct view *view)
+{
+       assert(view);
+       struct resize_indicator *indicator = &view->resize_indicator;
+       assert(!indicator->tree);
+
+       indicator->tree = wlr_scene_tree_create(view->scene_tree);
+       indicator->border = wlr_scene_rect_create(
+               indicator->tree, 0, 0, rc.theme->osd_border_color);
+       indicator->background = wlr_scene_rect_create(
+               indicator->tree, 0, 0, rc.theme->osd_bg_color);
+       indicator->text = scaled_font_buffer_create(indicator->tree);
+
+       wlr_scene_node_set_enabled(&indicator->tree->node, false);
+       resize_indicator_reconfigure_view(indicator);
+}
+
+static struct wlr_box
+get_size_hints(struct view *view)
+{
+       assert(view);
+
+       struct wlr_box hints = { 0 };
+       if (view->impl->fill_size_hints) {
+               view->impl->fill_size_hints(view, &hints);
+       }
+       return hints;
+}
+
+static bool
+wants_indicator(struct view *view)
+{
+       assert(view);
+
+       if (rc.resize_indicator == LAB_RESIZE_INDICATOR_NON_PIXEL) {
+               if (view->server->input_mode != LAB_INPUT_STATE_RESIZE) {
+                       return false;
+               }
+               struct wlr_box size_hints = get_size_hints(view);
+               if (size_hints.width && size_hints.height) {
+                       return true;
+               }
+       }
+       return rc.resize_indicator == LAB_RESIZE_INDICATOR_ALWAYS;
+}
+
+void
+resize_indicator_reconfigure(struct server *server)
+{
+       struct view *view;
+       wl_list_for_each(view, &server->views, link) {
+               struct resize_indicator *indicator = &view->resize_indicator;
+               if (indicator->tree) {
+                       resize_indicator_reconfigure_view(indicator);
+               }
+               if (view != server->grabbed_view) {
+                       continue;
+               }
+
+               /* This view is currently in an interactive move/resize operation */
+               if (indicator->tree && indicator->tree->node.enabled) {
+                       /* Indicator was active while reloading the config */
+                       if (wants_indicator(view)) {
+                               /* Apply new font setting */
+                               resize_indicator_update(view);
+                       } else {
+                               /* Indicator was disabled in config */
+                               resize_indicator_hide(view);
+                       }
+               } else if (wants_indicator(view)) {
+                       /* Indicator not yet active */
+                       resize_indicator_show(view);
+               }
+       }
+}
+
+static void
+resize_indicator_set_size(struct resize_indicator *indicator, int width)
+{
+       assert(indicator->tree);
+
+       /* We are not using a width-cache-early-out here to allow for theme changes */
+       indicator->width = width
+               + 2 * rc.theme->osd_window_switcher_padding
+               + 2 * rc.theme->osd_border_width;
+
+       wlr_scene_rect_set_size(indicator->border, indicator->width, indicator->height);
+       wlr_scene_rect_set_size(indicator->background,
+               indicator->width - 2 * rc.theme->osd_border_width,
+               indicator->height - 2 * rc.theme->osd_border_width);
+}
+
+void
+resize_indicator_show(struct view *view)
+{
+       assert(view);
+
+       if (!wants_indicator(view)) {
+               return;
+       }
+
+       struct resize_indicator *indicator = &view->resize_indicator;
+       if (!indicator->tree) {
+               /* Lazy initialize */
+               resize_indicator_init(view);
+       }
+
+       wlr_scene_node_raise_to_top(&indicator->tree->node);
+       wlr_scene_node_set_enabled(&indicator->tree->node, true);
+       resize_indicator_update(view);
+}
+
+void
+resize_indicator_update(struct view *view)
+{
+       assert(view);
+       assert(view == view->server->grabbed_view);
+
+       if (!wants_indicator(view)) {
+               return;
+       }
+
+       struct resize_indicator *indicator = &view->resize_indicator;
+       if (!indicator->tree) {
+               /*
+                * Future-proofs this routine:
+                *
+                * This can only happen when either src/interactive.c
+                * stops calling resize_indicator_show(), there is a
+                * bug in this file or resize_indicator_reconfigure()
+                * gets changed.
+                */
+               wlr_log(WLR_INFO, "Warning: resize_indicator has to use a fallback path");
+               resize_indicator_show(view);
+       }
+
+       char text[32]; /* 12345 x 12345 would be 13 chars + 1 null byte */
+
+       switch (view->server->input_mode) {
+       case LAB_INPUT_STATE_RESIZE:
+               ; /* works around "a label can only be part of a statement" */
+               struct wlr_box size_hints = get_size_hints(view);
+               snprintf(text, sizeof(text), "%d x %d",
+                       view->current.width / MAX(1, size_hints.width),
+                       view->current.height / MAX(1, size_hints.height));
+               break;
+       case LAB_INPUT_STATE_MOVE:
+               ; /* works around "a label can only be part of a statement" */
+               struct border margin = ssd_get_margin(view->ssd);
+               snprintf(text, sizeof(text), "%d , %d",
+                       view->current.x - margin.left,
+                       view->current.y - margin.top);
+               break;
+       default:
+               wlr_log(WLR_ERROR, "Invalid input mode for indicator update %u",
+                       view->server->input_mode);
+               return;
+       }
+
+       /* Let the indicator change width as required by the content */
+       int width = font_width(&rc.font_osd, text);
+
+       /* font_extents() adds 4 pixels to the calculated width */
+       width -= 4;
+
+       resize_indicator_set_size(indicator, width);
+
+       /* Center the indicator in the window */
+       wlr_scene_node_set_position(&indicator->tree->node,
+               (view->current.width - indicator->width) / 2,
+               (view->current.height - indicator->height) / 2);
+
+       scaled_font_buffer_update(indicator->text, text, width, &rc.font_osd,
+               rc.theme->osd_label_text_color, NULL /* const char *arrow */);
+}
+
+void
+resize_indicator_hide(struct view *view)
+{
+       assert(view);
+
+       struct resize_indicator *indicator = &view->resize_indicator;
+       if (!indicator->tree) {
+               return;
+       }
+
+       wlr_scene_node_set_enabled(&indicator->tree->node, false);
+}
index 729cedb26f172deef6f37ed71bae774247f95648..d85723e48dc4b849942810e2a4ad2dfb179aa4e4 100644 (file)
@@ -7,6 +7,7 @@
 #include "labwc.h"
 #include "menu/menu.h"
 #include "regions.h"
+#include "resize_indicator.h"
 #include "ssd.h"
 #include "view.h"
 #include "window-rules.h"
@@ -182,6 +183,9 @@ view_moved(struct view *view)
        if (view->toplevel.handle) {
                foreign_toplevel_update_outputs(view);
        }
+       if (rc.resize_indicator && view->server->grabbed_view == view) {
+               resize_indicator_update(view);
+       }
 }
 
 void