Skip to content

Commit 1ec4bec

Browse files
committed
Linux: make frameless window resizable
Merge upstream patch from #169010; Fix nwjs#142
1 parent 2fa8fb0 commit 1ec4bec

File tree

2 files changed

+130
-11
lines changed

2 files changed

+130
-11
lines changed

src/browser/native_window_gtk.cc

Lines changed: 112 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ const double kGtkCursorBlinkCycleFactor = 2000.0;
5151
NativeWindowGtk::NativeWindowGtk(const base::WeakPtr<content::Shell>& shell,
5252
base::DictionaryValue* manifest)
5353
: NativeWindow(shell, manifest),
54-
content_thinks_its_fullscreen_(false) {
54+
state_(GDK_WINDOW_STATE_WITHDRAWN),
55+
content_thinks_its_fullscreen_(false),
56+
frame_cursor_(NULL),
57+
resizable_(true) {
5558
window_ = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
5659

5760
vbox_ = gtk_vbox_new(FALSE, 0);
@@ -124,6 +127,9 @@ NativeWindowGtk::NativeWindowGtk(const base::WeakPtr<content::Shell>& shell,
124127
if (!has_frame_) {
125128
g_signal_connect(window_, "button-press-event",
126129
G_CALLBACK(OnButtonPressThunk), this);
130+
131+
g_signal_connect(window_, "motion-notify-event",
132+
G_CALLBACK(OnMouseMoveEventThunk), this);
127133
}
128134

129135
SetWebKitColorStyle();
@@ -193,6 +199,14 @@ bool NativeWindowGtk::IsFullscreen() {
193199
return content_thinks_its_fullscreen_;
194200
}
195201

202+
bool NativeWindowGtk::IsMaximized() const {
203+
return (state_ & GDK_WINDOW_STATE_MAXIMIZED);
204+
}
205+
206+
bool NativeWindowGtk::IsMinimized() const {
207+
return (state_ & GDK_WINDOW_STATE_ICONIFIED);
208+
}
209+
196210
void NativeWindowGtk::SetSize(const gfx::Size& size) {
197211
gtk_window_util::SetWindowSize(window_, size);
198212
}
@@ -225,6 +239,7 @@ void NativeWindowGtk::SetMaximumSize(int width, int height) {
225239
}
226240

227241
void NativeWindowGtk::SetResizable(bool resizable) {
242+
resizable_ = resizable;
228243
// Should request widget size after setting unresizable, otherwise the
229244
// window will shrink to a very small size.
230245
if (resizable == false) {
@@ -514,6 +529,8 @@ gboolean NativeWindowGtk::OnFocusOut(GtkWidget* window, GdkEventFocus*) {
514529
// Window state has changed.
515530
gboolean NativeWindowGtk::OnWindowState(GtkWidget* window,
516531
GdkEventWindowState* event) {
532+
state_ = event->new_window_state;
533+
517534
switch (event->changed_mask) {
518535
case GDK_WINDOW_STATE_ICONIFIED:
519536
if (shell()) {
@@ -562,23 +579,107 @@ gboolean NativeWindowGtk::OnWindowDeleteEvent(GtkWidget* widget,
562579
return FALSE;
563580
}
564581

582+
bool NativeWindowGtk::GetWindowEdge(int x, int y, GdkWindowEdge* edge) {
583+
if (has_frame_)
584+
return false;
585+
586+
if (IsMaximized() || IsFullscreen())
587+
return false;
588+
589+
return gtk_window_util::GetWindowEdge(GetBounds().size(), 0, x, y, edge);
590+
}
591+
592+
gboolean NativeWindowGtk::OnMouseMoveEvent(GtkWidget* widget,
593+
GdkEventMotion* event) {
594+
if (has_frame_) {
595+
// Reset the cursor.
596+
if (frame_cursor_) {
597+
frame_cursor_ = NULL;
598+
gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_)), NULL);
599+
}
600+
return FALSE;
601+
}
602+
603+
if (!resizable_)
604+
return FALSE;
605+
606+
int win_x, win_y;
607+
GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_));
608+
gdk_window_get_origin(gdk_window, &win_x, &win_y);
609+
gfx::Point point(static_cast<int>(event->x_root - win_x),
610+
static_cast<int>(event->y_root - win_y));
611+
612+
// Update the cursor if we're on the custom frame border.
613+
GdkWindowEdge edge;
614+
bool has_hit_edge = GetWindowEdge(point.x(), point.y(), &edge);
615+
GdkCursorType new_cursor = GDK_LAST_CURSOR;
616+
if (has_hit_edge)
617+
new_cursor = gtk_window_util::GdkWindowEdgeToGdkCursorType(edge);
618+
619+
GdkCursorType last_cursor = GDK_LAST_CURSOR;
620+
if (frame_cursor_)
621+
last_cursor = frame_cursor_->type;
622+
623+
if (last_cursor != new_cursor) {
624+
frame_cursor_ = has_hit_edge ? gfx::GetCursor(new_cursor) : NULL;
625+
gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_)),
626+
frame_cursor_);
627+
}
628+
return FALSE;
629+
}
630+
565631
// Capture mouse click on window.
566632
gboolean NativeWindowGtk::OnButtonPress(GtkWidget* widget,
567633
GdkEventButton* event) {
568-
if (!draggable_region_.isEmpty() &&
569-
draggable_region_.contains(event->x, event->y)) {
570-
if (event->button == 1 && GDK_BUTTON_PRESS == event->type) {
571-
if (!suppress_window_raise_)
634+
DCHECK(!has_frame_);
635+
// Make the button press coordinate relative to the browser window.
636+
int win_x, win_y;
637+
GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_));
638+
gdk_window_get_origin(gdk_window, &win_x, &win_y);
639+
640+
GdkWindowEdge edge;
641+
gfx::Point point(static_cast<int>(event->x_root - win_x),
642+
static_cast<int>(event->y_root - win_y));
643+
bool has_hit_edge = resizable_ && GetWindowEdge(point.x(), point.y(), &edge);
644+
bool has_hit_titlebar =
645+
!draggable_region_.isEmpty() && draggable_region_.contains(event->x, event->y);
646+
647+
if (event->button == 1) {
648+
if (GDK_BUTTON_PRESS == event->type) {
649+
// Raise the window after a click on either the titlebar or the border to
650+
// match the behavior of most window managers, unless that behavior has
651+
// been suppressed.
652+
if ((has_hit_titlebar || has_hit_edge) && !suppress_window_raise_)
572653
gdk_window_raise(GTK_WIDGET(widget)->window);
573654

574-
return gtk_window_util::HandleTitleBarLeftMousePress(
575-
GTK_WINDOW(widget), GetBounds(), event);
576-
} else if (event->button == 2) {
577-
gdk_window_lower(GTK_WIDGET(widget)->window);
578-
return TRUE;
655+
if (has_hit_edge) {
656+
gtk_window_begin_resize_drag(window_, edge, event->button,
657+
static_cast<gint>(event->x_root),
658+
static_cast<gint>(event->y_root),
659+
event->time);
660+
return TRUE;
661+
} else if (has_hit_titlebar) {
662+
return gtk_window_util::HandleTitleBarLeftMousePress(
663+
window_, GetBounds(), event);
664+
}
665+
} else if (GDK_2BUTTON_PRESS == event->type) {
666+
if (has_hit_titlebar && resizable_) {
667+
// Maximize/restore on double click.
668+
if (IsMaximized()) {
669+
gtk_window_unmaximize(window_);
670+
//gtk_window_util::UnMaximize(GTK_WINDOW(widget),
671+
// GetBounds(), restored_bounds_);
672+
} else {
673+
gtk_window_maximize(window_);
674+
}
675+
return TRUE;
676+
}
579677
}
678+
} else if (event->button == 2) {
679+
if (has_hit_titlebar || has_hit_edge)
680+
gdk_window_lower(gdk_window);
681+
return TRUE;
580682
}
581-
582683
return FALSE;
583684
}
584685

src/browser/native_window_gtk.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#define CONTENT_NW_SRC_BROWSER_NATIVE_WINDOW_GTK_H_
2323

2424
#include <gtk/gtk.h>
25+
#include <gdk/gdk.h>
2526

2627
#include "content/nw/src/browser/native_window.h"
2728
#include "third_party/skia/include/core/SkRegion.h"
@@ -70,6 +71,8 @@ class NativeWindowGtk : public NativeWindow {
7071

7172
GtkWindow* window() const { return window_; }
7273

74+
bool IsMinimized() const;
75+
bool IsMaximized() const;
7376
protected:
7477
// NativeWindow implementation.
7578
virtual void AddToolbar() OVERRIDE;
@@ -90,6 +93,11 @@ class NativeWindowGtk : public NativeWindow {
9093
// Get the position and size of the current window.
9194
gfx::Rect GetBounds();
9295

96+
// If the point (|x|, |y|) is within the resize border area of the window,
97+
// returns true and sets |edge| to the appropriate GdkWindowEdge value.
98+
// Otherwise, returns false.
99+
bool GetWindowEdge(int x, int y, GdkWindowEdge* edge);
100+
93101
CHROMEGTK_CALLBACK_0(NativeWindowGtk, void, OnBackButtonClicked);
94102
CHROMEGTK_CALLBACK_0(NativeWindowGtk, void, OnForwardButtonClicked);
95103
CHROMEGTK_CALLBACK_0(NativeWindowGtk, void, OnRefreshStopButtonClicked);
@@ -106,6 +114,8 @@ class NativeWindowGtk : public NativeWindow {
106114
GdkEvent*);
107115
CHROMEGTK_CALLBACK_1(NativeWindowGtk, gboolean, OnButtonPress,
108116
GdkEventButton*);
117+
CHROMEGTK_CALLBACK_1(NativeWindowGtk, gboolean, OnMouseMoveEvent,
118+
GdkEventMotion*);
109119

110120
GtkWindow* window_;
111121
GtkWidget* toolbar_;
@@ -116,6 +126,7 @@ class NativeWindowGtk : public NativeWindow {
116126
GtkToolItem* refresh_stop_button_;
117127
GtkToolItem* devtools_button_;
118128
GtkToolItem* dev_reload_button_;
129+
GdkWindowState state_;
119130

120131
// True if the RVH is in fullscreen mode. The window may not actually be in
121132
// fullscreen, however: some WMs don't support fullscreen.
@@ -129,6 +140,13 @@ class NativeWindowGtk : public NativeWindow {
129140
// bar or window border. This is to work around a compiz bug.
130141
bool suppress_window_raise_;
131142

143+
// The current window cursor. We set it to a resize cursor when over the
144+
// custom frame border. We set it to NULL if we want the default cursor.
145+
GdkCursor* frame_cursor_;
146+
147+
// True if the window should be resizable by the user.
148+
bool resizable_;
149+
132150
DISALLOW_COPY_AND_ASSIGN(NativeWindowGtk);
133151
};
134152

0 commit comments

Comments
 (0)