[mutter] Allow breaking out from maximization during a mouse resize



commit 1c3f7c408822ec55da3ea3c93d2d3b676046f2f8
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Sat Sep 18 22:28:30 2010 +0200

    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().
    
    Port of the metacity patch from Owen Taylor in bug 622517.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=629931

 src/core/display-private.h |    3 +
 src/core/display.c         |    1 +
 src/core/window-private.h  |    5 +
 src/core/window.c          |  216 +++++++++++++++++++++++++++++++++++++++-----
 4 files changed, 201 insertions(+), 24 deletions(-)
---
diff --git a/src/core/display-private.h b/src/core/display-private.h
index dc55338..b7a79cb 100644
--- a/src/core/display-private.h
+++ b/src/core/display-private.h
@@ -185,6 +185,9 @@ struct _MetaDisplay
   guint       grab_have_pointer : 1;
   guint       grab_have_keyboard : 1;
   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_initial_window_pos;
   int         grab_initial_x, grab_initial_y;  /* These are only relevant for */
   gboolean    grab_threshold_movement_reached; /* raise_on_click == FALSE.    */
diff --git a/src/core/display.c b/src/core/display.c
index 8c6b70d..c87bf11 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -3610,6 +3610,7 @@ meta_display_begin_grab_op (MetaDisplay *display,
   display->grab_last_user_action_was_snap = FALSE;
 #endif
   display->grab_frame_action = frame_action;
+  display->grab_resize_unmaximize = 0;
 
   if (display->grab_resize_timeout_id)
     {
diff --git a/src/core/window-private.h b/src/core/window-private.h
index b2c903f..485a683 100644
--- a/src/core/window-private.h
+++ b/src/core/window-private.h
@@ -436,6 +436,11 @@ void        meta_window_queue              (MetaWindow  *window,
 void        meta_window_maximize_internal  (MetaWindow        *window,
                                             MetaMaximizeFlags  directions,
                                             MetaRectangle     *saved_rect);
+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 a3c1666..d90ccf4 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -3250,9 +3250,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)
 {
   gboolean unmaximize_horizontally, unmaximize_vertically;
 
@@ -3297,13 +3299,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
@@ -3317,12 +3319,13 @@ meta_window_unmaximize (MetaWindow        *window,
 
 	  meta_window_get_outer_rect (window, &old_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);
 
 	  meta_window_get_outer_rect (window, &new_rect);
           meta_compositor_unmaximize_window (window->display->compositor,
@@ -3332,12 +3335,13 @@ meta_window_unmaximize (MetaWindow        *window,
         }
       else
         {
-          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.
@@ -3368,6 +3372,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)
 {
   g_return_if_fail (!window->override_redirect);
@@ -7880,6 +7914,112 @@ update_move (MetaWindow  *window,
   meta_window_move (window, TRUE, new_x, new_y);
 }
 
+/* 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 gboolean
 update_resize_timeout (gpointer data)
 {
@@ -7904,6 +8044,7 @@ update_resize (MetaWindow *window,
   int gravity;
   MetaRectangle old;
   double remaining;
+  MetaMaximizeFlags new_unmaximize;
 
   window->display->grab_latest_motion_x = x;
   window->display->grab_latest_motion_y = y;
@@ -7965,6 +8106,8 @@ update_resize (MetaWindow *window,
         }
     }
 
+  new_unmaximize = check_resize_unmaximize (window, dx, dy);
+
   switch (window->display->grab_op)
     {
     case META_GRAB_OP_RESIZING_SE:
@@ -8075,11 +8218,36 @@ update_resize (MetaWindow *window,
                                           snap,
                                           FALSE);
 
-   /* 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)



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]