[mutter/wip/gbsneto/tiling-improvements: 1/9] window: Allow tiled windows be resized



commit b6af7db7cd1f0012a3a7feb695ac090ac0781de3
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Sun Jun 11 14:58:21 2017 -0300

    window: Allow tiled windows be resized
    
    The current code forbids resizing tiled windows, and enforces
    that side-by-side tiled windows cover half of the current
    screen's width.
    
    This patch removes this restriction, and adds code to track the
    tiled window size isolated from the actual window size, using the
    following heuristics:
    
     * If the window was not tiled, cover 1/2 of the screen.
     * If the window was tiled, use the complement of thecurrent width
       to cover the "hole" in the screen.
     * When resizing a tiled window to the oposite border, maximize the
       window.
    
    Windows with client-side decoration does not follow this behavior
    for the moment, and patches for GTK+ shall be crafted to fix this
    in the client side.
    
    This patch, however, breaks tiling a window by dragging them to
    the edge of the screen. This is a consequence of making the tile
    mode stateful, and will be fixed by a future patch splitting the
    tile mode into active and preview modes.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=645153

 src/core/keybindings.c    |   19 ++++++-
 src/core/screen.c         |    2 +
 src/core/window-private.h |   16 +++++-
 src/core/window.c         |  150 +++++++++++++++++++++++++++++++++++++-------
 src/ui/frames.c           |    7 ++-
 5 files changed, 166 insertions(+), 28 deletions(-)
