diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd
index 4879873d..4a76a374 100644
--- a/docs/labwc-actions.5.scd
+++ b/docs/labwc-actions.5.scd
@@ -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.
-**++
-**
- 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".
+**++
+**
+ 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.
diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd
index a854d886..7ddf2d5b 100644
--- a/docs/labwc-config.5.scd
+++ b/docs/labwc-config.5.scd
@@ -727,7 +727,7 @@ extending outward from the snapped edge.
W-Return - lab-sensible-terminal
A-F4 - close window
W-a - toggle maximize
- W- - resize window to fill half the output
+ W- - resize window to fill half or quarter of the output
A-Space - show window menu
```
diff --git a/docs/rc.xml.all b/docs/rc.xml.all
index a96993ab..12d0c761 100644
--- a/docs/rc.xml.all
+++ b/docs/rc.xml.all
@@ -271,16 +271,16 @@
-
+
-
+
-
+
-
+
diff --git a/include/config/default-bindings.h b/include/config/default-bindings.h
index 08caeade..9d42d237 100644
--- a/include/config/default-bindings.h
+++ b/include/config/default-bindings.h
@@ -35,6 +35,10 @@ static struct key_combos {
.name = "direction",
.value = "left",
},
+ .attributes[1] = {
+ .name = "combine",
+ .value = "yes",
+ },
}, {
.binding = "W-Right",
.action = "SnapToEdge",
@@ -42,6 +46,10 @@ static struct key_combos {
.name = "direction",
.value = "right",
},
+ .attributes[1] = {
+ .name = "combine",
+ .value = "yes",
+ },
}, {
.binding = "W-Up",
.action = "SnapToEdge",
@@ -49,6 +57,10 @@ static struct key_combos {
.name = "direction",
.value = "up",
},
+ .attributes[1] = {
+ .name = "combine",
+ .value = "yes",
+ },
}, {
.binding = "W-Down",
.action = "SnapToEdge",
@@ -56,6 +68,10 @@ static struct key_combos {
.name = "direction",
.value = "down",
},
+ .attributes[1] = {
+ .name = "combine",
+ .value = "yes",
+ },
}, {
.binding = "A-Space",
.action = "ShowMenu",
diff --git a/include/view.h b/include/view.h
index 5ff70984..efdcf3fd 100644
--- a/include/view.h
+++ b/include/view.h
@@ -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);
diff --git a/src/action.c b/src/action.c
index 60c57b33..ab9d3e18 100644
--- a/src/action.c
+++ b/src/action.c
@@ -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:
diff --git a/src/interactive.c b/src/interactive.c
index f9c7f771..8ca258c1 100644
--- a/src/interactive.c
+++ b/src/interactive.c
@@ -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;
diff --git a/src/view.c b/src/view.c
index 97616cbc..e402b626 100644
--- a/src/view.c
+++ b/src/view.c
@@ -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,25 +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 (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;
- if (!output) {
+ if (across_outputs && view->tiled == edge) {
/*
- * No more output to move to
- *
- * We re-apply the tiled geometry without changing any
- * state because the window might have been moved away
- * (and thus got untiled) and then snapped back to the
- * original 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.
*/
- view_apply_tiled_geometry(view);
- return;
+ 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;
}
-
- /* When switching outputs, jump to the opposite edge */
- edge = lab_edge_invert(edge);
}
if (view->maximized != VIEW_AXIS_NONE) {
diff --git a/subprojects/libsfdo.wrap b/subprojects/libsfdo.wrap
index 22df1d6a..d05264db 100644
--- a/subprojects/libsfdo.wrap
+++ b/subprojects/libsfdo.wrap
@@ -1,6 +1,6 @@
[wrap-git]
url = https://gitlab.freedesktop.org/vyivel/libsfdo.git
-revision = v0.1.3
+revision = v0.1.4
[provide]
dependency_names = libsfdo-basedir, libsfdo-desktop, libsfdo-icon