--- 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;