---
diff --git a/src/core/keybindings.c b/src/core/keybindings.c
index f2aafdd..6e56020 100644
--- a/src/core/keybindings.c
+++ b/src/core/keybindings.c
@@ -2034,6 +2034,7 @@ process_mouse_move_resize_grab (MetaDisplay     *display,
         meta_screen_hide_tile_preview (screen);
 
       /* Restore the original tile mode */
+      window->previous_tile_mode = window->tile_mode;
       window->tile_mode = display->grab_tile_mode;
       window->tile_monitor_number = display->grab_tile_monitor_number;
 
@@ -2045,7 +2046,21 @@ process_mouse_move_resize_grab (MetaDisplay     *display,
       if (window->shaken_loose || window->tile_mode == META_TILE_MAXIMIZED)
         meta_window_maximize (window, META_MAXIMIZE_BOTH);
       else if (window->tile_mode != META_TILE_NONE)
-        meta_window_tile (window);
+        {
+          /* If the window is tiled, the user resizes it and cancels, calling
+           * meta_window_tile() would break the window position. This, if the
+           * window was tiled and continues to be tiles, just resize it to the
+           * previous position and size.
+           */
+          if (window->tile_mode != window->previous_tile_mode)
+            meta_window_tile (window);
+          else
+            meta_window_update_resize (window,
+                                       FALSE,
+                                       display->grab_initial_window_pos.x,
+                                       display->grab_initial_window_pos.y,
+                                       TRUE);
+        }
       else
         meta_window_move_resize_frame (display->grab_window,
                                        TRUE,
@@ -2981,6 +2996,7 @@ handle_toggle_tiled (MetaDisplay     *display,
     {
       window->tile_monitor_number = window->saved_maximize ? window->monitor->number
         : -1;
+      window->previous_tile_mode = window->tile_mode;
       window->tile_mode = window->saved_maximize ? META_TILE_MAXIMIZED
         : META_TILE_NONE;
 
@@ -2992,6 +3008,7 @@ handle_toggle_tiled (MetaDisplay     *display,
   else if (meta_window_can_tile_side_by_side (window))
     {
       window->tile_monitor_number = window->monitor->number;
+      window->previous_tile_mode = window->tile_mode;
       window->tile_mode = mode;
       /* Maximization constraints beat tiling constraints, so if the window
        * is maximized, tiling won't have any effect unless we unmaximize it
diff --git a/src/core/screen.c b/src/core/screen.c
index d6b5eac..83a0e9d 100644
--- a/src/core/screen.c
+++ b/src/core/screen.c
@@ -1435,6 +1435,8 @@ meta_screen_update_tile_preview_timeout (gpointer data)
       meta_window_get_current_tile_area (window, &tile_rect);
       meta_compositor_show_tile_preview (screen->display->compositor,
                                          window, &tile_rect, monitor);
+
+      window->tile_mode = META_TILE_NONE;
     }
   else
     meta_compositor_hide_tile_preview (screen->display->compositor);
diff --git a/src/core/window-private.h b/src/core/window-private.h
index 5c8ed98..6305f0e 100644
--- a/src/core/window-private.h
+++ b/src/core/window-private.h
@@ -207,6 +207,12 @@ struct _MetaWindow
    * that to toggle between normal/tiled or maximized/tiled states. */
   guint saved_maximize : 1;
   int tile_monitor_number;
+
+  /* Keep track of the previous tile mode so when changing between left and
+   * right tiles we can resize the window with the complementary width */
+  guint previous_tile_mode : 2;
+  MetaRectangle tile_rect;
+
   int preferred_output_winsys_id;
 
   /* Whether we're shaded */
@@ -554,12 +560,20 @@ struct _MetaWindowClass
 #define META_WINDOW_TILED_MAXIMIZED(w)(META_WINDOW_MAXIMIZED(w) && \
                                        (w)->tile_mode == META_TILE_MAXIMIZED)
 #define META_WINDOW_ALLOWS_MOVE(w)     ((w)->has_move_func && !(w)->fullscreen)
-#define META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS(w)   ((w)->has_resize_func && !META_WINDOW_MAXIMIZED (w) && 
!META_WINDOW_TILED_SIDE_BY_SIDE(w) && !(w)->fullscreen && !(w)->shaded)
+#define META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS(w)   ((w)->has_resize_func && !META_WINDOW_MAXIMIZED (w) && 
!(w)->fullscreen && !(w)->shaded)
 #define META_WINDOW_ALLOWS_RESIZE(w)   (META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS (w) &&                \
                                         (((w)->size_hints.min_width < (w)->size_hints.max_width) ||  \
                                          ((w)->size_hints.min_height < (w)->size_hints.max_height)))
 #define META_WINDOW_ALLOWS_HORIZONTAL_RESIZE(w) (META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS (w) && 
(w)->size_hints.min_width < (w)->size_hints.max_width)
 #define META_WINDOW_ALLOWS_VERTICAL_RESIZE(w)   (META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS (w) && 
(w)->size_hints.min_height < (w)->size_hints.max_height)
+#define META_WINDOW_TILED_LEFT_RESIZING(w) \
+  ((w)->tile_mode == META_TILE_LEFT && \
+   ((w)->display->grab_op == META_GRAB_OP_RESIZING_E || \
+    (w)->display->grab_op == META_GRAB_OP_KEYBOARD_RESIZING_E))
+#define META_WINDOW_TILED_RIGHT_RESIZING(w) \
+  ((w)->tile_mode == META_TILE_RIGHT && \
+   ((w)->display->grab_op == META_GRAB_OP_RESIZING_W || \
+    (w)->display->grab_op == META_GRAB_OP_KEYBOARD_RESIZING_W))
 
 MetaWindow * _meta_window_shared_new       (MetaDisplay         *display,
                                             MetaScreen          *screen,
diff --git a/src/core/window.c b/src/core/window.c
index 36ea96f..b56cbfd 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -2726,6 +2726,12 @@ meta_window_maximize (MetaWindow        *window,
           meta_window_unshade (window, timestamp);
         }
 
+      if (maximize_vertically && maximize_horizontally)
+        {
+          window->previous_tile_mode = window->tile_mode;
+          window->tile_mode = META_TILE_MAXIMIZED;
+        }
+
       /* if the window hasn't been placed yet, we'll maximize it then
        */
       if (!window->placed)
@@ -2886,6 +2892,49 @@ meta_window_requested_dont_bypass_compositor (MetaWindow *window)
   return window->bypass_compositor == _NET_WM_BYPASS_COMPOSITOR_HINT_OFF;
 }
 
+static void
+meta_window_update_tile_state_internal (MetaWindow *window)
+{
+  MetaRectangle monitor_area;
+  gboolean was_tiled;
+  gint borders_width = 0;
+
+  meta_window_get_work_area_current_monitor (window, &monitor_area);
+
+  /* window->tile_rect must consider the visible borders */
+  if (window->frame)
+    {
+      MetaFrameBorders borders;
+
+      meta_frame_calc_borders (window->frame, &borders);
+
+      borders_width = borders.visible.left + borders.visible.right;
+    }
+
+  window->tile_rect.x = monitor_area.x;
+  window->tile_rect.y = monitor_area.y;
+  window->tile_rect.height = monitor_area.height;
+
+  was_tiled = window->previous_tile_mode == META_TILE_LEFT ||
+              window->previous_tile_mode == META_TILE_RIGHT;
+
+  if (window->tile_mode == META_TILE_MAXIMIZED)
+    /* When maximized, cover the entire width*/
+    window->tile_rect.width = monitor_area.width;
+  else if (was_tiled && META_WINDOW_TILED_SIDE_BY_SIDE (window))
+    window->tile_rect.width = monitor_area.width - window->tile_rect.width;
+  else
+    /* Assume half of the work area of the current monitor */
+    window->tile_rect.width = monitor_area.width / 2 + borders_width;
+
+  /* Update the horizontal position */
+  if (window->tile_mode == META_TILE_RIGHT)
+    window->tile_rect.x = monitor_area.x + monitor_area.width - window->tile_rect.width - borders_width;
+
+  /* Track the previous tile mode here, so */
+  window->previous_tile_mode = window->tile_mode;
+}
+
 void
 meta_window_tile (MetaWindow *window)
 {
@@ -2902,6 +2951,7 @@ meta_window_tile (MetaWindow *window)
     directions = META_MAXIMIZE_VERTICAL;
 
   meta_window_maximize_internal (window, directions, NULL);
+  meta_window_update_tile_state_internal (window);
   meta_screen_update_tile_preview (window->screen, FALSE);
 
   meta_window_get_frame_rect (window, &old_frame_rect);
@@ -2916,7 +2966,7 @@ meta_window_tile (MetaWindow *window)
                                      META_MOVE_RESIZE_RESIZE_ACTION |
                                      META_MOVE_RESIZE_STATE_CHANGED),
                                     NorthWestGravity,
-                                    window->unconstrained_rect);
+                                    window->tile_rect);
 
   if (window->frame)
     meta_frame_queue_draw (window->frame);
