OLD | NEW |
| (Empty) |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/ui/gtk/apps/native_app_window_gtk.h" | |
6 | |
7 #include <gdk/gdkx.h> | |
8 #include <vector> | |
9 | |
10 #include "base/message_loop/message_loop.h" | |
11 #include "base/message_loop/message_pump_gtk.h" | |
12 #include "base/strings/utf_string_conversions.h" | |
13 #include "chrome/browser/profiles/profile.h" | |
14 #include "chrome/browser/ui/gtk/extensions/extension_keybinding_registry_gtk.h" | |
15 #include "chrome/browser/ui/gtk/gtk_util.h" | |
16 #include "chrome/browser/ui/gtk/gtk_window_util.h" | |
17 #include "chrome/browser/web_applications/web_app.h" | |
18 #include "chrome/common/extensions/extension.h" | |
19 #include "content/public/browser/render_view_host.h" | |
20 #include "content/public/browser/render_widget_host_view.h" | |
21 #include "content/public/browser/web_contents.h" | |
22 #include "content/public/browser/web_contents_view.h" | |
23 #include "ui/base/x/active_window_watcher_x.h" | |
24 #include "ui/gfx/gtk_util.h" | |
25 #include "ui/gfx/image/image.h" | |
26 #include "ui/gfx/rect.h" | |
27 | |
28 using apps::ShellWindow; | |
29 | |
30 namespace { | |
31 | |
32 // The timeout in milliseconds before we'll get the true window position with | |
33 // gtk_window_get_position() after the last GTK configure-event signal. | |
34 const int kDebounceTimeoutMilliseconds = 100; | |
35 | |
36 const char* kAtomsToCache[] = { | |
37 "_NET_WM_STATE", | |
38 "_NET_WM_STATE_HIDDEN", | |
39 NULL | |
40 }; | |
41 | |
42 } // namespace | |
43 | |
44 NativeAppWindowGtk::NativeAppWindowGtk(ShellWindow* shell_window, | |
45 const ShellWindow::CreateParams& params) | |
46 : shell_window_(shell_window), | |
47 window_(NULL), | |
48 state_(GDK_WINDOW_STATE_WITHDRAWN), | |
49 is_active_(false), | |
50 content_thinks_its_fullscreen_(false), | |
51 frameless_(params.frame == ShellWindow::FRAME_NONE), | |
52 frame_cursor_(NULL), | |
53 atom_cache_(base::MessagePumpGtk::GetDefaultXDisplay(), kAtomsToCache), | |
54 is_x_event_listened_(false) { | |
55 window_ = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); | |
56 | |
57 gfx::NativeView native_view = | |
58 web_contents()->GetView()->GetNativeView(); | |
59 gtk_container_add(GTK_CONTAINER(window_), native_view); | |
60 | |
61 if (params.bounds.x() != INT_MIN && params.bounds.y() != INT_MIN) | |
62 gtk_window_move(window_, params.bounds.x(), params.bounds.y()); | |
63 | |
64 // This is done to avoid a WM "feature" where setting the window size to | |
65 // the monitor size causes the WM to set the EWMH for full screen mode. | |
66 int win_height = params.bounds.height(); | |
67 if (frameless_ && | |
68 gtk_window_util::BoundsMatchMonitorSize(window_, params.bounds)) { | |
69 win_height -= 1; | |
70 } | |
71 gtk_window_set_default_size(window_, params.bounds.width(), win_height); | |
72 | |
73 resizable_ = params.resizable; | |
74 if (!resizable_) { | |
75 // If the window doesn't have a size request when we set resizable to | |
76 // false, GTK will shrink the window to 1x1px. | |
77 gtk_widget_set_size_request(GTK_WIDGET(window_), | |
78 params.bounds.width(), win_height); | |
79 gtk_window_set_resizable(window_, FALSE); | |
80 } | |
81 | |
82 // make sure bounds_ and restored_bounds_ have correct values until we | |
83 // get our first configure-event | |
84 bounds_ = restored_bounds_ = params.bounds; | |
85 gint x, y; | |
86 gtk_window_get_position(window_, &x, &y); | |
87 bounds_.set_origin(gfx::Point(x, y)); | |
88 | |
89 // Hide titlebar when {frame: 'none'} specified on ShellWindow. | |
90 if (frameless_) | |
91 gtk_window_set_decorated(window_, false); | |
92 | |
93 int min_width = params.minimum_size.width(); | |
94 int min_height = params.minimum_size.height(); | |
95 int max_width = params.maximum_size.width(); | |
96 int max_height = params.maximum_size.height(); | |
97 GdkGeometry hints; | |
98 int hints_mask = 0; | |
99 if (min_width || min_height) { | |
100 hints.min_height = min_height; | |
101 hints.min_width = min_width; | |
102 hints_mask |= GDK_HINT_MIN_SIZE; | |
103 } | |
104 if (max_width || max_height) { | |
105 hints.max_height = max_height ? max_height : G_MAXINT; | |
106 hints.max_width = max_width ? max_width : G_MAXINT; | |
107 hints_mask |= GDK_HINT_MAX_SIZE; | |
108 } | |
109 if (hints_mask) { | |
110 gtk_window_set_geometry_hints( | |
111 window_, | |
112 GTK_WIDGET(window_), | |
113 &hints, | |
114 static_cast<GdkWindowHints>(hints_mask)); | |
115 } | |
116 | |
117 // In some (older) versions of compiz, raising top-level windows when they | |
118 // are partially off-screen causes them to get snapped back on screen, not | |
119 // always even on the current virtual desktop. If we are running under | |
120 // compiz, suppress such raises, as they are not necessary in compiz anyway. | |
121 if (ui::GuessWindowManager() == ui::WM_COMPIZ) | |
122 suppress_window_raise_ = true; | |
123 | |
124 gtk_window_set_title(window_, extension()->name().c_str()); | |
125 | |
126 std::string app_name = web_app::GenerateApplicationNameFromExtensionId( | |
127 extension()->id()); | |
128 gtk_window_util::SetWindowCustomClass(window_, | |
129 web_app::GetWMClassFromAppName(app_name)); | |
130 | |
131 g_signal_connect(window_, "delete-event", | |
132 G_CALLBACK(OnMainWindowDeleteEventThunk), this); | |
133 g_signal_connect(window_, "configure-event", | |
134 G_CALLBACK(OnConfigureThunk), this); | |
135 g_signal_connect(window_, "window-state-event", | |
136 G_CALLBACK(OnWindowStateThunk), this); | |
137 if (frameless_) { | |
138 g_signal_connect(window_, "button-press-event", | |
139 G_CALLBACK(OnButtonPressThunk), this); | |
140 g_signal_connect(window_, "motion-notify-event", | |
141 G_CALLBACK(OnMouseMoveEventThunk), this); | |
142 } | |
143 | |
144 // If _NET_WM_STATE_HIDDEN is in _NET_SUPPORTED, listen for XEvent to work | |
145 // around GTK+ not reporting minimization state changes. See comment in the | |
146 // |OnXEvent|. | |
147 std::vector< ::Atom> supported_atoms; | |
148 if (ui::GetAtomArrayProperty(ui::GetX11RootWindow(), | |
149 "_NET_SUPPORTED", | |
150 &supported_atoms)) { | |
151 if (std::find(supported_atoms.begin(), | |
152 supported_atoms.end(), | |
153 atom_cache_.GetAtom("_NET_WM_STATE_HIDDEN")) != | |
154 supported_atoms.end()) { | |
155 GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(window_)); | |
156 gdk_window_add_filter(window, | |
157 &NativeAppWindowGtk::OnXEventThunk, | |
158 this); | |
159 is_x_event_listened_ = true; | |
160 } | |
161 } | |
162 | |
163 // Add the keybinding registry. | |
164 extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryGtk( | |
165 shell_window_->profile(), | |
166 window_, | |
167 extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY, | |
168 shell_window_)); | |
169 | |
170 ui::ActiveWindowWatcherX::AddObserver(this); | |
171 } | |
172 | |
173 NativeAppWindowGtk::~NativeAppWindowGtk() { | |
174 ui::ActiveWindowWatcherX::RemoveObserver(this); | |
175 if (is_x_event_listened_) { | |
176 gdk_window_remove_filter(NULL, | |
177 &NativeAppWindowGtk::OnXEventThunk, | |
178 this); | |
179 } | |
180 } | |
181 | |
182 bool NativeAppWindowGtk::IsActive() const { | |
183 if (ui::ActiveWindowWatcherX::WMSupportsActivation()) | |
184 return is_active_; | |
185 | |
186 // This still works even though we don't get the activation notification. | |
187 return gtk_window_is_active(window_); | |
188 } | |
189 | |
190 bool NativeAppWindowGtk::IsMaximized() const { | |
191 return (state_ & GDK_WINDOW_STATE_MAXIMIZED); | |
192 } | |
193 | |
194 bool NativeAppWindowGtk::IsMinimized() const { | |
195 return (state_ & GDK_WINDOW_STATE_ICONIFIED); | |
196 } | |
197 | |
198 bool NativeAppWindowGtk::IsFullscreen() const { | |
199 return (state_ & GDK_WINDOW_STATE_FULLSCREEN); | |
200 } | |
201 | |
202 gfx::NativeWindow NativeAppWindowGtk::GetNativeWindow() { | |
203 return window_; | |
204 } | |
205 | |
206 gfx::Rect NativeAppWindowGtk::GetRestoredBounds() const { | |
207 gfx::Rect window_bounds = restored_bounds_; | |
208 window_bounds.Inset(-GetFrameInsets()); | |
209 return window_bounds; | |
210 } | |
211 | |
212 ui::WindowShowState NativeAppWindowGtk::GetRestoredState() const { | |
213 if (IsMaximized()) | |
214 return ui::SHOW_STATE_MAXIMIZED; | |
215 return ui::SHOW_STATE_NORMAL; | |
216 } | |
217 | |
218 gfx::Rect NativeAppWindowGtk::GetBounds() const { | |
219 gfx::Rect window_bounds = bounds_; | |
220 window_bounds.Inset(-GetFrameInsets()); | |
221 return window_bounds; | |
222 } | |
223 | |
224 void NativeAppWindowGtk::Show() { | |
225 gtk_window_present(window_); | |
226 } | |
227 | |
228 void NativeAppWindowGtk::ShowInactive() { | |
229 gtk_window_set_focus_on_map(window_, false); | |
230 gtk_widget_show(GTK_WIDGET(window_)); | |
231 } | |
232 | |
233 void NativeAppWindowGtk::Hide() { | |
234 gtk_widget_hide(GTK_WIDGET(window_)); | |
235 } | |
236 | |
237 void NativeAppWindowGtk::Close() { | |
238 shell_window_->OnNativeWindowChanged(); | |
239 | |
240 // Cancel any pending callback from the window configure debounce timer. | |
241 window_configure_debounce_timer_.Stop(); | |
242 | |
243 GtkWidget* window = GTK_WIDGET(window_); | |
244 // To help catch bugs in any event handlers that might get fired during the | |
245 // destruction, set window_ to NULL before any handlers will run. | |
246 window_ = NULL; | |
247 gtk_widget_destroy(window); | |
248 | |
249 // On other platforms, the native window doesn't get destroyed synchronously. | |
250 // We simulate that here so that ShellWindow can assume that it doesn't get | |
251 // deleted immediately upon calling Close(). | |
252 base::MessageLoop::current()->PostTask( | |
253 FROM_HERE, | |
254 base::Bind(&ShellWindow::OnNativeClose, | |
255 base::Unretained(shell_window_))); | |
256 } | |
257 | |
258 void NativeAppWindowGtk::Activate() { | |
259 gtk_window_present(window_); | |
260 } | |
261 | |
262 void NativeAppWindowGtk::Deactivate() { | |
263 gdk_window_lower(gtk_widget_get_window(GTK_WIDGET(window_))); | |
264 } | |
265 | |
266 void NativeAppWindowGtk::Maximize() { | |
267 // Represent the window first in order to keep the maximization behavior | |
268 // consistency with Windows platform. Otherwise the window will be hidden if | |
269 // it has been minimized. | |
270 gtk_window_present(window_); | |
271 gtk_window_maximize(window_); | |
272 } | |
273 | |
274 void NativeAppWindowGtk::Minimize() { | |
275 gtk_window_iconify(window_); | |
276 } | |
277 | |
278 void NativeAppWindowGtk::Restore() { | |
279 if (IsMaximized()) | |
280 gtk_window_unmaximize(window_); | |
281 else if (IsMinimized()) | |
282 gtk_window_deiconify(window_); | |
283 | |
284 // Represent the window to keep restoration behavior consistency with Windows | |
285 // platform. | |
286 // TODO(zhchbin): verify whether we need this until http://crbug.com/261013 is | |
287 // fixed. | |
288 gtk_window_present(window_); | |
289 } | |
290 | |
291 void NativeAppWindowGtk::SetBounds(const gfx::Rect& bounds) { | |
292 gfx::Rect content_bounds = bounds; | |
293 content_bounds.Inset(GetFrameInsets()); | |
294 gtk_window_move(window_, content_bounds.x(), content_bounds.y()); | |
295 if (!resizable_) { | |
296 if (frameless_ && | |
297 gtk_window_util::BoundsMatchMonitorSize(window_, content_bounds)) { | |
298 content_bounds.set_height(content_bounds.height() - 1); | |
299 } | |
300 // TODO(jeremya): set_size_request doesn't honor min/max size, so the | |
301 // bounds should be constrained manually. | |
302 gtk_widget_set_size_request(GTK_WIDGET(window_), | |
303 content_bounds.width(), content_bounds.height()); | |
304 } else { | |
305 gtk_window_util::SetWindowSize(window_, | |
306 gfx::Size(bounds.width(), bounds.height())); | |
307 } | |
308 } | |
309 | |
310 GdkFilterReturn NativeAppWindowGtk::OnXEvent(GdkXEvent* gdk_x_event, | |
311 GdkEvent* gdk_event) { | |
312 // Work around GTK+ not reporting minimization state changes. Listen | |
313 // for _NET_WM_STATE property changes and use _NET_WM_STATE_HIDDEN's | |
314 // presence to set or clear the iconified bit if _NET_WM_STATE_HIDDEN | |
315 // is supported. http://crbug.com/162794. | |
316 XEvent* x_event = static_cast<XEvent*>(gdk_x_event); | |
317 std::vector< ::Atom> atom_list; | |
318 | |
319 if (x_event->type == PropertyNotify && | |
320 x_event->xproperty.atom == atom_cache_.GetAtom("_NET_WM_STATE") && | |
321 ui::GetAtomArrayProperty(GDK_WINDOW_XWINDOW(GTK_WIDGET(window_)->window), | |
322 "_NET_WM_STATE", | |
323 &atom_list)) { | |
324 std::vector< ::Atom>::iterator it = | |
325 std::find(atom_list.begin(), | |
326 atom_list.end(), | |
327 atom_cache_.GetAtom("_NET_WM_STATE_HIDDEN")); | |
328 state_ = (it != atom_list.end()) ? GDK_WINDOW_STATE_ICONIFIED : | |
329 static_cast<GdkWindowState>(state_ & ~GDK_WINDOW_STATE_ICONIFIED); | |
330 | |
331 shell_window_->OnNativeWindowChanged(); | |
332 } | |
333 | |
334 return GDK_FILTER_CONTINUE; | |
335 } | |
336 | |
337 void NativeAppWindowGtk::FlashFrame(bool flash) { | |
338 gtk_window_set_urgency_hint(window_, flash); | |
339 } | |
340 | |
341 bool NativeAppWindowGtk::IsAlwaysOnTop() const { | |
342 return false; | |
343 } | |
344 | |
345 void NativeAppWindowGtk::RenderViewHostChanged() { | |
346 web_contents()->GetView()->Focus(); | |
347 } | |
348 | |
349 gfx::Insets NativeAppWindowGtk::GetFrameInsets() const { | |
350 if (frameless_) | |
351 return gfx::Insets(); | |
352 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_)); | |
353 if (!gdk_window) | |
354 return gfx::Insets(); | |
355 | |
356 gint current_width = 0; | |
357 gint current_height = 0; | |
358 gtk_window_get_size(window_, ¤t_width, ¤t_height); | |
359 gint current_x = 0; | |
360 gint current_y = 0; | |
361 gdk_window_get_position(gdk_window, ¤t_x, ¤t_y); | |
362 GdkRectangle rect_with_decorations = {0}; | |
363 gdk_window_get_frame_extents(gdk_window, | |
364 &rect_with_decorations); | |
365 | |
366 int left_inset = current_x - rect_with_decorations.x; | |
367 int top_inset = current_y - rect_with_decorations.y; | |
368 return gfx::Insets( | |
369 top_inset, | |
370 left_inset, | |
371 rect_with_decorations.height - current_height - top_inset, | |
372 rect_with_decorations.width - current_width - left_inset); | |
373 } | |
374 | |
375 gfx::NativeView NativeAppWindowGtk::GetHostView() const { | |
376 NOTIMPLEMENTED(); | |
377 return NULL; | |
378 } | |
379 | |
380 gfx::Point NativeAppWindowGtk::GetDialogPosition(const gfx::Size& size) { | |
381 gint current_width = 0; | |
382 gint current_height = 0; | |
383 gtk_window_get_size(window_, ¤t_width, ¤t_height); | |
384 return gfx::Point(current_width / 2 - size.width() / 2, | |
385 current_height / 2 - size.height() / 2); | |
386 } | |
387 | |
388 void NativeAppWindowGtk::AddObserver( | |
389 web_modal::WebContentsModalDialogHostObserver* observer) { | |
390 observer_list_.AddObserver(observer); | |
391 } | |
392 | |
393 void NativeAppWindowGtk::RemoveObserver( | |
394 web_modal::WebContentsModalDialogHostObserver* observer) { | |
395 observer_list_.RemoveObserver(observer); | |
396 } | |
397 | |
398 void NativeAppWindowGtk::ActiveWindowChanged(GdkWindow* active_window) { | |
399 // Do nothing if we're in the process of closing the browser window. | |
400 if (!window_) | |
401 return; | |
402 | |
403 is_active_ = gtk_widget_get_window(GTK_WIDGET(window_)) == active_window; | |
404 if (is_active_) | |
405 shell_window_->OnNativeWindowActivated(); | |
406 } | |
407 | |
408 // Callback for the delete event. This event is fired when the user tries to | |
409 // close the window (e.g., clicking on the X in the window manager title bar). | |
410 gboolean NativeAppWindowGtk::OnMainWindowDeleteEvent(GtkWidget* widget, | |
411 GdkEvent* event) { | |
412 Close(); | |
413 | |
414 // Return true to prevent the GTK window from being destroyed. Close will | |
415 // destroy it for us. | |
416 return TRUE; | |
417 } | |
418 | |
419 gboolean NativeAppWindowGtk::OnConfigure(GtkWidget* widget, | |
420 GdkEventConfigure* event) { | |
421 // We update |bounds_| but not |restored_bounds_| here. The latter needs | |
422 // to be updated conditionally when the window is non-maximized and non- | |
423 // fullscreen, but whether those state updates have been processed yet is | |
424 // window-manager specific. We update |restored_bounds_| in the debounced | |
425 // handler below, after the window state has been updated. | |
426 bounds_.SetRect(event->x, event->y, event->width, event->height); | |
427 | |
428 // The GdkEventConfigure* we get here doesn't have quite the right | |
429 // coordinates though (they're relative to the drawable window area, rather | |
430 // than any window manager decorations, if enabled), so we need to call | |
431 // gtk_window_get_position() to get the right values. (Otherwise session | |
432 // restore, if enabled, will restore windows to incorrect positions.) That's | |
433 // a round trip to the X server though, so we set a debounce timer and only | |
434 // call it (in OnConfigureDebounced() below) after we haven't seen a | |
435 // reconfigure event in a short while. | |
436 // We don't use Reset() because the timer may not yet be running. | |
437 // (In that case Stop() is a no-op.) | |
438 window_configure_debounce_timer_.Stop(); | |
439 window_configure_debounce_timer_.Start(FROM_HERE, | |
440 base::TimeDelta::FromMilliseconds(kDebounceTimeoutMilliseconds), this, | |
441 &NativeAppWindowGtk::OnConfigureDebounced); | |
442 | |
443 return FALSE; | |
444 } | |
445 | |
446 void NativeAppWindowGtk::OnConfigureDebounced() { | |
447 gtk_window_util::UpdateWindowPosition(this, &bounds_, &restored_bounds_); | |
448 shell_window_->OnNativeWindowChanged(); | |
449 | |
450 FOR_EACH_OBSERVER(web_modal::WebContentsModalDialogHostObserver, | |
451 observer_list_, | |
452 OnPositionRequiresUpdate()); | |
453 | |
454 // Fullscreen of non-resizable windows requires them to be made resizable | |
455 // first. After that takes effect and OnConfigure is called we transition | |
456 // to fullscreen. | |
457 if (!IsFullscreen() && IsFullscreenOrPending()) { | |
458 gtk_window_fullscreen(window_); | |
459 } | |
460 } | |
461 | |
462 gboolean NativeAppWindowGtk::OnWindowState(GtkWidget* sender, | |
463 GdkEventWindowState* event) { | |
464 state_ = event->new_window_state; | |
465 | |
466 if (content_thinks_its_fullscreen_ && | |
467 !(state_ & GDK_WINDOW_STATE_FULLSCREEN)) { | |
468 content_thinks_its_fullscreen_ = false; | |
469 content::RenderViewHost* rvh = web_contents()->GetRenderViewHost(); | |
470 if (rvh) | |
471 rvh->ExitFullscreen(); | |
472 } | |
473 | |
474 return FALSE; | |
475 } | |
476 | |
477 bool NativeAppWindowGtk::GetWindowEdge(int x, int y, GdkWindowEdge* edge) { | |
478 if (!frameless_) | |
479 return false; | |
480 | |
481 if (IsMaximized() || IsFullscreen()) | |
482 return false; | |
483 | |
484 return gtk_window_util::GetWindowEdge(bounds_.size(), 0, x, y, edge); | |
485 } | |
486 | |
487 gboolean NativeAppWindowGtk::OnMouseMoveEvent(GtkWidget* widget, | |
488 GdkEventMotion* event) { | |
489 if (!frameless_) { | |
490 // Reset the cursor. | |
491 if (frame_cursor_) { | |
492 frame_cursor_ = NULL; | |
493 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_)), NULL); | |
494 } | |
495 return FALSE; | |
496 } | |
497 | |
498 if (!resizable_) | |
499 return FALSE; | |
500 | |
501 // Update the cursor if we're on the custom frame border. | |
502 GdkWindowEdge edge; | |
503 bool has_hit_edge = GetWindowEdge(static_cast<int>(event->x), | |
504 static_cast<int>(event->y), &edge); | |
505 GdkCursorType new_cursor = GDK_LAST_CURSOR; | |
506 if (has_hit_edge) | |
507 new_cursor = gtk_window_util::GdkWindowEdgeToGdkCursorType(edge); | |
508 | |
509 GdkCursorType last_cursor = GDK_LAST_CURSOR; | |
510 if (frame_cursor_) | |
511 last_cursor = frame_cursor_->type; | |
512 | |
513 if (last_cursor != new_cursor) { | |
514 frame_cursor_ = has_hit_edge ? gfx::GetCursor(new_cursor) : NULL; | |
515 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_)), | |
516 frame_cursor_); | |
517 } | |
518 return FALSE; | |
519 } | |
520 | |
521 gboolean NativeAppWindowGtk::OnButtonPress(GtkWidget* widget, | |
522 GdkEventButton* event) { | |
523 DCHECK(frameless_); | |
524 // Make the button press coordinate relative to the browser window. | |
525 int win_x, win_y; | |
526 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_)); | |
527 gdk_window_get_origin(gdk_window, &win_x, &win_y); | |
528 | |
529 GdkWindowEdge edge; | |
530 gfx::Point point(static_cast<int>(event->x_root - win_x), | |
531 static_cast<int>(event->y_root - win_y)); | |
532 bool has_hit_edge = resizable_ && GetWindowEdge(point.x(), point.y(), &edge); | |
533 bool has_hit_titlebar = | |
534 draggable_region_ && draggable_region_->contains(event->x, event->y); | |
535 | |
536 if (event->button == 1) { | |
537 if (GDK_BUTTON_PRESS == event->type) { | |
538 // Raise the window after a click on either the titlebar or the border to | |
539 // match the behavior of most window managers, unless that behavior has | |
540 // been suppressed. | |
541 if ((has_hit_titlebar || has_hit_edge) && !suppress_window_raise_) | |
542 gdk_window_raise(GTK_WIDGET(widget)->window); | |
543 | |
544 if (has_hit_edge) { | |
545 gtk_window_begin_resize_drag(window_, edge, event->button, | |
546 static_cast<gint>(event->x_root), | |
547 static_cast<gint>(event->y_root), | |
548 event->time); | |
549 return TRUE; | |
550 } else if (has_hit_titlebar) { | |
551 return gtk_window_util::HandleTitleBarLeftMousePress( | |
552 window_, bounds_, event); | |
553 } | |
554 } else if (GDK_2BUTTON_PRESS == event->type) { | |
555 if (has_hit_titlebar && resizable_) { | |
556 // Maximize/restore on double click. | |
557 if (IsMaximized()) { | |
558 gtk_window_util::UnMaximize(GTK_WINDOW(widget), | |
559 bounds_, restored_bounds_); | |
560 } else { | |
561 gtk_window_maximize(window_); | |
562 } | |
563 return TRUE; | |
564 } | |
565 } | |
566 } else if (event->button == 2) { | |
567 if (has_hit_titlebar || has_hit_edge) | |
568 gdk_window_lower(gdk_window); | |
569 return TRUE; | |
570 } | |
571 | |
572 return FALSE; | |
573 } | |
574 | |
575 void NativeAppWindowGtk::SetFullscreen(bool fullscreen) { | |
576 content_thinks_its_fullscreen_ = fullscreen; | |
577 if (fullscreen){ | |
578 if (resizable_) { | |
579 gtk_window_fullscreen(window_); | |
580 } else { | |
581 // We must first make the window resizable. That won't take effect | |
582 // immediately, so OnConfigureDebounced completes the fullscreen call. | |
583 gtk_window_set_resizable(window_, TRUE); | |
584 } | |
585 } else { | |
586 gtk_window_unfullscreen(window_); | |
587 if (!resizable_) | |
588 gtk_window_set_resizable(window_, FALSE); | |
589 } | |
590 } | |
591 | |
592 bool NativeAppWindowGtk::IsFullscreenOrPending() const { | |
593 return content_thinks_its_fullscreen_; | |
594 } | |
595 | |
596 bool NativeAppWindowGtk::IsDetached() const { | |
597 return false; | |
598 } | |
599 | |
600 void NativeAppWindowGtk::UpdateWindowIcon() { | |
601 Profile* profile = shell_window_->profile(); | |
602 gfx::Image app_icon = shell_window_->app_icon(); | |
603 if (!app_icon.IsEmpty()) | |
604 gtk_util::SetWindowIcon(window_, profile, app_icon.ToGdkPixbuf()); | |
605 else | |
606 gtk_util::SetWindowIcon(window_, profile); | |
607 } | |
608 | |
609 void NativeAppWindowGtk::UpdateWindowTitle() { | |
610 string16 title = shell_window_->GetTitle(); | |
611 gtk_window_set_title(window_, UTF16ToUTF8(title).c_str()); | |
612 } | |
613 | |
614 void NativeAppWindowGtk::HandleKeyboardEvent( | |
615 const content::NativeWebKeyboardEvent& event) { | |
616 // No-op. | |
617 } | |
618 | |
619 void NativeAppWindowGtk::UpdateDraggableRegions( | |
620 const std::vector<extensions::DraggableRegion>& regions) { | |
621 // Draggable region is not supported for non-frameless window. | |
622 if (!frameless_) | |
623 return; | |
624 | |
625 draggable_region_.reset(ShellWindow::RawDraggableRegionsToSkRegion(regions)); | |
626 } | |
OLD | NEW |