Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/ui/gtk/extensions/native_app_window_gtk.h" | 5 #include "chrome/browser/ui/gtk/extensions/native_app_window_gtk.h" |
| 6 | 6 |
| 7 #include <gdk/gdkx.h> | |
| 8 #include <vector> | |
| 9 | |
| 10 #include "base/message_loop/message_pump_gtk.h" | |
| 7 #include "base/strings/utf_string_conversions.h" | 11 #include "base/strings/utf_string_conversions.h" |
| 8 #include "chrome/browser/profiles/profile.h" | 12 #include "chrome/browser/profiles/profile.h" |
| 9 #include "chrome/browser/ui/gtk/extensions/extension_keybinding_registry_gtk.h" | 13 #include "chrome/browser/ui/gtk/extensions/extension_keybinding_registry_gtk.h" |
| 10 #include "chrome/browser/ui/gtk/gtk_util.h" | 14 #include "chrome/browser/ui/gtk/gtk_util.h" |
| 11 #include "chrome/browser/ui/gtk/gtk_window_util.h" | 15 #include "chrome/browser/ui/gtk/gtk_window_util.h" |
| 12 #include "chrome/browser/web_applications/web_app.h" | 16 #include "chrome/browser/web_applications/web_app.h" |
| 13 #include "chrome/common/extensions/extension.h" | 17 #include "chrome/common/extensions/extension.h" |
| 14 #include "content/public/browser/render_view_host.h" | 18 #include "content/public/browser/render_view_host.h" |
| 15 #include "content/public/browser/render_widget_host_view.h" | 19 #include "content/public/browser/render_widget_host_view.h" |
| 16 #include "content/public/browser/web_contents.h" | 20 #include "content/public/browser/web_contents.h" |
| 17 #include "content/public/browser/web_contents_view.h" | 21 #include "content/public/browser/web_contents_view.h" |
| 18 #include "ui/base/x/active_window_watcher_x.h" | 22 #include "ui/base/x/active_window_watcher_x.h" |
| 19 #include "ui/gfx/gtk_util.h" | 23 #include "ui/gfx/gtk_util.h" |
| 20 #include "ui/gfx/image/image.h" | 24 #include "ui/gfx/image/image.h" |
| 21 #include "ui/gfx/rect.h" | 25 #include "ui/gfx/rect.h" |
| 22 | 26 |
| 23 using apps::ShellWindow; | 27 using apps::ShellWindow; |
| 24 | 28 |
| 25 namespace { | 29 namespace { |
| 26 | 30 |
| 27 // The timeout in milliseconds before we'll get the true window position with | 31 // The timeout in milliseconds before we'll get the true window position with |
| 28 // gtk_window_get_position() after the last GTK configure-event signal. | 32 // gtk_window_get_position() after the last GTK configure-event signal. |
| 29 const int kDebounceTimeoutMilliseconds = 100; | 33 const int kDebounceTimeoutMilliseconds = 100; |
| 30 | 34 |
| 35 const char* kAtomsToCache[] = { | |
| 36 "_NET_WM_STATE", | |
| 37 "_NET_WM_STATE_HIDDEN", | |
| 38 NULL | |
| 39 }; | |
| 40 | |
| 31 } // namespace | 41 } // namespace |
| 32 | 42 |
| 33 NativeAppWindowGtk::NativeAppWindowGtk(ShellWindow* shell_window, | 43 NativeAppWindowGtk::NativeAppWindowGtk(ShellWindow* shell_window, |
| 34 const ShellWindow::CreateParams& params) | 44 const ShellWindow::CreateParams& params) |
| 35 : shell_window_(shell_window), | 45 : shell_window_(shell_window), |
| 36 window_(NULL), | 46 window_(NULL), |
| 37 state_(GDK_WINDOW_STATE_WITHDRAWN), | 47 state_(GDK_WINDOW_STATE_WITHDRAWN), |
| 38 is_active_(false), | 48 is_active_(false), |
| 39 content_thinks_its_fullscreen_(false), | 49 content_thinks_its_fullscreen_(false), |
| 40 frameless_(params.frame == ShellWindow::FRAME_NONE), | 50 frameless_(params.frame == ShellWindow::FRAME_NONE), |
| 41 frame_cursor_(NULL) { | 51 frame_cursor_(NULL), |
| 52 atom_cache_(base::MessagePumpGtk::GetDefaultXDisplay(), kAtomsToCache) { | |
| 42 window_ = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); | 53 window_ = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); |
| 43 | 54 |
| 44 gfx::NativeView native_view = | 55 gfx::NativeView native_view = |
| 45 web_contents()->GetView()->GetNativeView(); | 56 web_contents()->GetView()->GetNativeView(); |
| 46 gtk_container_add(GTK_CONTAINER(window_), native_view); | 57 gtk_container_add(GTK_CONTAINER(window_), native_view); |
| 47 | 58 |
| 48 if (params.bounds.x() != INT_MIN && params.bounds.y() != INT_MIN) | 59 if (params.bounds.x() != INT_MIN && params.bounds.y() != INT_MIN) |
| 49 gtk_window_move(window_, params.bounds.x(), params.bounds.y()); | 60 gtk_window_move(window_, params.bounds.x(), params.bounds.y()); |
| 50 | 61 |
| 51 // This is done to avoid a WM "feature" where setting the window size to | 62 // This is done to avoid a WM "feature" where setting the window size to |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 120 g_signal_connect(window_, "configure-event", | 131 g_signal_connect(window_, "configure-event", |
| 121 G_CALLBACK(OnConfigureThunk), this); | 132 G_CALLBACK(OnConfigureThunk), this); |
| 122 g_signal_connect(window_, "window-state-event", | 133 g_signal_connect(window_, "window-state-event", |
| 123 G_CALLBACK(OnWindowStateThunk), this); | 134 G_CALLBACK(OnWindowStateThunk), this); |
| 124 if (frameless_) { | 135 if (frameless_) { |
| 125 g_signal_connect(window_, "button-press-event", | 136 g_signal_connect(window_, "button-press-event", |
| 126 G_CALLBACK(OnButtonPressThunk), this); | 137 G_CALLBACK(OnButtonPressThunk), this); |
| 127 g_signal_connect(window_, "motion-notify-event", | 138 g_signal_connect(window_, "motion-notify-event", |
| 128 G_CALLBACK(OnMouseMoveEventThunk), this); | 139 G_CALLBACK(OnMouseMoveEventThunk), this); |
| 129 } | 140 } |
| 141 GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(window_)); | |
| 142 gdk_window_add_filter(window, | |
| 143 &NativeAppWindowGtk::OnXEventThunk, | |
| 144 this); | |
| 130 | 145 |
| 131 // Add the keybinding registry. | 146 // Add the keybinding registry. |
| 132 extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryGtk( | 147 extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryGtk( |
| 133 shell_window_->profile(), | 148 shell_window_->profile(), |
| 134 window_, | 149 window_, |
| 135 extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY, | 150 extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY, |
| 136 shell_window_)); | 151 shell_window_)); |
| 137 | 152 |
| 138 ui::ActiveWindowWatcherX::AddObserver(this); | 153 ui::ActiveWindowWatcherX::AddObserver(this); |
| 139 } | 154 } |
| 140 | 155 |
| 141 NativeAppWindowGtk::~NativeAppWindowGtk() { | 156 NativeAppWindowGtk::~NativeAppWindowGtk() { |
| 142 ui::ActiveWindowWatcherX::RemoveObserver(this); | 157 ui::ActiveWindowWatcherX::RemoveObserver(this); |
| 158 gdk_window_remove_filter(NULL, | |
| 159 &NativeAppWindowGtk::OnXEventThunk, | |
| 160 this); | |
| 143 } | 161 } |
| 144 | 162 |
| 145 bool NativeAppWindowGtk::IsActive() const { | 163 bool NativeAppWindowGtk::IsActive() const { |
| 146 if (ui::ActiveWindowWatcherX::WMSupportsActivation()) | 164 if (ui::ActiveWindowWatcherX::WMSupportsActivation()) |
| 147 return is_active_; | 165 return is_active_; |
| 148 | 166 |
| 149 // This still works even though we don't get the activation notification. | 167 // This still works even though we don't get the activation notification. |
| 150 return gtk_window_is_active(window_); | 168 return gtk_window_is_active(window_); |
| 151 } | 169 } |
| 152 | 170 |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 217 | 235 |
| 218 void NativeAppWindowGtk::Activate() { | 236 void NativeAppWindowGtk::Activate() { |
| 219 gtk_window_present(window_); | 237 gtk_window_present(window_); |
| 220 } | 238 } |
| 221 | 239 |
| 222 void NativeAppWindowGtk::Deactivate() { | 240 void NativeAppWindowGtk::Deactivate() { |
| 223 gdk_window_lower(gtk_widget_get_window(GTK_WIDGET(window_))); | 241 gdk_window_lower(gtk_widget_get_window(GTK_WIDGET(window_))); |
| 224 } | 242 } |
| 225 | 243 |
| 226 void NativeAppWindowGtk::Maximize() { | 244 void NativeAppWindowGtk::Maximize() { |
| 245 gtk_window_present(window_); | |
|
scheib
2013/07/16 22:11:21
I'm not sure why this is needed here?
zhchbin
2013/07/17 02:39:32
After minimize the app window, calling maximize sh
scheib
2013/07/17 04:47:39
Ah! I agree that we should move the different syst
zhchbin
2013/07/17 09:05:36
Done.
| |
| 227 gtk_window_maximize(window_); | 246 gtk_window_maximize(window_); |
| 228 } | 247 } |
| 229 | 248 |
| 230 void NativeAppWindowGtk::Minimize() { | 249 void NativeAppWindowGtk::Minimize() { |
| 231 gtk_window_iconify(window_); | 250 gtk_window_iconify(window_); |
| 232 } | 251 } |
| 233 | 252 |
| 234 void NativeAppWindowGtk::Restore() { | 253 void NativeAppWindowGtk::Restore() { |
| 235 if (IsMaximized()) | 254 if (IsMaximized()) |
| 236 gtk_window_unmaximize(window_); | 255 gtk_window_unmaximize(window_); |
| 237 else if (IsMinimized()) | 256 else if (IsMinimized()) |
| 238 gtk_window_deiconify(window_); | 257 gtk_window_deiconify(window_); |
| 258 | |
| 259 gtk_window_present(window_); | |
|
scheib
2013/07/16 22:11:21
We should only present if the window isn't hidden
zhchbin
2013/07/17 02:39:32
However, I find these behavior are different on th
scheib
2013/07/17 04:47:39
Hmm, on win7 with window state sample (from github
zhchbin
2013/07/17 09:05:36
Done.
| |
| 239 } | 260 } |
| 240 | 261 |
| 241 void NativeAppWindowGtk::SetBounds(const gfx::Rect& bounds) { | 262 void NativeAppWindowGtk::SetBounds(const gfx::Rect& bounds) { |
| 242 gfx::Rect content_bounds = bounds; | 263 gfx::Rect content_bounds = bounds; |
| 243 content_bounds.Inset(GetFrameInsets()); | 264 content_bounds.Inset(GetFrameInsets()); |
| 244 gtk_window_move(window_, content_bounds.x(), content_bounds.y()); | 265 gtk_window_move(window_, content_bounds.x(), content_bounds.y()); |
| 245 if (!resizable_) { | 266 if (!resizable_) { |
| 246 if (frameless_ && | 267 if (frameless_ && |
| 247 gtk_window_util::BoundsMatchMonitorSize(window_, content_bounds)) { | 268 gtk_window_util::BoundsMatchMonitorSize(window_, content_bounds)) { |
| 248 content_bounds.set_height(content_bounds.height() - 1); | 269 content_bounds.set_height(content_bounds.height() - 1); |
| 249 } | 270 } |
| 250 // TODO(jeremya): set_size_request doesn't honor min/max size, so the | 271 // TODO(jeremya): set_size_request doesn't honor min/max size, so the |
| 251 // bounds should be constrained manually. | 272 // bounds should be constrained manually. |
| 252 gtk_widget_set_size_request(GTK_WIDGET(window_), | 273 gtk_widget_set_size_request(GTK_WIDGET(window_), |
| 253 content_bounds.width(), content_bounds.height()); | 274 content_bounds.width(), content_bounds.height()); |
| 254 } else { | 275 } else { |
| 255 gtk_window_util::SetWindowSize(window_, | 276 gtk_window_util::SetWindowSize(window_, |
| 256 gfx::Size(bounds.width(), bounds.height())); | 277 gfx::Size(bounds.width(), bounds.height())); |
| 257 } | 278 } |
| 258 } | 279 } |
| 259 | 280 |
| 281 GdkFilterReturn NativeAppWindowGtk::OnXEvent(GdkXEvent* xevent, | |
|
scheib
2013/07/16 22:11:21
Please add a comment explaining why this method is
zhchbin
2013/07/17 02:39:32
Done.
scheib
2013/07/17 04:47:39
Thank you, though the comment needs a bit more cle
zhchbin
2013/07/17 09:05:36
Done.
| |
| 282 GdkEvent* event) { | |
| 283 XEvent* xev = static_cast<XEvent*>(xevent); | |
|
scheib
2013/07/16 22:11:21
name it xevent, and name the method parameter gdkx
zhchbin
2013/07/17 02:39:32
Done.
| |
| 284 ::Atom state = atom_cache_.GetAtom("_NET_WM_STATE"); | |
|
scheib
2013/07/16 22:11:21
variable only used once, right? Consider calling G
zhchbin
2013/07/17 02:39:32
Done.
| |
| 285 std::vector< ::Atom> atom_list; | |
| 286 | |
| 287 if (xev->type == PropertyNotify && | |
| 288 xev->xproperty.atom == state && | |
| 289 ui::GetAtomArrayProperty(GDK_WINDOW_XWINDOW(GTK_WIDGET(window_)->window), | |
| 290 "_NET_WM_STATE", | |
| 291 &atom_list)) { | |
| 292 ::Atom state_hidden = atom_cache_.GetAtom("_NET_WM_STATE_HIDDEN"); | |
| 293 std::vector< ::Atom>::iterator it = std::find(atom_list.begin(), | |
| 294 atom_list.end(), | |
| 295 state_hidden); | |
|
scheib
2013/07/16 22:11:21
One thing I don't understand is why the hidden ato
zhchbin
2013/07/17 02:39:32
Glance through the source code of "gtk_widget_hide
| |
| 296 if (it != atom_list.end()) | |
| 297 state_ = GDK_WINDOW_STATE_ICONIFIED; | |
| 298 else | |
| 299 state_ = | |
| 300 static_cast<GdkWindowState>(state_ & ~GDK_WINDOW_STATE_ICONIFIED); | |
| 301 | |
| 302 shell_window_->OnNativeWindowChanged(); | |
| 303 } | |
| 304 | |
| 305 return GDK_FILTER_CONTINUE; | |
| 306 } | |
| 307 | |
| 260 void NativeAppWindowGtk::FlashFrame(bool flash) { | 308 void NativeAppWindowGtk::FlashFrame(bool flash) { |
| 261 gtk_window_set_urgency_hint(window_, flash); | 309 gtk_window_set_urgency_hint(window_, flash); |
| 262 } | 310 } |
| 263 | 311 |
| 264 bool NativeAppWindowGtk::IsAlwaysOnTop() const { | 312 bool NativeAppWindowGtk::IsAlwaysOnTop() const { |
| 265 return false; | 313 return false; |
| 266 } | 314 } |
| 267 | 315 |
| 268 void NativeAppWindowGtk::RenderViewHostChanged() { | 316 void NativeAppWindowGtk::RenderViewHostChanged() { |
| 269 web_contents()->GetView()->Focus(); | 317 web_contents()->GetView()->Focus(); |
| (...skipping 277 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 547 | 595 |
| 548 draggable_region_.reset(ShellWindow::RawDraggableRegionsToSkRegion(regions)); | 596 draggable_region_.reset(ShellWindow::RawDraggableRegionsToSkRegion(regions)); |
| 549 } | 597 } |
| 550 | 598 |
| 551 // static | 599 // static |
| 552 NativeAppWindow* NativeAppWindow::Create( | 600 NativeAppWindow* NativeAppWindow::Create( |
| 553 ShellWindow* shell_window, | 601 ShellWindow* shell_window, |
| 554 const ShellWindow::CreateParams& params) { | 602 const ShellWindow::CreateParams& params) { |
| 555 return new NativeAppWindowGtk(shell_window, params); | 603 return new NativeAppWindowGtk(shell_window, params); |
| 556 } | 604 } |
| OLD | NEW |