@@ -3020,6 +3070,12 @@ meta_window_unmaximize (MetaWindow        *window,
       meta_window_get_frame_rect (window, &old_frame_rect);
       meta_window_get_buffer_rect (window, &old_buffer_rect);
 
+      if (unmaximize_vertically)
+        {
+          window->previous_tile_mode = window->tile_mode;
+          window->tile_mode = META_TILE_NONE;
+        }
+
       meta_topic (META_DEBUG_WINDOW_OPS,
                   "Unmaximizing %s%s\n",
                   window->desc,
@@ -5834,6 +5890,35 @@ update_resize_timeout (gpointer data)
 }
 
 static void
+update_tile_rect (MetaWindow *window,
+                  int        *out_new_w)
+{
+  int new_w;
+
+  if (!META_WINDOW_TILED_SIDE_BY_SIDE (window))
+    return;
+
+  new_w = *out_new_w;
+
+  /* Make sure the resize does not break minimum sizes */
+  new_w = MAX (new_w, window->size_hints.min_width);
+
+  if (META_WINDOW_TILED_LEFT_RESIZING (window))
+    {
+      window->tile_rect.width = new_w;
+    }
+  else if (META_WINDOW_TILED_RIGHT_RESIZING (window))
+    {
+      window->tile_rect.x += window->tile_rect.width - new_w;
+      window->tile_rect.width = new_w;
+    }
+
+  /* We can potentially change the new width, so make sure update_resize() always
+   * has the most recent value */
+  *out_new_w = new_w;
+}
+
+static void
 update_resize (MetaWindow *window,
                gboolean    snap,
                int x, int y,
@@ -5958,6 +6043,7 @@ update_resize (MetaWindow *window,
                                           snap,
                                           FALSE);
 
+  update_tile_rect (window, &new_w);
   meta_window_resize_frame_with_gravity (window, TRUE, new_w, new_h, gravity);
 
   /* Store the latest resize time, if we actually resized. */
@@ -5966,19 +6052,43 @@ update_resize (MetaWindow *window,
 }
 
 static void
-update_tile_mode (MetaWindow *window)
+update_tile_mode_after_resize (MetaWindow *window,
+                               gint        x,
+                               gint        y)
 {
-  switch (window->tile_mode)
+  const MetaLogicalMonitor *monitor;
+  MetaMonitorManager *monitor_manager;
+  MetaRectangle work_area;
+  MetaBackend *backend;
+  gint shake_threshold;
+
+  backend = meta_get_backend ();
+  shake_threshold = meta_prefs_get_drag_threshold ();
+  monitor_manager = meta_backend_get_monitor_manager (backend);
+  monitor = meta_monitor_manager_get_logical_monitor_at (monitor_manager, x, y);
+
+  meta_window_get_work_area_for_monitor (window, monitor->number, &work_area);
+
+  /* If the window is tiled and we reach the oposite edge, maximize the window */
+  if ((META_WINDOW_TILED_LEFT (window) && x >= work_area.x + work_area.width - shake_threshold) ||
+      (META_WINDOW_TILED_RIGHT (window) && x <= work_area.x + shake_threshold))
     {
-      case META_TILE_LEFT:
-      case META_TILE_RIGHT:
-          if (!META_WINDOW_TILED_SIDE_BY_SIDE (window))
-              window->tile_mode = META_TILE_NONE;
-          break;
-      case META_TILE_MAXIMIZED:
-          if (!META_WINDOW_MAXIMIZED (window))
-              window->tile_mode = META_TILE_NONE;
-          break;
+      meta_window_maximize (window, META_MAXIMIZE_BOTH);
+    }
+  else
+    {
+      switch (window->tile_mode)
+        {
+          case META_TILE_LEFT:
+          case META_TILE_RIGHT:
+              if (!META_WINDOW_TILED_SIDE_BY_SIDE (window))
+                  window->tile_mode = META_TILE_NONE;
+              break;
+          case META_TILE_MAXIMIZED:
+              if (!META_WINDOW_MAXIMIZED (window))
+                  window->tile_mode = META_TILE_NONE;
+              break;
+        }
     }
 }
 
@@ -6033,7 +6143,7 @@ end_grab_op (MetaWindow *window,
            * would break the ability to snap back to the tiled
            * state, so we wait until mouse release.
            */
-          update_tile_mode (window);
+          update_tile_mode_after_resize (window, x, y);
         }
     }
   meta_display_end_grab_op (window->display, clutter_event_get_time (event));
@@ -6242,20 +6352,12 @@ void
 meta_window_get_current_tile_area (MetaWindow    *window,
                                    MetaRectangle *tile_area)
 {
-  int tile_monitor_number;
-
   g_return_if_fail (window->tile_mode != META_TILE_NONE);
 
-  tile_monitor_number = meta_window_get_current_tile_monitor_number (window);
+  if (window->tile_mode != window->previous_tile_mode)
+    meta_window_update_tile_state_internal (window);
 
-  meta_window_get_work_area_for_monitor (window, tile_monitor_number, tile_area);
-
-  if (window->tile_mode == META_TILE_LEFT  ||
-      window->tile_mode == META_TILE_RIGHT)
-    tile_area->width /= 2;
-
-  if (window->tile_mode == META_TILE_RIGHT)
-    tile_area->x += tile_area->width;
+  *tile_area = window->tile_rect;
 }
 
 gboolean
diff --git a/src/ui/frames.c b/src/ui/frames.c
index 2d86254..129a9e5 100644
--- a/src/ui/frames.c
+++ b/src/ui/frames.c
@@ -1621,6 +1621,9 @@ get_control (MetaUIFrame *frame, int root_x, int root_y)
   has_vert = (flags & META_FRAME_ALLOWS_VERTICAL_RESIZE) != 0;
   has_horiz = (flags & META_FRAME_ALLOWS_HORIZONTAL_RESIZE) != 0;
 
+  if (flags & META_FRAME_TILED_LEFT || flags & META_FRAME_TILED_RIGHT)
+    has_vert = has_horiz = FALSE;
+
   if (POINT_IN_RECT (x, y, fgeom.title_rect))
     {
       if (has_vert && y <= TOP_RESIZE_HEIGHT && has_north_resize)
@@ -1693,12 +1696,12 @@ get_control (MetaUIFrame *frame, int root_x, int root_y)
     }
   else if (x <= fgeom.borders.total.left)
     {
-      if (has_horiz)
+      if (has_horiz || flags & META_FRAME_TILED_RIGHT)
         return META_FRAME_CONTROL_RESIZE_W;
     }
   else if (x >= (fgeom.width - fgeom.borders.total.right))
     {
-      if (has_horiz)
+      if (has_horiz || flags & META_FRAME_TILED_LEFT)
         return META_FRAME_CONTROL_RESIZE_E;
     }
 


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