]> git.mdlowis.com Git - proto/labwc.git/commitdiff
interactive: allow moving horizontally/vertically maximized window
authortokyo4j <hrak1529@gmail.com>
Wed, 7 Aug 2024 00:17:25 +0000 (09:17 +0900)
committerConsolatis <35009135+Consolatis@users.noreply.github.com>
Mon, 26 Aug 2024 18:30:22 +0000 (20:30 +0200)
Applies drag resistance unidirectionally for horizontally/vertically
maximized windows, allowing them to be dragged without being untiled
immediately. When the distance of cursor movement orthogonal to the
maximized direction exceeds <resistance><unMaximizeThreshold>.

While dragging a horizontally/vertically maximized window, edge/region
snapping is disabled to prevent unintentional snapping and overlays.

This commit also includes some refactoring to simplify the logic.

docs/labwc-config.5.scd
docs/rc.xml.all
include/config/rcxml.h
include/labwc.h
include/resistance.h
src/config/rcxml.c
src/input/cursor.c
src/interactive.c
src/regions.c
src/resistance.c
src/xdg.c

index 7436fc5f897f6900aeb0a2ead6aeaae5ced952da..e2c84116b8a6ab5acfd79a2a95a9cf4eb64bf090 100644 (file)
@@ -330,6 +330,11 @@ this is for compatibility with Openbox.
        Sets the movement of cursor in pixel required for a tiled or maximized
        window to be moved with an interactive move. Default is 20.
 
+*<resistance><unMaximizeThreshold>*
+       Sets the one-dimentional movement of cursor in pixel required for a
+       *vertically or horizontally* maximized window to be moved with an
+       interactive move. Default is 150.
+
 ## FOCUS
 
 *<focus><followMouse>* [yes|no]
index f7bba9988b77dd37e6baa967609b1a9cef4b1902..cb95ac711ff6bd920c161c353bf61714e5b9fc45 100644 (file)
   <resistance>
     <screenEdgeStrength>20</screenEdgeStrength>
     <windowEdgeStrength>20</windowEdgeStrength>
+    <!-- resistance for maximized/tiled windows -->
     <unSnapThreshold>20</unSnapThreshold>
+    <!-- resistance for vertically/horizontally maximized windows -->
+    <unMaximizeThreshold>150</unMaximizeThreshold>
   </resistance>
 
   <resize>
