Bug 1283299 - Part 8: Add client-size resizers for GTK windows. r?karlt draft
authorAndrew Comminos <andrew@comminos.com>
Tue, 05 Jul 2016 16:43:27 -0400
changeset 384225 f511ac18ce61
parent 384224 1da2d6f52f42
child 384226 9cacb2b9314e
push id22212
push userbmo:andrew@comminos.com
push dateTue, 05 Jul 2016 20:52:16 +0000
reviewerskarlt
bugs1283299
milestone50.0a1
Bug 1283299 - Part 8: Add client-size resizers for GTK windows. r?karlt MozReview-Commit-ID: Hbz9iGLSTrs
widget/gtk/nsWindow.cpp
widget/gtk/nsWindow.h
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -2641,16 +2641,49 @@ nsWindow::OnMotionNotifyEvent(GdkEventMo
         if (gPluginFocusWindow && gPluginFocusWindow != this) {
             RefPtr<nsWindow> kungFuDeathGrip = gPluginFocusWindow;
             gPluginFocusWindow->LoseNonXEmbedPluginFocus();
         }
 #endif /* MOZ_WIDGET_GTK == 2 */
     }
 #endif /* MOZ_X11 */
 
+    GdkWindowEdge edge;
+    if (CheckResizerEdge(GetRefPoint(this, aEvent), edge)) {
+        nsCursor cursor = eCursor_none;
+        switch (edge) {
+        case GDK_WINDOW_EDGE_NORTH:
+            cursor = eCursor_n_resize;
+            break;
+        case GDK_WINDOW_EDGE_NORTH_WEST:
+            cursor = eCursor_nw_resize;
+            break;
+        case GDK_WINDOW_EDGE_NORTH_EAST:
+            cursor = eCursor_ne_resize;
+            break;
+        case GDK_WINDOW_EDGE_WEST:
+            cursor = eCursor_w_resize;
+            break;
+        case GDK_WINDOW_EDGE_EAST:
+            cursor = eCursor_e_resize;
+            break;
+        case GDK_WINDOW_EDGE_SOUTH:
+            cursor = eCursor_s_resize;
+            break;
+        case GDK_WINDOW_EDGE_SOUTH_WEST:
+            cursor = eCursor_sw_resize;
+            break;
+        case GDK_WINDOW_EDGE_SOUTH_EAST:
+            cursor = eCursor_se_resize;
+            break;
+        }
+        SetCursor(cursor);
+        return;
+    }
+
     WidgetMouseEvent event(true, eMouseMove, this, WidgetMouseEvent::eReal);
 
     gdouble pressure = 0;
     gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
     // Sometime gdk generate 0 pressure value between normal values
     // We have to ignore that and use last valid value
     if (pressure)
       mLastMotionPressure = pressure;
@@ -2810,16 +2843,25 @@ nsWindow::OnButtonPressEvent(GdkEventBut
     if (!gFocusWindow && containerWindow) {
         containerWindow->DispatchActivateEvent();
     }
 
     // check to see if we should rollup
     if (CheckForRollup(aEvent->x_root, aEvent->y_root, false, false))
         return;
 
+    // Check to see if the event is within our window's resize region
+    GdkWindowEdge edge;
+    if (CheckResizerEdge(GetRefPoint(this, aEvent), edge)) {
+        gdk_window_begin_resize_drag(mGdkWindow, edge, aEvent->button,
+                                     aEvent->x_root, aEvent->y_root,
+                                     aEvent->time);
+        return;
+    }
+
     gdouble pressure = 0;
     gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
     mLastMotionPressure = pressure;
 
     uint16_t domButton;
     switch (aEvent->button) {
     case 1:
         domButton = WidgetMouseEvent::eLeftButton;
@@ -7214,16 +7256,30 @@ nsWindow::RoundsWidgetCoordinatesTo()
     return GdkScaleFactor();
 }
 
 bool
 nsWindow::IsClientDecorated() const
 {
     return mDrawsInTitlebar;
 }
+
+int
+nsWindow::GetClientResizerSize()
+{
+  if (!mShell)
+    return 0;
+
+  // GTK uses a default size of 20px as of 3.20.
+  gint size = 20;
+  gtk_widget_style_get(mShell, "decoration-resize-handle", &size, nullptr);
+
+  return GdkCoordToDevicePixels(size);
+}
+
 void
 nsWindow::UpdateClientShadowWidth()
 {
   if (gtk_check_version(3, 12, 0) != nullptr)
     return;
 
   // Shadows are only used for normal, non-solid client windows with CSD.
   gint top = 0, right = 0, bottom = 0, left = 0;
@@ -7231,8 +7287,58 @@ nsWindow::UpdateClientShadowWidth()
     moz_gtk_get_window_decoration_extents(&top, &right, &bottom, &left);
   }
 
   static auto sGdkWindowSetShadowWidth =
     (void (*)(GdkWindow*, gint, gint, gint, gint))
     dlsym(RTLD_DEFAULT, "gdk_window_set_shadow_width");
   sGdkWindowSetShadowWidth(mGdkWindow, left, right, top, bottom);
 }
