]> git.mdlowis.com Git - proto/labwc.git/commitdiff
action: allow SnapToEdge to combine two cardinal directions
authortokyo4j <hrak1529@gmail.com>
Fri, 5 Sep 2025 03:14:52 +0000 (12:14 +0900)
committerHiroaki Yamamoto <hrak1529@gmail.com>
Fri, 19 Sep 2025 07:23:23 +0000 (16:23 +0900)
This patch adds `combine` argument to (Toggle)SnapToEdge actions.
This allows to snap a window to e.g. up-left by running two actions:
- `<action name="SnapToEdge" direction="left" combine="yes" />`
- `<action name="SnapToEdge" direction="up" combine="yes" />`

Then running `<action name="SnapToEdge" direction="down" combine="yes" />`
snaps it to left again. This behavior is almost the same as KWin, except
that snapping a up-right-tiled window to right doesn't move it to the
right-adjacent output, but makes it right-tiled first.

docs/labwc-actions.5.scd
include/view.h
src/action.c
src/interactive.c
src/view.c

index 4879873df496a5f349a4a225d739f54c70fc3069..4a76a374deabd960ab69f1cb009ff6465cbec11a 100644 (file)
@@ -92,11 +92,18 @@ Actions are used in menus and keyboard/mouse bindings.
        Move window relative to its current position. Positive value of x moves
        it right, negative left. Positive value of y moves it down, negative up.
 
-*<action name="ToggleSnapToEdge" direction="value" />*++
-*<action name="SnapToEdge" direction="value" />*
-       Resize window to fill half the output in the given direction. Supports
-       directions "left", "up", "right", "down", "up-left", "up-right", "down-left",
-       "down-right" and "center".
+*<action name="ToggleSnapToEdge" direction="value" combine="value" />*++
+*<action name="SnapToEdge" direction="value" combine="value" />*
+       Resize window to fill half or quarter the output in the given direction.
+
+       *direction* [up|down|left|right|up-left|up-right|down-left|down-right|center]
+               Direction in which to snap the window.
+
+       *combine* [yes|no]
+               Allows to snap a window to an output corner by combining two
+               directions. For example, snapping a window to *right* and then
+               to *up* places it in the *up-right* quarter of the output.
+               Default is no.
 
        ToggleSnapToEdge additionally toggles the active window between
        tiled to the given direction and its untiled position.
index 5ff709840fc2efe234e16b0dc644d33f2a47ad96..efdcf3fdbe1c5ec213e45345cae4dc56aa8a9d24 100644 (file)
@@ -548,7 +548,7 @@ void view_move_to_edge(struct view *view, enum lab_edge direction, bool snap_to_
 void view_grow_to_edge(struct view *view, enum lab_edge direction);
 void view_shrink_to_edge(struct view *view, enum lab_edge direction);
 void view_snap_to_edge(struct view *view, enum lab_edge direction,
-       bool across_outputs, bool store_natural_geometry);
+       bool across_outputs, bool combine, bool store_natural_geometry);
 void view_snap_to_region(struct view *view, struct region *region, bool store_natural_geometry);
 void view_move_to_output(struct view *view, struct output *output);
 
index 60c57b33b76530e288e70cccdb4893c509935029..ab9d3e1818e4be7990829a509653e99cf20b71f0 100644 (file)
@@ -337,11 +337,6 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
                }
                break;
        case ACTION_TYPE_MOVE_TO_EDGE:
-               if (!strcasecmp(argument, "snapWindows")) {
-                       action_arg_add_bool(action, argument, parse_bool(content, true));
-                       goto cleanup;
-               }
-               /* Falls through */
        case ACTION_TYPE_TOGGLE_SNAP_TO_EDGE:
        case ACTION_TYPE_SNAP_TO_EDGE:
        case ACTION_TYPE_GROW_TO_EDGE:
@@ -358,6 +353,17 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
                        }
                        goto cleanup;
                }
