summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/display-private.h1
-rw-r--r--src/core/display.c18
-rw-r--r--src/core/window-private.h2
-rw-r--r--src/core/window.c169
4 files changed, 190 insertions, 0 deletions
diff --git a/src/core/display-private.h b/src/core/display-private.h
index 70e3ce0d..f2e0f88e 100644
--- a/src/core/display-private.h
+++ b/src/core/display-private.h
@@ -192,6 +192,7 @@ struct _MetaDisplay {
int grab_wireframe_last_display_height;
GList* grab_old_window_stacking;
MetaEdgeResistanceData* grab_edge_resistance_data;
+ MetaWindow* grab_tile_match; /* Matching tiled window used for simultaneous resizing */
/* we use property updates as sentinels for certain window focus events
* to avoid some race conditions on EnterNotify events
diff --git a/src/core/display.c b/src/core/display.c
index ac63a3c6..4d1d9b7b 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -3666,6 +3666,22 @@ meta_display_begin_grab_op (MetaDisplay *display,
display->grab_tile_mode = META_TILE_NONE;
display->grab_tile_monitor_number = -1;
}
+
+ /* Find whether there's a tiled window that matches the one we're resizing. */
+ display->grab_tile_match = NULL;
+ if (window && meta_grab_op_is_resizing (op) && META_WINDOW_TILED (window))
+ {
+ int gravity = meta_resize_gravity_from_grab_op (op);
+
+ if ((window->tile_mode == META_TILE_LEFT && gravity == WestGravity) ||
+ (window->tile_mode == META_TILE_RIGHT && gravity == EastGravity) ||
+ (window->tile_mode == META_TILE_TOP_LEFT && (gravity == WestGravity || gravity == NorthWestGravity)) ||
+ (window->tile_mode == META_TILE_TOP_RIGHT && (gravity == EastGravity || gravity == NorthEastGravity)) ||
+ (window->tile_mode == META_TILE_BOTTOM_LEFT && (gravity == WestGravity || gravity == SouthWestGravity)) ||
+ (window->tile_mode == META_TILE_BOTTOM_RIGHT && (gravity == EastGravity || gravity == SouthEastGravity)))
+ display->grab_tile_match = meta_window_find_tile_match (window);
+ }
+
display->grab_anchor_root_x = root_x;
display->grab_anchor_root_y = root_y;
display->grab_latest_motion_x = root_x;
@@ -3846,6 +3862,8 @@ meta_display_end_grab_op (MetaDisplay *display,
if (display->grab_op == META_GRAB_OP_NONE)
return;
+ display->grab_tile_match = NULL;
+
if (display->grab_window != NULL)
display->grab_window->shaken_loose = FALSE;
diff --git a/src/core/window-private.h b/src/core/window-private.h
index 8ce9a121..83d0d6ae 100644
--- a/src/core/window-private.h
+++ b/src/core/window-private.h
@@ -642,6 +642,8 @@ void meta_window_get_work_area_all_xineramas (MetaWindow *window,
void meta_window_get_current_tile_area (MetaWindow *window,
MetaRectangle *tile_area);
+MetaWindow* meta_window_find_tile_match (MetaWindow *window);
+
gboolean meta_window_same_application (MetaWindow *window,
MetaWindow *other_window);
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)