+
+bool
+nsWindow::CheckResizerEdge(LayoutDeviceIntPoint aPoint, GdkWindowEdge& aOutEdge)
+{
+  // We only need to handle resizers when using CSD.
+  if (!IsClientDecorated())
+    return false;
+
+  // Don't allow resizing maximized windows.
+  if (mSizeState != nsSizeMode_Normal)
+    return false;
+
+  gint left, top, right, bottom;
+  WidgetNodeType type = UseSolidCSD() ? MOZ_GTK_WINDOW_DECORATION_SOLID
+                                      : MOZ_GTK_WINDOW_DECORATION;
+  Unused << moz_gtk_get_widget_border(type, &left, &top, &right, &bottom,
+                                      GTK_TEXT_DIR_LTR, false);
+  gint scale = GdkScaleFactor();
+  left *= scale;
+  top *= scale;
+  right *= scale;
+  bottom *= scale;
+
+  int resizerSize = GetClientResizerSize();
+  int topDist = aPoint.y;
+  int leftDist = aPoint.x;
+  int rightDist = mBounds.width - aPoint.x;
+  int bottomDist = mBounds.height - aPoint.y;
+
+  if (leftDist <= resizerSize && topDist <= resizerSize) {
+    aOutEdge = GDK_WINDOW_EDGE_NORTH_WEST;
+  } else if (rightDist <= resizerSize && topDist <= resizerSize) {
+    aOutEdge = GDK_WINDOW_EDGE_NORTH_EAST;
+  } else if (leftDist <= resizerSize && bottomDist <= resizerSize) {
+    aOutEdge = GDK_WINDOW_EDGE_SOUTH_WEST;
+  } else if (rightDist <= resizerSize && bottomDist <= resizerSize) {
+    aOutEdge = GDK_WINDOW_EDGE_SOUTH_EAST;
+  } else if (topDist <= top) {
+    aOutEdge = GDK_WINDOW_EDGE_NORTH;
+  } else if (leftDist <= left) {
+    aOutEdge = GDK_WINDOW_EDGE_WEST;
+  } else if (rightDist <= right) {
+    aOutEdge = GDK_WINDOW_EDGE_EAST;
+  } else if (bottomDist <= bottom) {
+    aOutEdge = GDK_WINDOW_EDGE_SOUTH;
+  } else {
+    return false;
+  }
+  return true;
+}
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -394,20 +394,26 @@ protected:
                                       GtkWidget* aNewContainer,
                                       GdkWindow* aNewParentWindow,
                                       GtkWidget* aOldContainer);
 
     virtual void RegisterTouchWindow() override;
 
     // Decorations
     bool IsClientDecorated() const;
+    int GetClientResizerSize();
+
     // Informs the window manager about the size of the shadows surrounding
     // a client-side decorated window.
     void UpdateClientShadowWidth();
 
+    // Returns true if the given point (in device pixels) is within a resizer
+    // region of the window. Only used when drawing decorations client side.
+    bool CheckResizerEdge(LayoutDeviceIntPoint aPoint, GdkWindowEdge& aOutEdge);
+
     nsCOMPtr<nsIWidget> mParent;
     // Is this a toplevel window?
     bool                mIsTopLevel;
     // Has this widget been destroyed yet?
     bool                mIsDestroyed;
 
     // Should we send resize events on all resizes?
     bool                mListenForResizes;