+               if (action->type == ACTION_TYPE_MOVE_TO_EDGE
+                               && !strcasecmp(argument, "snapWindows")) {
+                       action_arg_add_bool(action, argument, parse_bool(content, true));
+                       goto cleanup;
+               }
+               if ((action->type == ACTION_TYPE_SNAP_TO_EDGE
+                                       || action->type == ACTION_TYPE_TOGGLE_SNAP_TO_EDGE)
+                               && !strcasecmp(argument, "combine")) {
+                       action_arg_add_bool(action, argument, parse_bool(content, false));
+                       goto cleanup;
+               }
                break;
        case ACTION_TYPE_SHOW_MENU:
                if (!strcmp(argument, "menu")) {
@@ -1031,9 +1037,9 @@ run_action(struct view *view, struct server *server, struct action *action,
                                view_apply_natural_geometry(view);
                                break;
                        }
-                       view_snap_to_edge(view, edge,
-                               /*across_outputs*/ true,
-                               /*store_natural_geometry*/ true);
+                       bool combine = action_get_bool(action, "combine", false);
+                       view_snap_to_edge(view, edge, /*across_outputs*/ true,
+                               combine, /*store_natural_geometry*/ true);
                }
                break;
        case ACTION_TYPE_GROW_TO_EDGE:
index f9c7f7714bff3531d016a9a9bab485f5265eb6a4..8ca258c13d871426b736b822717dbd5f1332f455 100644 (file)
@@ -260,9 +260,8 @@ snap_to_edge(struct view *view)
                view_maximize(view, VIEW_AXIS_BOTH,
                        /*store_natural_geometry*/ false);
        } else {
-               view_snap_to_edge(view, edge,
-                       /*across_outputs*/ false,
-                       /*store_natural_geometry*/ false);
+               view_snap_to_edge(view, edge, /*across_outputs*/ false,
+                       /*combine*/ false, /*store_natural_geometry*/ false);
        }
 
        return true;
index 13b36a54fb3ad5a8bbb447c7c75848cc40490ba3..e402b6263c9e565c33018b0fe861fe09dc4f5e6e 100644 (file)
@@ -2126,7 +2126,7 @@ view_placement_parse(const char *policy)
 
 void
 view_snap_to_edge(struct view *view, enum lab_edge edge,
-                       bool across_outputs, bool store_natural_geometry)
+               bool across_outputs, bool combine, bool store_natural_geometry)
 {
        assert(view);
 
@@ -2142,15 +2142,45 @@ view_snap_to_edge(struct view *view, enum lab_edge edge,
 
        view_set_shade(view, false);
 
-       if (across_outputs && view->tiled == edge && view->maximized == VIEW_AXIS_NONE) {
-               /* We are already tiled for this edge; try to switch outputs */
-               output = output_get_adjacent(view->output, edge, /* wrap */ false);
-               if (!output) {
-                       return;
-               }
+       if (lab_edge_is_cardinal(edge) && view->maximized == VIEW_AXIS_NONE) {
+               enum lab_edge invert_edge = lab_edge_invert(edge);
+               /* Represents axis of snapping direction */
+               enum lab_edge parallel_mask = edge | invert_edge;
+               /*
+                * The vector view->tiled is split to components
+                * parallel/orthogonal to snapping direction. For example,
+                * view->tiled=TOP_LEFT is split to parallel_tiled=TOP and
+                * orthogonal_tiled=LEFT when edge=TOP or edge=BOTTOM.
+                */
+               enum lab_edge parallel_tiled = view->tiled & parallel_mask;
+               enum lab_edge orthogonal_tiled = view->tiled & ~parallel_mask;
 
-               /* When switching outputs, jump to the opposite edge */
-               edge = lab_edge_invert(edge);
+               if (across_outputs && view->tiled == edge) {
+                       /*
+                        * E.g. when window is tiled to up and being snapped
+                        * to up again, move it to the output above and tile
+                        * it to down.
+                        */
+                       output = output_get_adjacent(view->output, edge,
+                               /* wrap */ false);
+                       if (!output) {
+                               return;
+                       }
+                       edge = invert_edge;
+               } else if (combine && parallel_tiled == invert_edge
+                               && orthogonal_tiled != LAB_EDGE_NONE) {
+                       /*
+                        * E.g. when window is tiled to downleft/downright and
+                        * being snapped to up, tile it to left/right.
+                        */
+                       edge = view->tiled & ~parallel_mask;
+               } else if (combine && parallel_tiled == LAB_EDGE_NONE) {
+                       /*
+                        * E.g. when window is tiled to left/right and being
+                        * snapped to up, tile it to upleft/upright.
+                        */
+                       edge = view->tiled | edge;
+               }
        }
 
        if (view->maximized != VIEW_AXIS_NONE) {