summaryrefslogtreecommitdiff
path: root/src/core/window.c
diff options
context:
space:
mode:
authorVictor Kareh <[email protected]>2026-03-24 13:53:52 -0400
committerVictor Kareh <[email protected]>2026-03-24 13:53:52 -0400
commit1426be28971e8e05a082b5431f804b863a317a3e (patch)
tree882969ffc1bb72d51b250b6ea8136673785f6192 /src/core/window.c
parent4974ffb4b16bed4bd8e98fd52484cb2a2cfb095e (diff)
downloadmarco-double-tile-resize.tar.bz2
marco-double-tile-resize.tar.xz
window: Resize adjacent tiled windows when dragging shared edgedouble-tile-resize
When two windows are tiled side-by-side or in complementary quarter-tile positions, dragging the shared inside edge now resizes both windows simultaneously. Fixes #615
Diffstat (limited to 'src/core/window.c')
-rw-r--r--src/core/window.c169
1 files changed, 169 insertions, 0 deletions
diff --git a/src/core/window.c b/src/core/window.c
index cdf7ab3b..c877c12e 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -7872,6 +7872,34 @@ update_resize (MetaWindow *window,
gravity = meta_resize_gravity_from_grab_op (window->display->grab_op);
g_assert (gravity >= 0);
+ /* Clamp resize to respect the minimum size of the matched window */
+ if (window->display->grab_tile_match != NULL)
+ {
+ MetaWindow *match = window->display->grab_tile_match;
+ MetaRectangle work_area;
+ MetaFrameBorders borders;
+ int max_primary_width;
+ int primary_borders_w = 0;
+ int match_borders_w = 0;
+
+ meta_window_get_work_area_for_xinerama (window,
+ window->tile_monitor_number,
+ &work_area);
+
+ /* Use visible borders only since invisible borders overlap between
+ * adjacent tiled windows and don't consume work area space */
+ meta_frame_calc_borders (window->frame, &borders);
+ primary_borders_w = borders.visible.left + borders.visible.right;
+
+ meta_frame_calc_borders (match->frame, &borders);
+ match_borders_w = borders.visible.left + borders.visible.right;
+
+ max_primary_width = work_area.width - primary_borders_w -
+ match->size_hints.min_width - match_borders_w;
+ if (new_w > max_primary_width)
+ new_w = max_primary_width;
+ }
+
/* Do any edge resistance/snapping */
meta_window_edge_resistance_for_resize (window,
old.width,
@@ -7906,6 +7934,61 @@ update_resize (MetaWindow *window,
meta_window_resize_with_gravity (window, TRUE, new_w, new_h, gravity);
}
+ /* Resize matching tiled window */
+ if (window->display->grab_tile_match != NULL && !window->display->grab_wireframe_active)
+ {
+ MetaWindow *match = window->display->grab_tile_match;
+
+ /* Validate match is still valid */
+ if (!match->tiled || match->minimized || match->workspace != window->workspace)
+ {
+ window->display->grab_tile_match = NULL;
+ }
+ else
+ {
+ MetaRectangle work_area;
+ MetaFrameBorders borders;
+ int match_gravity;
+ int match_width;
+ int primary_borders_w = 0;
+ int match_borders_w = 0;
+
+ meta_window_get_work_area_for_xinerama (window,
+ window->tile_monitor_number,
+ &work_area);
+
+ /* Use visible borders only since invisible borders overlap between
+ * adjacent tiled windows and don't consume work area space */
+ meta_frame_calc_borders (window->frame, &borders);
+ primary_borders_w = borders.visible.left + borders.visible.right;
+
+ meta_frame_calc_borders (match->frame, &borders);
+ match_borders_w = borders.visible.left + borders.visible.right;
+
+ /* Match client width */
+ match_width = work_area.width - window->rect.width - primary_borders_w - match_borders_w;
+
+ /* Determine what gravity to use regardless of
+ * whether it's corner tiled or edge tiled. */
+ if (match->tile_mode == META_TILE_LEFT ||
+ match->tile_mode == META_TILE_TOP_LEFT ||
+ match->tile_mode == META_TILE_BOTTOM_LEFT)
+ match_gravity = WestGravity;
+ else
+ match_gravity = EastGravity;
+
+ /* Do the actual resize of the matching window */
+ if (match_width >= match->size_hints.min_width)
+ {
+ match->tile_resized = TRUE;
+ meta_window_resize_with_gravity (match, TRUE,
+ match_width,
+ match->rect.height, /* keep original height */
+ match_gravity);
+ }
+ }
+ }
+
/* Store the latest resize time, if we actually resized. */
if (window->rect.width != old.width || window->rect.height != old.height)
window->display->grab_last_moveresize_time = g_get_real_time ();
@@ -8324,6 +8407,92 @@ meta_window_get_current_tile_area (MetaWindow *window,
tile_area->width = width;
}
+MetaWindow*
+meta_window_find_tile_match (MetaWindow *window)
+{
+ MetaTileMode opposite_mode;
+ GList *windows, *l;
+
+ if (!window->tiled || !window->frame)
+ return NULL;
+
+ switch (window->tile_mode)
+ {
+ case META_TILE_LEFT:
+ opposite_mode = META_TILE_RIGHT;
+ break;
+ case META_TILE_RIGHT:
+ opposite_mode = META_TILE_LEFT;
+ break;
+ case META_TILE_TOP_LEFT:
+ opposite_mode = META_TILE_TOP_RIGHT;
+ break;
+ case META_TILE_TOP_RIGHT:
+ opposite_mode = META_TILE_TOP_LEFT;
+ break;
+ case META_TILE_BOTTOM_LEFT:
+ opposite_mode = META_TILE_BOTTOM_RIGHT;
+ break;
+ case META_TILE_BOTTOM_RIGHT:
+ opposite_mode = META_TILE_BOTTOM_LEFT;
+ break;
+ default:
+ return NULL;
+ }
+
+ /* Use stack list and iterate backwards since meta_stack_list_windows returns
+ * bottom-to-top order. We want to match the most likely visible window */
+ windows = meta_stack_list_windows (window->screen->stack, window->workspace);
+
+ for (l = g_list_last (windows); l != NULL; l = l->prev)
+ {
+ MetaWindow *candidate = l->data;
+
+ /* Skip itself, non-tiled windows, and windows without frames (e.g. docks, etc.) */
+ if (candidate == window || !candidate->tiled || !candidate->frame)
+ continue;
+
+ /* Skip windows that don't match the opposite tile mode */
+ if (candidate->tile_mode != opposite_mode)
+ continue;
+
+ /* Skip windows on different monitors */
+ if (candidate->tile_monitor_number != window->tile_monitor_number)
+ continue;
+
+ /* Skip windows not visible in the current workspace (e.g. minimized) */
+ if (!meta_window_showing_on_its_workspace (candidate))
+ continue;
+
+ /* Since windows can be tiled at different sizes, we verify that their
+ * edges touch (or at least are close enough) */
+ MetaRectangle win_rect, cand_rect;
+ int tolerance = 8; /* pixels */
+
+ meta_window_get_outer_rect (window, &win_rect);
+ meta_window_get_outer_rect (candidate, &cand_rect);
+
+ if (window->tile_mode == META_TILE_LEFT ||
+ window->tile_mode == META_TILE_TOP_LEFT ||
+ window->tile_mode == META_TILE_BOTTOM_LEFT)
+ {
+ if (ABS (win_rect.x + win_rect.width - cand_rect.x) > tolerance)
+ continue;
+ }
+ else
+ {
+ if (ABS (cand_rect.x + cand_rect.width - win_rect.x) > tolerance)
+ continue;
+ }
+
+ g_list_free (windows);
+ return candidate;
+ }
+
+ g_list_free (windows);
+ return NULL;
+}
+
gboolean
meta_window_same_application (MetaWindow *window,
MetaWindow *other_window)