From 339710e884cfbab810382a63fafb3cdab8ee51e9 Mon Sep 17 00:00:00 2001 From: Owen W. Taylor Date: Wed, 23 Jun 2010 15:08:38 -0400 Subject: [PATCH] Allow breaking out from maximization during a mouse resize A maximized window can't be resized from the screen edges (preserves Fitts law goodness for the application), but it's still possible to start a resize drag with alt-middle-button. Currently we just don't let the user resize the window, while showing drag feedback; it's more useful to let the user "break" out from the resize. This provides a fast way to get a window partially aligned with the screen edges - maximize, then alt-drag it out from one edge. Behavior choices in this patch: - You can drag out a window out of maximization in both directions - smaller and larger. This can be potentilaly useful in multihead. - Dragging a window in only one direction unmaximizes the window fully, rather than leaving it in a horizontally/vertically maximized state. This is done because the horizontally/vertically maximzed states don't have clear visual representation and can be confusing to the user. - If you drag back to the maximized state after breaking out, maximization is restored, but you can't maximize a window by dragging to the full size if it didn't start out that way. A new internal function meta_window_unmaximize_with_gravity() is added for implementing this; it's a hybrid of meta_window_unmaximize() and meta_window_resize_with_gravity(). https://bugzilla.gnome.org/show_bug.cgi?id=622517 --- src/core/display-private.h | 3 + src/core/display.c | 20 +++- src/core/window-private.h | 5 + src/core/window.c | 224 ++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 228 insertions(+), 24 deletions(-) diff --git a/src/core/display-private.h b/src/core/display-private.h index 19287f3..a19d1d8 100644 --- a/src/core/display-private.h +++ b/src/core/display-private.h @@ -170,6 +170,9 @@ struct _MetaDisplay guint grab_wireframe_active : 1; guint grab_was_cancelled : 1; /* Only used in wireframe mode */ guint grab_frame_action : 1; + /* During a resize operation, the directions in which we've broken + * out of the initial maximization state */ + guint grab_resize_unmaximize : 2; /* MetaMaximizeFlags */ MetaRectangle grab_wireframe_rect; MetaRectangle grab_wireframe_last_xor_rect; MetaRectangle grab_initial_window_pos; diff --git a/src/core/display.c b/src/core/display.c index 4c7b4c0..2122851 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -3353,6 +3353,7 @@ meta_display_begin_grab_op (MetaDisplay *display, #endif display->grab_was_cancelled = FALSE; display->grab_frame_action = frame_action; + display->grab_resize_unmaximize = 0; if (display->grab_resize_timeout_id) { @@ -3583,11 +3584,20 @@ meta_display_end_grab_op (MetaDisplay *display, display->grab_wireframe_rect.x, display->grab_wireframe_rect.y); if (meta_grab_op_is_resizing (display->grab_op)) - meta_window_resize_with_gravity (display->grab_window, - TRUE, - display->grab_wireframe_rect.width, - display->grab_wireframe_rect.height, - meta_resize_gravity_from_grab_op (display->grab_op)); + { + if (display->grab_resize_unmaximize != 0) + meta_window_unmaximize_with_gravity (display->grab_window, + display->grab_resize_unmaximize, + display->grab_wireframe_rect.width, + display->grab_wireframe_rect.height, + meta_resize_gravity_from_grab_op (display->grab_op)); + else + meta_window_resize_with_gravity (display->grab_window, + TRUE, + display->grab_wireframe_rect.width, + display->grab_wireframe_rect.height, + meta_resize_gravity_from_grab_op (display->grab_op)); + } } meta_window_calc_showing (display->grab_window); } diff --git a/src/core/window-private.h b/src/core/window-private.h index da3fc52..65143ce 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -412,6 +412,11 @@ void meta_window_maximize_internal (MetaWindow *window, MetaRectangle *saved_rect); void meta_window_unmaximize (MetaWindow *window, MetaMaximizeFlags directions); +void meta_window_unmaximize_with_gravity (MetaWindow *window, + MetaMaximizeFlags directions, + int new_width, + int new_height, + int gravity); void meta_window_make_above (MetaWindow *window); void meta_window_unmake_above (MetaWindow *window); void meta_window_shade (MetaWindow *window, diff --git a/src/core/window.c b/src/core/window.c index 9af5283..6438540 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -2666,9 +2666,11 @@ unmaximize_window_before_freeing (MetaWindow *window) } } -void -meta_window_unmaximize (MetaWindow *window, - MetaMaximizeFlags directions) +static void +meta_window_unmaximize_internal (MetaWindow *window, + MetaMaximizeFlags directions, + MetaRectangle *desired_rect, + int gravity) { /* At least one of the two directions ought to be set */ gboolean unmaximize_horizontally, unmaximize_vertically; @@ -2702,13 +2704,13 @@ meta_window_unmaximize (MetaWindow *window, meta_window_get_client_root_coords (window, &target_rect); if (unmaximize_horizontally) { - target_rect.x = window->saved_rect.x; - target_rect.width = window->saved_rect.width; + target_rect.x = desired_rect->x; + target_rect.width = desired_rect->width; } if (unmaximize_vertically) { - target_rect.y = window->saved_rect.y; - target_rect.height = window->saved_rect.height; + target_rect.y = desired_rect->y; + target_rect.height = desired_rect->height; } /* Window's size hints may have changed while maximized, making @@ -2727,12 +2729,13 @@ meta_window_unmaximize (MetaWindow *window, window->display->grab_anchor_window_pos = target_rect; } - meta_window_move_resize (window, - FALSE, - target_rect.x, - target_rect.y, - target_rect.width, - target_rect.height); + meta_window_move_resize_internal (window, + META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION, + gravity, + target_rect.x, + target_rect.y, + target_rect.width, + target_rect.height); /* Make sure user_rect is current. */ @@ -2749,6 +2752,36 @@ meta_window_unmaximize (MetaWindow *window, } void +meta_window_unmaximize (MetaWindow *window, + MetaMaximizeFlags directions) +{ + meta_window_unmaximize_internal (window, directions, &window->saved_rect, + NorthWestGravity); +} + +/* Like meta_window_unmaximize(), but instead of unmaximizing to the + * saved position, we give the new desired size, and the gravity that + * determines the positioning relationship between the area occupied + * maximized and the new are. The arguments are similar to + * meta_window_resize_with_gravity(). + */ +void +meta_window_unmaximize_with_gravity (MetaWindow *window, + MetaMaximizeFlags directions, + int new_width, + int new_height, + int gravity) +{ + MetaRectangle desired_rect; + + meta_window_get_position (window, &desired_rect.x, &desired_rect.y); + desired_rect.width = new_width; + desired_rect.height = new_height; + + meta_window_unmaximize_internal (window, directions, &desired_rect, gravity); +} + +void meta_window_make_above (MetaWindow *window) { window->wm_state_above = TRUE; @@ -7033,6 +7066,112 @@ update_resize_timeout (gpointer data) return FALSE; } +/* When resizing a maximized window by using alt-middle-drag (resizing + * with the grips or the menu for a maximized window is not enabled), + * the user can "break" out of the maximized state. This checks for + * that possibility. During such a break-out resize the user can also + * return to the previous maximization state by resizing back to near + * the original size. + */ +static MetaMaximizeFlags +check_resize_unmaximize(MetaWindow *window, + int dx, + int dy) +{ + int threshold; + MetaMaximizeFlags new_unmaximize; + +#define DRAG_THRESHOLD_TO_RESIZE_THRESHOLD_FACTOR 3 + + threshold = meta_ui_get_drag_threshold (window->screen->ui) * + DRAG_THRESHOLD_TO_RESIZE_THRESHOLD_FACTOR; + new_unmaximize = 0; + + if (window->maximized_horizontally || + (window->display->grab_resize_unmaximize & META_MAXIMIZE_HORIZONTAL) != 0) + { + int x_amount; + + /* We allow breaking out of maximization in either direction, to make + * the window larger than the monitor as well as smaller than the + * monitor. If we wanted to only allow resizing smaller than the + * monitor, we'd use - dx for NE/E/SE and dx for SW/W/NW. + */ + switch (window->display->grab_op) + { + case META_GRAB_OP_RESIZING_NE: + case META_GRAB_OP_KEYBOARD_RESIZING_NE: + case META_GRAB_OP_RESIZING_E: + case META_GRAB_OP_KEYBOARD_RESIZING_E: + case META_GRAB_OP_RESIZING_SE: + case META_GRAB_OP_KEYBOARD_RESIZING_SE: + case META_GRAB_OP_RESIZING_SW: + case META_GRAB_OP_KEYBOARD_RESIZING_SW: + case META_GRAB_OP_RESIZING_W: + case META_GRAB_OP_KEYBOARD_RESIZING_W: + case META_GRAB_OP_RESIZING_NW: + case META_GRAB_OP_KEYBOARD_RESIZING_NW: + x_amount = dx < 0 ? - dx : dx; + break; + default: + x_amount = 0; + break; + } + + if (x_amount > threshold) + new_unmaximize |= META_MAXIMIZE_HORIZONTAL; + } + + if (window->maximized_vertically || + (window->display->grab_resize_unmaximize & META_MAXIMIZE_VERTICAL) != 0) + { + int y_amount; + + switch (window->display->grab_op) + { + case META_GRAB_OP_RESIZING_N: + case META_GRAB_OP_KEYBOARD_RESIZING_N: + case META_GRAB_OP_RESIZING_NE: + case META_GRAB_OP_KEYBOARD_RESIZING_NE: + case META_GRAB_OP_RESIZING_NW: + case META_GRAB_OP_KEYBOARD_RESIZING_NW: + case META_GRAB_OP_RESIZING_SE: + case META_GRAB_OP_KEYBOARD_RESIZING_SE: + case META_GRAB_OP_RESIZING_S: + case META_GRAB_OP_KEYBOARD_RESIZING_S: + case META_GRAB_OP_RESIZING_SW: + case META_GRAB_OP_KEYBOARD_RESIZING_SW: + y_amount = dy < 0 ? - dy : dy; + break; + default: + y_amount = 0; + break; + } + + if (y_amount > threshold) + new_unmaximize |= META_MAXIMIZE_VERTICAL; + } + + /* Metacity doesn't have a full user interface for only horizontally or + * vertically maximized, so while only unmaximizing in the direction drags + * has some advantages, it will also confuse the user. So, we always + * unmaximize both ways if possible. + */ + if (new_unmaximize != 0) + { + new_unmaximize = 0; + + if (window->maximized_horizontally || + (window->display->grab_resize_unmaximize & META_MAXIMIZE_HORIZONTAL) != 0) + new_unmaximize |= META_MAXIMIZE_HORIZONTAL; + if (window->maximized_vertically || + (window->display->grab_resize_unmaximize & META_MAXIMIZE_VERTICAL) != 0) + new_unmaximize |= META_MAXIMIZE_VERTICAL; + } + + return new_unmaximize; +} + static void update_resize (MetaWindow *window, gboolean snap, @@ -7045,6 +7184,7 @@ update_resize (MetaWindow *window, MetaRectangle old; int new_x, new_y; double remaining; + MetaMaximizeFlags new_unmaximize; window->display->grab_latest_motion_x = x; window->display->grab_latest_motion_y = y; @@ -7109,7 +7249,9 @@ update_resize (MetaWindow *window, meta_window_update_keyboard_resize (window, TRUE); } } - + + new_unmaximize = check_resize_unmaximize (window, dx, dy); + /* FIXME: This stupidity only needed because of wireframe mode and * the fact that wireframe isn't making use of * meta_rectangle_resize_with_gravity(). If we were to use that, we @@ -7233,6 +7375,8 @@ update_resize (MetaWindow *window, if (window->display->grab_wireframe_active) { + MetaRectangle root_coords; + if ((new_x + new_w <= new_x) || (new_y + new_h <= new_y)) return; @@ -7242,18 +7386,60 @@ update_resize (MetaWindow *window, * wireframe, but still resize it; however, that probably * confuses broken clients that have problems with opaque * resize, they probably don't track their visibility. + * + * We handle the basic constraints on maximized windows here + * to give the user feedback. */ + + if (window->maximized_horizontally && (new_unmaximize & META_MAXIMIZE_HORIZONTAL) == 0) + { + meta_window_get_client_root_coords (window, &root_coords); + new_x = root_coords.x; + new_w = root_coords.width; + } + if (window->maximized_vertically && (new_unmaximize & META_MAXIMIZE_VERTICAL) == 0) + { + meta_window_get_client_root_coords (window, &root_coords); + new_y = root_coords.y; + new_h = root_coords.height; + } + meta_window_update_wireframe (window, new_x, new_y, new_w, new_h); } else { - /* We don't need to update unless the specified width and height - * are actually different from what we had before. - */ - if (old.width != new_w || old.height != new_h) - meta_window_resize_with_gravity (window, TRUE, new_w, new_h, gravity); + if (new_unmaximize == window->display->grab_resize_unmaximize) + { + /* We don't need to update unless the specified width and height + * are actually different from what we had before. + */ + if (old.width != new_w || old.height != new_h) + { + if ((window->display->grab_resize_unmaximize == new_unmaximize)) + meta_window_resize_with_gravity (window, TRUE, new_w, new_h, gravity); + } + } + else + { + if ((new_unmaximize & ~window->display->grab_resize_unmaximize) != 0) + { + meta_window_unmaximize_with_gravity (window, + (new_unmaximize & ~window->display->grab_resize_unmaximize), + new_w, new_h, gravity); + } + + if ((window->display->grab_resize_unmaximize & ~new_unmaximize)) + { + MetaRectangle saved_rect = window->saved_rect; + meta_window_maximize (window, + (window->display->grab_resize_unmaximize & ~new_unmaximize)); + window->saved_rect = saved_rect; + } + } } + window->display->grab_resize_unmaximize = new_unmaximize; + /* Store the latest resize time, if we actually resized. */ if (window->rect.width != old.width || window->rect.height != old.height) g_get_current_time (&window->display->grab_last_moveresize_time); -- 1.7.0.1