index cb56d4732ebab83b98a093d490522af10a5ac302..80ce4df74b29ea081bcd512c9abbf8ba9a6ce75f 100644 (file)
@@ -135,6 +135,7 @@ struct rcxml {
        int screen_edge_strength;
        int window_edge_strength;
        int unsnap_threshold;
+       int unmaximize_threshold;
 
        /* window snapping */
        int snap_edge_range;
index 810fed13a50a1ea33eeef38389537dac37a1daa7..93cb4038f7c082545134585973669615d253c281 100644 (file)
@@ -263,13 +263,6 @@ struct server {
        /* cursor interactive */
        enum input_mode input_mode;
        struct view *grabbed_view;
-       /*
-        * When an interactive move is requested for tiled/maximized views by CSD
-        * clients or by Drag actions, the actual motion and untiling of the view
-        * can be delayed to prevent the view from being unintentionally untiled.
-        * During this delay, move_pending is set.
-        */
-       bool move_pending;
        /* Cursor position when interactive move/resize is requested */
        double grab_x, grab_y;
        /* View geometry when interactive move/resize is requested */
@@ -506,26 +499,15 @@ void seat_reset_pressed(struct seat *seat);
 void seat_output_layout_changed(struct seat *seat);
 
 /**
- * interactive_anchor_to_cursor() - repositions the view to remain
+ * interactive_anchor_to_cursor() - repositions the geometry to remain
  * underneath the cursor when its size changes during interactive move.
+ * This function also resizes server->grab_box and repositions it to remain
+ * underneath server->grab_{x,y}.
  *
- * geometry->{width,height} are provided by the caller.
- * geometry->{x,y} are computed by this function.
- *
- * @note When <unSnapThreshold> is non-zero, cursor_x/y should be the original
- *       cursor position when the button was pressed.
- */
-void interactive_anchor_to_cursor(struct view *view, struct wlr_box *geometry,
-       int cursor_x, int cursor_y);
-
-/**
- * interactive_move_tiled_view_to() - Un-tile the tiled/maximized view at the
- * start of an interactive move or when an interactive move is pending.
- * Returns true if the distance of cursor motion exceeds the value of
- * <resistance><unSnapThreshold> and the view is un-tiled.
+ * geo->{width,height} are provided by the caller.
+ * geo->{x,y} are computed by this function.
  */
-bool interactive_move_tiled_view_to(struct server *server, struct view *view,
-       struct wlr_box *geometry);
+void interactive_anchor_to_cursor(struct server *server, struct wlr_box *geo);
 
 void interactive_begin(struct view *view, enum input_mode mode, uint32_t edges);
 void interactive_finish(struct view *view);
index 65b148699cd9ade04b0eb4a5743ef03da66ae153..6a155cebb4a344957c69c2e33c1bf2278d4c43bf 100644 (file)
@@ -3,7 +3,12 @@
 #define LABWC_RESISTANCE_H
 #include "labwc.h"
 
-void resistance_move_apply(struct view *view, double *x, double *y);
+/**
+ * resistance_unsnap_apply() - Apply resistance when dragging a
+ * maximized/tiled window. Returns true when the view needs to be un-tiled.
+ */
+bool resistance_unsnap_apply(struct view *view, int *x, int *y);
+void resistance_move_apply(struct view *view, int *x, int *y);
 void resistance_resize_apply(struct view *view, struct wlr_box *new_view_geo);
 
 #endif /* LABWC_RESISTANCE_H */
index d6ce92b6493eb72ac265df9ae297a8c6ff0c5fc8..007ae160c6286c5e3676ec0a2294c32dd40189a0 100644 (file)
@@ -1043,6 +1043,8 @@ entry(xmlNode *node, char *nodename, char *content)
                rc.window_edge_strength = atoi(content);
        } else if (!strcasecmp(nodename, "unSnapThreshold.resistance")) {
                rc.unsnap_threshold = atoi(content);
+       } else if (!strcasecmp(nodename, "unMaximizeThreshold.resistance")) {
+               rc.unmaximize_threshold = atoi(content);
        } else if (!strcasecmp(nodename, "range.snapping")) {
                rc.snap_edge_range = atoi(content);
        } else if (!strcasecmp(nodename, "enabled.overlay.snapping")) {
@@ -1370,6 +1372,7 @@ rcxml_init(void)
        rc.screen_edge_strength = 20;
        rc.window_edge_strength = 20;
        rc.unsnap_threshold = 20;
+       rc.unmaximize_threshold = 150;
 
        rc.snap_edge_range = 1;
        rc.snap_overlay_enabled = true;
index cd28cf4429858bdf8d299b2508105524c7a5c7a5..4b76d75ab4c227d1f8861f19542886f92bfd340d 100644 (file)
@@ -235,24 +235,34 @@ process_cursor_move(struct server *server, uint32_t time)
 {
        struct view *view = server->grabbed_view;
 
-       /*
-        * Un-tile the view when interactive move is delayed and the distance
-        * of cursor movement exceeds <resistance><unSnapThreshold>.
-        */
-       if (server->move_pending && !interactive_move_tiled_view_to(
-                       server, server->grabbed_view, &server->grab_box)) {
-               return;
-       }
+       int x = server->grab_box.x + (server->seat.cursor->x - server->grab_x);
+       int y = server->grab_box.y + (server->seat.cursor->y - server->grab_y);
 
-       double dx = server->seat.cursor->x - server->grab_x;
-       double dy = server->seat.cursor->y - server->grab_y;
+       /* Apply resistance for maximized/tiled view */
+       bool needs_untile = resistance_unsnap_apply(view, &x, &y);
+       if (needs_untile) {
+               /*
+                * When the view needs to be un-tiled, resize it to natural
+                * geometry while anchoring it to cursor. If the natural
+                * geometry is unknown (possible with xdg-shell views), then
+                * we set a size of 0x0 here and determine the correct geometry
+                * later. See do_late_positioning() in xdg.c.
+                */
+               struct wlr_box new_geo = {
+                       .width = view->natural_geometry.width,
+                       .height = view->natural_geometry.height,
+               };
+               interactive_anchor_to_cursor(server, &new_geo);
+               view_set_untiled(view);
+               view_restore_to(view, new_geo);
+               x = new_geo.x;
+               y = new_geo.y;
+       }
 
-       /* Move the grabbed view to the new position. */
-       dx += server->grab_box.x;
-       dy += server->grab_box.y;
-       resistance_move_apply(view, &dx, &dy);
-       view_move(view, dx, dy);
+       /* Then apply window & edge resistance */
+       resistance_move_apply(view, &x, &y);
 
+       view_move(view, x, y);
        overlay_update(&server->seat);
 }
 
index 570d80fc6f05b72307587985259b823ac4bfc9d4..851e2231ee75641ef349706a3ef9cd0ba6d584fe 100644 (file)
@@ -9,58 +9,47 @@
 #include "view.h"
 #include "window-rules.h"
 
+/*
+ *   pos_old  pos_cursor
+ *      v         v
+ *      +---------+-------------------+
+ *      <-----------size_old---------->
+ *
+ *      return value
+ *           v
+ *           +----+---------+
+ *           <---size_new--->
+ */
 static int
-max_move_scale(double pos_cursor, double pos_current,
-       double size_current, double size_orig)
+max_move_scale(double pos_cursor, double pos_old, double size_old,
+               double size_new)
 {
-       double anchor_frac = (pos_cursor - pos_current) / size_current;
-       int pos_new = pos_cursor - (size_orig * anchor_frac);
-       if (pos_new < pos_current) {
+       double anchor_frac = (pos_cursor - pos_old) / size_old;
+       int pos_new = pos_cursor - (size_new * anchor_frac);
+       if (pos_new < pos_old) {
                /* Clamp by using the old offsets of the maximized window */
-               pos_new = pos_current;
+               pos_new = pos_old;
        }
        return pos_new;
 }
 
 void
-interactive_anchor_to_cursor(struct view *view, struct wlr_box *geometry,
-               int cursor_x, int cursor_y)
-{
-       geometry->x = max_move_scale(cursor_x, view->current.x,
-               view->current.width, geometry->width);
-       geometry->y = max_move_scale(cursor_y, view->current.y,
-               view->current.height, geometry->height);
-}
-
-bool
-interactive_move_tiled_view_to(struct server *server, struct view *view,
-               struct wlr_box *geometry)
+interactive_anchor_to_cursor(struct server *server, struct wlr_box *geo)
 {
-       assert(!view_is_floating(view));
-
-       int threshold = rc.unsnap_threshold;
-
-       if (server->input_mode == LAB_INPUT_STATE_MOVE) {
-               /* When called from cursor motion handler */
-               assert(server->move_pending && server->grabbed_view == view);
-
-               double dx = server->seat.cursor->x - server->grab_x;
-               double dy = server->seat.cursor->y - server->grab_y;
-               if (dx * dx + dy * dy < threshold * threshold) {
-                       return false;
-               }
-       } else {
-               /* When called from interactive_begin() */
-               if (threshold > 0) {
-                       return false;
-               }
+       assert(server->input_mode == LAB_INPUT_STATE_MOVE);
+       if (wlr_box_empty(geo)) {
+               return;
        }
-
-       view_set_untiled(view);
-       view_restore_to(view, *geometry);
-       server->move_pending = false;
-
-       return true;
+       /* Resize grab_box while anchoring it to grab_box.{x,y} */
+       server->grab_box.x = max_move_scale(server->grab_x, server->grab_box.x,
+               server->grab_box.width, geo->width);
+       server->grab_box.y = max_move_scale(server->grab_y, server->grab_box.y,
+               server->grab_box.height, geo->height);
+       server->grab_box.width = geo->width;
+       server->grab_box.height = geo->height;
+
+       geo->x = server->grab_box.x + (server->seat.cursor->x - server->grab_x);
+       geo->y = server->grab_box.y + (server->seat.cursor->y - server->grab_y);
 }
 
 void
@@ -73,7 +62,6 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
         */
        struct server *server = view->server;
        struct seat *seat = &server->seat;
-       struct wlr_box geometry = view->current;
 
        if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
                return;
@@ -97,31 +85,7 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
                         */
                        return;
                }
-               if (!view_is_floating(view)) {
-                       /*
-                        * Un-maximize and restore natural width/height.
-                        * If the natural geometry is unknown (possible
-                        * with xdg-shell views), then we set a size of
-                        * 0x0 here and determine the correct geometry
-                        * later. See do_late_positioning() in xdg.c.
-                        */
-                       geometry.width = view->natural_geometry.width;
-                       geometry.height = view->natural_geometry.height;
-                       if (!wlr_box_empty(&geometry)) {
-                               interactive_anchor_to_cursor(view, &geometry,
-                                       seat->cursor->x, seat->cursor->y);
-                       }
-
-                       /*
-                        * If <resistance><unSnapThreshold> is non-zero, the
-                        * tiled/maximized view is un-tiled later in cursor
-                        * motion handler.
-                        */
-                       if (!interactive_move_tiled_view_to(
-                                       server, view, &geometry)) {
-                               server->move_pending = true;
-                       }
-               } else {
+               if (view_is_floating(view)) {
                        /* Store natural geometry at start of move */
                        view_store_natural_geometry(view);
                        view_invalidate_last_layout_geometry(view);
@@ -151,7 +115,7 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
 
                /*
                 * If tiled or maximized in only one direction, reset
-                * tiled/maximized state but keep the same geometry as
+                * maximized/tiled state but keep the same geometry as
                 * the starting point for the resize.
                 */
                view_set_untiled(view);
@@ -168,8 +132,24 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
        /* Remember view and cursor positions at start of move/resize */
        server->grab_x = seat->cursor->x;
        server->grab_y = seat->cursor->y;
-       server->grab_box = geometry;
+       server->grab_box = view->current;
        server->resize_edges = edges;
+
+       /*
+        * Un-tile maximized/tiled view immediately if <unSnapThreshold> is
+        * zero. Otherwise, un-tile it later in cursor motion handler.
+        * If the natural geometry is unknown (possible with xdg-shell views),
+        * then we set a size of 0x0 here and determine the correct geometry
+        * later. See do_late_positioning() in xdg.c.
+        */
+       if (mode == LAB_INPUT_STATE_MOVE && !view_is_floating(view)
+                       && rc.unsnap_threshold <= 0) {
+               struct wlr_box natural_geo = view->natural_geometry;
+               interactive_anchor_to_cursor(server, &natural_geo);
+               view_set_untiled(view);
+               view_restore_to(view, natural_geo);
+       }
+
        if (rc.resize_indicator) {
                resize_indicator_show(view);
        }
@@ -181,6 +161,10 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
 enum view_edge
 edge_from_cursor(struct seat *seat, struct output **dest_output)
 {
+       if (!view_is_floating(seat->server->grabbed_view)) {
+               return VIEW_EDGE_INVALID;
+       }
+
        int snap_range = rc.snap_edge_range;
        if (!snap_range) {
                return VIEW_EDGE_INVALID;
@@ -296,7 +280,6 @@ interactive_cancel(struct view *view)
 
        view->server->input_mode = LAB_INPUT_STATE_PASSTHROUGH;
        view->server->grabbed_view = NULL;
-       view->server->move_pending = false;
 
        /* Update focus/cursor image */
        cursor_update_focus(view->server);
index 1b265fe32278112d14d12d330f2599f55c19984e..f52a56197d34226969b9dc20939eae989a2b5ad0 100644 (file)
@@ -20,7 +20,8 @@ regions_should_snap(struct server *server)
 {
        if (server->input_mode != LAB_INPUT_STATE_MOVE
                        || wl_list_empty(&rc.regions)
-                       || server->seat.region_prevent_snap) {
+                       || server->seat.region_prevent_snap
+                       || !view_is_floating(server->grabbed_view)) {
                return false;
        }
 
index ce2efbdd3e0be4a2ce7b51b5644ab816ad96a66e..a20f3b3c105697bf5e8280b8fddeda7dd24ce3e3 100644 (file)
@@ -91,8 +91,38 @@ check_edge_window(int *next, struct edge current, struct edge target,
                oppose, align, rc.window_edge_strength, lesser);
 }
 
+bool
+resistance_unsnap_apply(struct view *view, int *x, int *y)
+{
+       if (view_is_floating(view)) {
+               return false;
+       }
+
+       int dx = *x - view->current.x;
+       int dy = *y - view->current.y;
+       if (view->maximized == VIEW_AXIS_HORIZONTAL) {
+               if (abs(dx) < rc.unmaximize_threshold) {
+                       *x = view->current.x;
+                       return false;
+               }
+       } else if (view->maximized == VIEW_AXIS_VERTICAL) {
+               if (abs(dy) < rc.unmaximize_threshold) {
+                       *y = view->current.y;
+                       return false;
+               }
+       } else {
+               if (dx * dx + dy * dy < rc.unsnap_threshold * rc.unsnap_threshold) {
+                       *x = view->current.x;
+                       *y = view->current.y;
+                       return false;
+               }
+       }
+
+       return true;
+}
+
 void
-resistance_move_apply(struct view *view, double *x, double *y)
+resistance_move_apply(struct view *view, int *x, int *y)
 {
        assert(view);
 
index 23bceba25b8012a237b77f69535fe7b7c3050091..b73ff84d03ffcd9c65c7bb59b8c7cb21930f0f7b 100644 (file)
--- a/src/xdg.c
+++ b/src/xdg.c
@@ -93,14 +93,8 @@ do_late_positioning(struct view *view)
        struct server *server = view->server;
        if (server->input_mode == LAB_INPUT_STATE_MOVE
                        && view == server->grabbed_view) {
-               /* Anchor view to original grab position */
-               interactive_anchor_to_cursor(view, &view->pending,
-                       server->grab_x, server->grab_y);
-               /* Next update grab offsets */
-               server->grab_box = view->pending;
-               /* Finally, move by same distance cursor has moved */
-               view->pending.x += server->seat.cursor->x - server->grab_x;
-               view->pending.y += server->seat.cursor->y - server->grab_y;
+               /* Reposition the view while anchoring it to cursor */
+               interactive_anchor_to_cursor(server, &view->pending);
        } else {
                /* TODO: smart placement? */
                view_compute_centered_position(view, NULL,