panel_gtk.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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 3// found in the LICENSE file. 4 5#include "chrome/browser/ui/gtk/panels/panel_gtk.h" 6 7#include <gdk/gdk.h> 8#include <gdk/gdkkeysyms.h> 9#include <X11/XF86keysym.h> 10 11#include "base/bind.h" 12#include "base/debug/trace_event.h" 13#include "base/logging.h" 14#include "base/message_loop.h" 15#include "base/utf_string_conversions.h" 16#include "chrome/app/chrome_command_ids.h" 17#include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog_queue.h" 18#include "chrome/browser/ui/gtk/custom_button.h" 19#include "chrome/browser/ui/gtk/gtk_util.h" 20#include "chrome/browser/ui/gtk/gtk_window_util.h" 21#include "chrome/browser/ui/gtk/panels/panel_titlebar_gtk.h" 22#include "chrome/browser/ui/gtk/panels/panel_drag_gtk.h" 23#include "chrome/browser/ui/panels/panel.h" 24#include "chrome/browser/ui/panels/panel_constants.h" 25#include "chrome/browser/ui/panels/panel_manager.h" 26#include "chrome/browser/web_applications/web_app.h" 27#include "chrome/common/chrome_notification_types.h" 28#include "content/public/browser/native_web_keyboard_event.h" 29#include "content/public/browser/notification_service.h" 30#include "content/public/browser/web_contents.h" 31#include "content/public/browser/web_contents_view.h" 32#include "grit/ui_resources.h" 33#include "ui/base/accelerators/platform_accelerator_gtk.h" 34#include "ui/base/gtk/gtk_compat.h" 35#include "ui/base/gtk/gtk_expanded_container.h" 36#include "ui/base/gtk/gtk_hig_constants.h" 37#include "ui/base/x/active_window_watcher_x.h" 38#include "ui/gfx/canvas.h" 39#include "ui/gfx/image/cairo_cached_surface.h" 40#include "ui/gfx/image/image.h" 41 42using content::NativeWebKeyboardEvent; 43using content::WebContents; 44 45namespace { 46 47const char* kPanelWindowKey = "__PANEL_GTK__"; 48 49// The number of milliseconds between loading animation frames. 50const int kLoadingAnimationFrameTimeMs = 30; 51 52// The frame border is only visible in restored mode and is hardcoded to 4 px 53// on each side regardless of the system window border size. 54const int kFrameBorderThickness = 4; 55// While resize areas on Windows are normally the same size as the window 56// borders, our top area is shrunk by 1 px to make it easier to move the window 57// around with our thinner top grabbable strip. (Incidentally, our side and 58// bottom resize areas don't match the frame border thickness either -- they 59// span the whole nonclient area, so there's no "dead zone" for the mouse.) 60const int kTopResizeAdjust = 1; 61// In the window corners, the resize areas don't actually expand bigger, but 62// the 16 px at the end of each edge triggers diagonal resizing. 63const int kResizeAreaCornerSize = 16; 64 65// Colors used to draw frame background under default theme. 66const SkColor kActiveBackgroundDefaultColor = SkColorSetRGB(0x3a, 0x3d, 0x3d); 67const SkColor kInactiveBackgroundDefaultColor = SkColorSetRGB(0x7a, 0x7c, 0x7c); 68const SkColor kAttentionBackgroundDefaultColor = 69 SkColorSetRGB(0x53, 0xa9, 0x3f); 70const SkColor kMinimizeBackgroundDefaultColor = SkColorSetRGB(0xf5, 0xf4, 0xf0); 71const SkColor kMinimizeBorderDefaultColor = SkColorSetRGB(0xc9, 0xc9, 0xc9); 72 73// Set minimium width for window really small. 74const int kMinWindowWidth = 26; 75 76// Table of supported accelerators in Panels. 77const struct AcceleratorMapping { 78 guint keyval; 79 int command_id; 80 GdkModifierType modifier_type; 81} kAcceleratorMap[] = { 82 // Window controls. 83 { GDK_w, IDC_CLOSE_WINDOW, GDK_CONTROL_MASK }, 84 { GDK_w, IDC_CLOSE_WINDOW, 85 GdkModifierType(GDK_CONTROL_MASK | GDK_SHIFT_MASK) }, 86 { GDK_q, IDC_EXIT, GdkModifierType(GDK_CONTROL_MASK | GDK_SHIFT_MASK) }, 87 88 // Zoom level. 89 { GDK_KP_Add, IDC_ZOOM_PLUS, GDK_CONTROL_MASK }, 90 { GDK_plus, IDC_ZOOM_PLUS, 91 GdkModifierType(GDK_CONTROL_MASK | GDK_SHIFT_MASK) }, 92 { GDK_equal, IDC_ZOOM_PLUS, GDK_CONTROL_MASK }, 93 { XF86XK_ZoomIn, IDC_ZOOM_PLUS, GdkModifierType(0) }, 94 { GDK_KP_0, IDC_ZOOM_NORMAL, GDK_CONTROL_MASK }, 95 { GDK_0, IDC_ZOOM_NORMAL, GDK_CONTROL_MASK }, 96 { GDK_KP_Subtract, IDC_ZOOM_MINUS, GDK_CONTROL_MASK }, 97 { GDK_minus, IDC_ZOOM_MINUS, GDK_CONTROL_MASK }, 98 { GDK_underscore, IDC_ZOOM_MINUS, 99 GdkModifierType(GDK_CONTROL_MASK | GDK_SHIFT_MASK) }, 100 { XF86XK_ZoomOut, IDC_ZOOM_MINUS, GdkModifierType(0) }, 101 102 // Navigation. 103 { GDK_Escape, IDC_STOP, GdkModifierType(0) }, 104 { XF86XK_Stop, IDC_STOP, GdkModifierType(0) }, 105 { GDK_r, IDC_RELOAD, GDK_CONTROL_MASK }, 106 { GDK_r, IDC_RELOAD_IGNORING_CACHE, 107 GdkModifierType(GDK_CONTROL_MASK|GDK_SHIFT_MASK) }, 108 { GDK_F5, IDC_RELOAD, GdkModifierType(0) }, 109 { GDK_F5, IDC_RELOAD_IGNORING_CACHE, GDK_CONTROL_MASK }, 110 { GDK_F5, IDC_RELOAD_IGNORING_CACHE, GDK_SHIFT_MASK }, 111 { XF86XK_Reload, IDC_RELOAD, GdkModifierType(0) }, 112 { XF86XK_Refresh, IDC_RELOAD, GdkModifierType(0) }, 113 114 // Editing. 115 { GDK_c, IDC_COPY, GDK_CONTROL_MASK }, 116 { GDK_x, IDC_CUT, GDK_CONTROL_MASK }, 117 { GDK_v, IDC_PASTE, GDK_CONTROL_MASK }, 118 119 // Dev tools. 120 { GDK_i, IDC_DEV_TOOLS, 121 GdkModifierType(GDK_CONTROL_MASK | GDK_SHIFT_MASK) }, 122 { GDK_j, IDC_DEV_TOOLS_CONSOLE, 123 GdkModifierType(GDK_CONTROL_MASK | GDK_SHIFT_MASK) }, 124 125}; 126 127// Table of accelerator mappings to command ids. 128typedef std::map<ui::Accelerator, int> AcceleratorMap; 129 130const AcceleratorMap& GetAcceleratorTable() { 131 CR_DEFINE_STATIC_LOCAL(AcceleratorMap, accelerator_table, ()); 132 if (accelerator_table.empty()) { 133 for (size_t i = 0; i < arraysize(kAcceleratorMap); ++i) { 134 const AcceleratorMapping& entry = kAcceleratorMap[i]; 135 ui::Accelerator accelerator = ui::AcceleratorForGdkKeyCodeAndModifier( 136 entry.keyval, entry.modifier_type); 137 accelerator_table[accelerator] = entry.command_id; 138 } 139 } 140 return accelerator_table; 141} 142 143gfx::Image CreateImageForColor(SkColor color) { 144 gfx::Canvas canvas(gfx::Size(1, 1), ui::SCALE_FACTOR_100P, true); 145 canvas.DrawColor(color); 146 return gfx::Image(gfx::ImageSkia(canvas.ExtractImageRep())); 147} 148 149const gfx::Image GetActiveBackgroundDefaultImage() { 150 CR_DEFINE_STATIC_LOCAL(gfx::Image, image, ()); 151 if (image.IsEmpty()) 152 image = CreateImageForColor(kActiveBackgroundDefaultColor); 153 return image; 154} 155 156gfx::Image GetInactiveBackgroundDefaultImage() { 157 CR_DEFINE_STATIC_LOCAL(gfx::Image, image, ()); 158 if (image.IsEmpty()) 159 image = CreateImageForColor(kInactiveBackgroundDefaultColor); 160 return image; 161} 162 163gfx::Image GetAttentionBackgroundDefaultImage() { 164 CR_DEFINE_STATIC_LOCAL(gfx::Image, image, ()); 165 if (image.IsEmpty()) 166 image = CreateImageForColor(kAttentionBackgroundDefaultColor); 167 return image; 168} 169 170gfx::Image GetMinimizeBackgroundDefaultImage() { 171 CR_DEFINE_STATIC_LOCAL(gfx::Image, image, ()); 172 if (image.IsEmpty()) 173 image = CreateImageForColor(kMinimizeBackgroundDefaultColor); 174 return image; 175} 176 177// Used to stash a pointer to the Panel window inside the native 178// Gtk window for retrieval in static callbacks. 179GQuark GetPanelWindowQuarkKey() { 180 static GQuark quark = g_quark_from_static_string(kPanelWindowKey); 181 return quark; 182} 183 184// Size of window frame. Empty until first panel has been allocated 185// and sized. Frame size won't change for other panels so it can be 186// computed once for all panels. 187gfx::Size& GetFrameSize() { 188 CR_DEFINE_STATIC_LOCAL(gfx::Size, frame_size, ()); 189 return frame_size; 190} 191 192void SetFrameSize(const gfx::Size& new_size) { 193 gfx::Size& frame_size = GetFrameSize(); 194 frame_size.SetSize(new_size.width(), new_size.height()); 195} 196 197} 198 199// static 200NativePanel* Panel::CreateNativePanel(Panel* panel, const gfx::Rect& bounds) { 201 PanelGtk* panel_gtk = new PanelGtk(panel, bounds); 202 panel_gtk->Init(); 203 return panel_gtk; 204} 205 206PanelGtk::PanelGtk(Panel* panel, const gfx::Rect& bounds) 207 : panel_(panel), 208 bounds_(bounds), 209 always_on_top_(false), 210 is_shown_(false), 211 paint_state_(PAINT_AS_INACTIVE), 212 is_drawing_attention_(false), 213 frame_cursor_(NULL), 214 is_active_(!ui::ActiveWindowWatcherX::WMSupportsActivation()), 215 window_(NULL), 216 window_container_(NULL), 217 window_vbox_(NULL), 218 render_area_event_box_(NULL), 219 contents_expanded_(NULL), 220 accel_group_(NULL), 221 corner_style_(panel::ALL_ROUNDED) { 222} 223 224PanelGtk::~PanelGtk() { 225 ui::ActiveWindowWatcherX::RemoveObserver(this); 226} 227 228void PanelGtk::Init() { 229 ui::ActiveWindowWatcherX::AddObserver(this); 230 231 window_ = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); 232 g_object_set_qdata(G_OBJECT(window_), GetPanelWindowQuarkKey(), this); 233 gtk_widget_add_events(GTK_WIDGET(window_), GDK_BUTTON_PRESS_MASK | 234 GDK_POINTER_MOTION_MASK); 235 gtk_window_set_decorated(window_, false); 236 237 // Disable the resize gripper on Ubuntu. 238 gtk_window_util::DisableResizeGrip(window_); 239 240 // Add this window to its own unique window group to allow for 241 // window-to-parent modality. 242 gtk_window_group_add_window(gtk_window_group_new(), window_); 243 g_object_unref(gtk_window_get_group(window_)); 244 245 // Set minimum height for the window. 246 GdkGeometry hints; 247 hints.min_height = panel::kMinimizedPanelHeight; 248 hints.min_width = kMinWindowWidth; 249 gtk_window_set_geometry_hints( 250 window_, GTK_WIDGET(window_), &hints, GDK_HINT_MIN_SIZE); 251 252 // Connect signal handlers to the window. 253 g_signal_connect(window_, "delete-event", 254 G_CALLBACK(OnMainWindowDeleteEventThunk), this); 255 g_signal_connect(window_, "destroy", 256 G_CALLBACK(OnMainWindowDestroyThunk), this); 257 g_signal_connect(window_, "configure-event", 258 G_CALLBACK(OnConfigureThunk), this); 259 g_signal_connect(window_, "key-press-event", 260 G_CALLBACK(OnKeyPressThunk), this); 261 g_signal_connect(window_, "motion-notify-event", 262 G_CALLBACK(OnMouseMoveEventThunk), this); 263 g_signal_connect(window_, "button-press-event", 264 G_CALLBACK(OnButtonPressEventThunk), this); 265 266 // This vbox contains the titlebar and the render area, but not 267 // the custom frame border. 268 window_vbox_ = gtk_vbox_new(FALSE, 0); 269 gtk_widget_show(window_vbox_); 270 271 // TODO(jennb): add GlobalMenuBar after refactoring out Browser. 272 273 // The window container draws the custom window frame. 274 window_container_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0); 275 gtk_widget_set_name(window_container_, "chrome-custom-frame-border"); 276 gtk_widget_set_app_paintable(window_container_, TRUE); 277 gtk_widget_set_double_buffered(window_container_, FALSE); 278 gtk_widget_set_redraw_on_allocate(window_container_, TRUE); 279 gtk_alignment_set_padding(GTK_ALIGNMENT(window_container_), 0, 280 kFrameBorderThickness, kFrameBorderThickness, kFrameBorderThickness); 281 g_signal_connect(window_container_, "expose-event", 282 G_CALLBACK(OnCustomFrameExposeThunk), this); 283 gtk_container_add(GTK_CONTAINER(window_container_), window_vbox_); 284 285 // Build the titlebar. 286 titlebar_.reset(new PanelTitlebarGtk(this)); 287 titlebar_->Init(); 288 gtk_box_pack_start(GTK_BOX(window_vbox_), titlebar_->widget(), FALSE, FALSE, 289 0); 290 g_signal_connect(titlebar_->widget(), "button-press-event", 291 G_CALLBACK(OnTitlebarButtonPressEventThunk), this); 292 g_signal_connect(titlebar_->widget(), "button-release-event", 293 G_CALLBACK(OnTitlebarButtonReleaseEventThunk), this); 294 295 contents_expanded_ = gtk_expanded_container_new(); 296 gtk_widget_show(contents_expanded_); 297 298 render_area_event_box_ = gtk_event_box_new(); 299 // Set a white background so during startup the user sees white in the 300 // content area before we get a WebContents in place. 301 gtk_widget_modify_bg(render_area_event_box_, GTK_STATE_NORMAL, 302 &ui::kGdkWhite); 303 gtk_container_add(GTK_CONTAINER(render_area_event_box_), 304 contents_expanded_); 305 gtk_widget_show(render_area_event_box_); 306 gtk_box_pack_end(GTK_BOX(window_vbox_), render_area_event_box_, 307 TRUE, TRUE, 0); 308 309 gtk_container_add(GTK_CONTAINER(window_), window_container_); 310 gtk_widget_show(window_container_); 311 312 ConnectAccelerators(); 313} 314 315void PanelGtk::SetWindowCornerStyle(panel::CornerStyle corner_style) { 316 corner_style_ = corner_style; 317 UpdateWindowShape(); 318} 319 320void PanelGtk::MinimizePanelBySystem() { 321 NOTIMPLEMENTED(); 322} 323 324bool PanelGtk::IsPanelMinimizedBySystem() const { 325 NOTIMPLEMENTED(); 326 return false; 327} 328 329void PanelGtk::UpdateWindowShape() { 330 int width = configure_size_.width(); 331 int height = configure_size_.height(); 332 if (!width || !height) 333 return; 334 335 GdkRegion* mask; 336 if (corner_style_ & panel::TOP_ROUNDED) { 337 GdkRectangle top_top_rect = { 3, 0, width - 6, 1 }; 338 GdkRectangle top_mid_rect = { 1, 1, width - 2, 2 }; 339 mask = gdk_region_rectangle(&top_top_rect); 340 gdk_region_union_with_rect(mask, &top_mid_rect); 341 } else { 342 GdkRectangle top_rect = { 0, 0, width, 3 }; 343 mask = gdk_region_rectangle(&top_rect); 344 } 345 346 if (corner_style_ & panel::BOTTOM_ROUNDED) { 347 GdkRectangle mid_rect = { 0, 3, width, height - 6 }; 348 GdkRectangle bottom_mid_rect = { 1, height - 3, width - 2, 2 }; 349 GdkRectangle bottom_bottom_rect = { 3, height - 1, width - 6, 1 }; 350 gdk_region_union_with_rect(mask, &mid_rect); 351 gdk_region_union_with_rect(mask, &bottom_mid_rect); 352 gdk_region_union_with_rect(mask, &bottom_bottom_rect); 353 } else { 354 GdkRectangle mid_rect = { 0, 3, width, height - 3 }; 355 gdk_region_union_with_rect(mask, &mid_rect); 356 } 357 358 gdk_window_shape_combine_region( 359 gtk_widget_get_window(GTK_WIDGET(window_)), mask, 0, 0); 360 if (mask) 361 gdk_region_destroy(mask); 362} 363 364gboolean PanelGtk::OnConfigure(GtkWidget* widget, 365 GdkEventConfigure* event) { 366 // When the window moves, we'll get multiple configure-event signals. We can 367 // also get events when the bounds haven't changed, but the window's stacking 368 // has, which we aren't interested in. http://crbug.com/70125 369 gfx::Size new_size(event->width, event->height); 370 if (new_size == configure_size_) 371 return FALSE; 372 configure_size_ = new_size; 373 374 UpdateWindowShape(); 375 376 if (!GetFrameSize().IsEmpty()) 377 return FALSE; 378 379 // Save the frame size allocated by the system as the 380 // frame size will be affected when we shrink the panel smaller 381 // than the frame (e.g. when the panel is minimized). 382 SetFrameSize(GetNonClientFrameSize()); 383 panel_->OnWindowSizeAvailable(); 384 385 content::NotificationService::current()->Notify( 386 chrome::NOTIFICATION_PANEL_WINDOW_SIZE_KNOWN, 387 content::Source<Panel>(panel_.get()), 388 content::NotificationService::NoDetails()); 389 390 return FALSE; 391} 392 393void PanelGtk::ConnectAccelerators() { 394 accel_group_ = gtk_accel_group_new(); 395 gtk_window_add_accel_group(window_, accel_group_); 396 397 const AcceleratorMap& accelerator_table = GetAcceleratorTable(); 398 for (AcceleratorMap::const_iterator iter = accelerator_table.begin(); 399 iter != accelerator_table.end(); ++iter) { 400 gtk_accel_group_connect( 401 accel_group_, 402 ui::GetGdkKeyCodeForAccelerator(iter->first), 403 ui::GetGdkModifierForAccelerator(iter->first), 404 GtkAccelFlags(0), 405 g_cclosure_new(G_CALLBACK(OnGtkAccelerator), 406 GINT_TO_POINTER(iter->second), NULL)); 407 } 408} 409 410void PanelGtk::DisconnectAccelerators() { 411 // Disconnecting the keys we connected to our accelerator group frees the 412 // closures allocated in ConnectAccelerators. 413 const AcceleratorMap& accelerator_table = GetAcceleratorTable(); 414 for (AcceleratorMap::const_iterator iter = accelerator_table.begin(); 415 iter != accelerator_table.end(); ++iter) { 416 gtk_accel_group_disconnect_key( 417 accel_group_, 418 ui::GetGdkKeyCodeForAccelerator(iter->first), 419 ui::GetGdkModifierForAccelerator(iter->first)); 420 } 421 gtk_window_remove_accel_group(window_, accel_group_); 422 g_object_unref(accel_group_); 423 accel_group_ = NULL; 424} 425 426// static 427gboolean PanelGtk::OnGtkAccelerator(GtkAccelGroup* accel_group, 428 GObject* acceleratable, 429 guint keyval, 430 GdkModifierType modifier, 431 void* user_data) { 432 DCHECK(acceleratable); 433 int command_id = GPOINTER_TO_INT(user_data); 434 PanelGtk* panel_gtk = static_cast<PanelGtk*>( 435 g_object_get_qdata(acceleratable, GetPanelWindowQuarkKey())); 436 return panel_gtk->panel()->ExecuteCommandIfEnabled(command_id); 437} 438 439gboolean PanelGtk::OnKeyPress(GtkWidget* widget, GdkEventKey* event) { 440 // No way to deactivate a window in GTK, so ignore input if window 441 // is supposed to be 'inactive'. See comments in DeactivatePanel(). 442 if (!is_active_) 443 return TRUE; 444 445 // Propagate the key event to child widget first, so we don't override 446 // their accelerators. 447 if (!gtk_window_propagate_key_event(GTK_WINDOW(widget), event)) { 448 if (!gtk_window_activate_key(GTK_WINDOW(widget), event)) { 449 gtk_bindings_activate_event(GTK_OBJECT(widget), event); 450 } 451 } 452 return TRUE; 453} 454 455bool PanelGtk::GetWindowEdge(int x, int y, GdkWindowEdge* edge) const { 456 // Only detect the window edge when panels can be resized by the user. 457 // This method is used by the base class to detect when the cursor has 458 // hit the window edge in order to change the cursor to a resize cursor 459 // and to detect when to initiate a resize drag. 460 panel::Resizability resizability = panel_->CanResizeByMouse(); 461 if (panel::NOT_RESIZABLE == resizability) 462 return false; 463 464 int width = bounds_.width(); 465 int height = bounds_.height(); 466 if (x < kFrameBorderThickness) { 467 if (y < kResizeAreaCornerSize - kTopResizeAdjust && 468 (resizability & panel::RESIZABLE_TOP_LEFT)) { 469 *edge = GDK_WINDOW_EDGE_NORTH_WEST; 470 return true; 471 } else if (y >= height - kResizeAreaCornerSize && 472 (resizability & panel::RESIZABLE_BOTTOM_LEFT)) { 473 *edge = GDK_WINDOW_EDGE_SOUTH_WEST; 474 return true; 475 } 476 } else if (x >= width - kFrameBorderThickness) { 477 if (y < kResizeAreaCornerSize - kTopResizeAdjust && 478 (resizability & panel::RESIZABLE_TOP_RIGHT)) { 479 *edge = GDK_WINDOW_EDGE_NORTH_EAST; 480 return true; 481 } else if (y >= height - kResizeAreaCornerSize && 482 (resizability & panel::RESIZABLE_BOTTOM_RIGHT)) { 483 *edge = GDK_WINDOW_EDGE_SOUTH_EAST; 484 return true; 485 } 486 } 487 488 if (x < kFrameBorderThickness && (resizability & panel::RESIZABLE_LEFT)) { 489 *edge = GDK_WINDOW_EDGE_WEST; 490 return true; 491 } else if (x >= width - kFrameBorderThickness && 492 (resizability & panel::RESIZABLE_RIGHT)) { 493 *edge = GDK_WINDOW_EDGE_EAST; 494 return true; 495 } 496 497 if (y < kFrameBorderThickness && (resizability & panel::RESIZABLE_TOP)) { 498 *edge = GDK_WINDOW_EDGE_NORTH; 499 return true; 500 } else if (y >= height - kFrameBorderThickness && 501 (resizability & panel::RESIZABLE_BOTTOM)) { 502 *edge = GDK_WINDOW_EDGE_SOUTH; 503 return true; 504 } 505 506 return false; 507} 508 509gfx::Image PanelGtk::GetFrameBackground() const { 510 switch (paint_state_) { 511 case PAINT_AS_INACTIVE: 512 return GetInactiveBackgroundDefaultImage(); 513 case PAINT_AS_ACTIVE: 514 return GetActiveBackgroundDefaultImage(); 515 case PAINT_AS_MINIMIZED: 516 return GetMinimizeBackgroundDefaultImage(); 517 case PAINT_FOR_ATTENTION: 518 return GetAttentionBackgroundDefaultImage(); 519 default: 520 NOTREACHED(); 521 return GetInactiveBackgroundDefaultImage(); 522 } 523} 524 525gboolean PanelGtk::OnCustomFrameExpose(GtkWidget* widget, 526 GdkEventExpose* event) { 527 TRACE_EVENT0("ui::gtk", "PanelGtk::OnCustomFrameExpose"); 528 cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(widget)); 529 gdk_cairo_rectangle(cr, &event->area); 530 cairo_clip(cr); 531 532 // Update the painting state. 533 int window_height = gdk_window_get_height(gtk_widget_get_window(widget)); 534 if (is_drawing_attention_) 535 paint_state_ = PAINT_FOR_ATTENTION; 536 else if (window_height <= panel::kMinimizedPanelHeight) 537 paint_state_ = PAINT_AS_MINIMIZED; 538 else if (is_active_) 539 paint_state_ = PAINT_AS_ACTIVE; 540 else 541 paint_state_ = PAINT_AS_INACTIVE; 542 543 // Draw the background. 544 gfx::CairoCachedSurface* surface = GetFrameBackground().ToCairo(); 545 surface->SetSource(cr, widget, 0, 0); 546 cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT); 547 cairo_rectangle(cr, event->area.x, event->area.y, 548 event->area.width, event->area.height); 549 cairo_fill(cr); 550 551 // Draw the border for the minimized panel only. 552 if (paint_state_ == PAINT_AS_MINIMIZED) { 553 cairo_move_to(cr, 0, 3); 554 cairo_line_to(cr, 1, 2); 555 cairo_line_to(cr, 1, 1); 556 cairo_line_to(cr, 2, 1); 557 cairo_line_to(cr, 3, 0); 558 cairo_line_to(cr, event->area.width - 3, 0); 559 cairo_line_to(cr, event->area.width - 2, 1); 560 cairo_line_to(cr, event->area.width - 1, 1); 561 cairo_line_to(cr, event->area.width - 1, 2); 562 cairo_line_to(cr, event->area.width - 1, 3); 563 cairo_line_to(cr, event->area.width - 1, event->area.height - 1); 564 cairo_line_to(cr, 0, event->area.height - 1); 565 cairo_close_path(cr); 566 cairo_set_source_rgb(cr, 567 SkColorGetR(kMinimizeBorderDefaultColor) / 255.0, 568 SkColorGetG(kMinimizeBorderDefaultColor) / 255.0, 569 SkColorGetB(kMinimizeBorderDefaultColor) / 255.0); 570 cairo_set_line_width(cr, 1.0); 571 cairo_stroke(cr); 572 } 573 574 cairo_destroy(cr); 575 576 return FALSE; // Allow subwidgets to paint. 577} 578 579void PanelGtk::EnsureDragHelperCreated() { 580 if (drag_helper_.get()) 581 return; 582 583 drag_helper_.reset(new PanelDragGtk(panel_.get())); 584 gtk_box_pack_end(GTK_BOX(window_vbox_), drag_helper_->widget(), 585 FALSE, FALSE, 0); 586} 587 588gboolean PanelGtk::OnTitlebarButtonPressEvent( 589 GtkWidget* widget, GdkEventButton* event) { 590 if (event->button != 1) 591 return TRUE; 592 if (event->type != GDK_BUTTON_PRESS) 593 return TRUE; 594 595 gdk_window_raise(gtk_widget_get_window(GTK_WIDGET(window_))); 596 EnsureDragHelperCreated(); 597 drag_helper_->InitialTitlebarMousePress(event, titlebar_->widget()); 598 return TRUE; 599} 600 601gboolean PanelGtk::OnTitlebarButtonReleaseEvent( 602 GtkWidget* widget, GdkEventButton* event) { 603 if (event->button != 1) 604 return TRUE; 605 606 panel_->OnTitlebarClicked((event->state & GDK_CONTROL_MASK) ? 607 panel::APPLY_TO_ALL : panel::NO_MODIFIER); 608 return TRUE; 609} 610 611gboolean PanelGtk::OnMouseMoveEvent(GtkWidget* widget, 612 GdkEventMotion* event) { 613 // This method is used to update the mouse cursor when over the edge of the 614 // custom frame. If we're over some other widget, do nothing. 615 if (event->window != gtk_widget_get_window(widget)) { 616 // Reset the cursor. 617 if (frame_cursor_) { 618 frame_cursor_ = NULL; 619 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_)), NULL); 620 } 621 return FALSE; 622 } 623 624 // Update the cursor if we're on the custom frame border. 625 GdkWindowEdge edge; 626 bool has_hit_edge = GetWindowEdge(static_cast<int>(event->x), 627 static_cast<int>(event->y), &edge); 628 GdkCursorType new_cursor = has_hit_edge ? 629 gtk_window_util::GdkWindowEdgeToGdkCursorType(edge) : GDK_LAST_CURSOR; 630 GdkCursorType last_cursor = 631 frame_cursor_ ? frame_cursor_->type : GDK_LAST_CURSOR; 632 633 if (last_cursor != new_cursor) { 634 frame_cursor_ = has_hit_edge ? gfx::GetCursor(new_cursor) : NULL; 635 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_)), 636 frame_cursor_); 637 } 638 return FALSE; 639} 640 641gboolean PanelGtk::OnButtonPressEvent(GtkWidget* widget, 642 GdkEventButton* event) { 643 if (event->button != 1 || event->type != GDK_BUTTON_PRESS) 644 return FALSE; 645 646 // No way to deactivate a window in GTK, so we pretended it is deactivated. 647 // See comments in DeactivatePanel(). 648 // Mouse click anywhere in window should re-activate window so do it now. 649 if (!is_active_) 650 panel_->Activate(); 651 652 // Make the button press coordinate relative to the panel window. 653 int win_x, win_y; 654 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_)); 655 gdk_window_get_origin(gdk_window, &win_x, &win_y); 656 657 GdkWindowEdge edge; 658 gfx::Point point(static_cast<int>(event->x_root - win_x), 659 static_cast<int>(event->y_root - win_y)); 660 bool has_hit_edge = GetWindowEdge(point.x(), point.y(), &edge); 661 if (has_hit_edge) { 662 gdk_window_raise(gdk_window); 663 EnsureDragHelperCreated(); 664 // Resize cursor was set by PanelGtk when mouse moved over window edge. 665 GdkCursor* cursor = 666 gdk_window_get_cursor(gtk_widget_get_window(GTK_WIDGET(window_))); 667 drag_helper_->InitialWindowEdgeMousePress(event, cursor, edge); 668 return TRUE; 669 } 670 671 return FALSE; // Continue to propagate the event. 672} 673 674void PanelGtk::ActiveWindowChanged(GdkWindow* active_window) { 675 // Do nothing if we're in the process of closing the panel window. 676 if (!window_) 677 return; 678 679 bool is_active = gtk_widget_get_window(GTK_WIDGET(window_)) == active_window; 680 if (is_active == is_active_) 681 return; // State did not change. 682 683 if (is_active) { 684 // If there's an app modal dialog (e.g., JS alert), try to redirect 685 // the user's attention to the window owning the dialog. 686 if (AppModalDialogQueue::GetInstance()->HasActiveDialog()) { 687 AppModalDialogQueue::GetInstance()->ActivateModalDialog(); 688 return; 689 } 690 } 691 692 is_active_ = is_active; 693 titlebar_->UpdateTextColor(); 694 InvalidateWindow(); 695 panel_->OnActiveStateChanged(is_active_); 696} 697 698// Callback for the delete event. This event is fired when the user tries to 699// close the window. 700gboolean PanelGtk::OnMainWindowDeleteEvent(GtkWidget* widget, 701 GdkEvent* event) { 702 ClosePanel(); 703 704 // Return true to prevent the gtk window from being destroyed. Close will 705 // destroy it for us. 706 return TRUE; 707} 708 709void PanelGtk::OnMainWindowDestroy(GtkWidget* widget) { 710 // BUG 8712. When we gtk_widget_destroy() in ClosePanel(), this will emit the 711 // signal right away, and we will be here (while ClosePanel() is still in the 712 // call stack). Let stack unwind before deleting the panel. 713 // 714 // We don't want to use DeleteSoon() here since it won't work on a nested pump 715 // (like in UI tests). 716 MessageLoop::current()->PostTask( 717 FROM_HERE, base::Bind(&base::DeletePointer<PanelGtk>, this)); 718} 719 720void PanelGtk::ShowPanel() { 721 gtk_window_present(window_); 722 RevealPanel(); 723} 724 725void PanelGtk::ShowPanelInactive() { 726 gtk_window_set_focus_on_map(window_, false); 727 gtk_widget_show(GTK_WIDGET(window_)); 728 RevealPanel(); 729} 730 731void PanelGtk::RevealPanel() { 732 DCHECK(!is_shown_); 733 is_shown_ = true; 734 SetBoundsInternal(bounds_); 735} 736 737gfx::Rect PanelGtk::GetPanelBounds() const { 738 return bounds_; 739} 740 741void PanelGtk::SetPanelBounds(const gfx::Rect& bounds) { 742 SetBoundsInternal(bounds); 743} 744 745void PanelGtk::SetPanelBoundsInstantly(const gfx::Rect& bounds) { 746 SetBoundsInternal(bounds); 747} 748 749void PanelGtk::SetBoundsInternal(const gfx::Rect& bounds) { 750 if (is_shown_) { 751 gdk_window_move_resize(gtk_widget_get_window(GTK_WIDGET(window_)), 752 bounds.x(), bounds.y(), 753 bounds.width(), bounds.height()); 754 } 755 756 bounds_ = bounds; 757 758 titlebar_->SendEnterNotifyToCloseButtonIfUnderMouse(); 759 panel_->manager()->OnPanelAnimationEnded(panel_.get()); 760} 761 762void PanelGtk::ClosePanel() { 763 // We're already closing. Do nothing. 764 if (!window_) 765 return; 766 767 if (!panel_->ShouldCloseWindow()) 768 return; 769 770 if (drag_helper_.get()) 771 drag_helper_.reset(); 772 773 if (accel_group_) 774 DisconnectAccelerators(); 775 776 // Cancel any pending callback from the loading animation timer. 777 loading_animation_timer_.Stop(); 778 779 if (panel_->GetWebContents()) { 780 // Hide the window (so it appears to have closed immediately). 781 // When web contents are destroyed, we will be called back again. 782 gtk_widget_hide(GTK_WIDGET(window_)); 783 panel_->OnWindowClosing(); 784 return; 785 } 786 787 GtkWidget* window = GTK_WIDGET(window_); 788 // To help catch bugs in any event handlers that might get fired during the 789 // destruction, set window_ to NULL before any handlers will run. 790 window_ = NULL; 791 792 panel_->OnNativePanelClosed(); 793 794 // We don't want GlobalMenuBar handling any notifications or commands after 795 // the window is destroyed. 796 // TODO(jennb): global_menu_bar_->Disable(); 797 gtk_widget_destroy(window); 798} 799 800void PanelGtk::ActivatePanel() { 801 gtk_window_present(window_); 802 803 // When the user clicks to expand the minimized panel, the panel has already 804 // become an active window before gtk_window_present is called. Thus the 805 // active window change event, fired by ActiveWindowWatcherXObserver, is not 806 // triggered. We need to call ActiveWindowChanged manually to update panel's 807 // active status. It is OK to call ActiveWindowChanged with the same active 808 // window twice since the 2nd call is just a no-op. 809 ActiveWindowChanged(gtk_widget_get_window(GTK_WIDGET(window_))); 810} 811 812void PanelGtk::DeactivatePanel() { 813 gdk_window_lower(gtk_widget_get_window(GTK_WIDGET(window_))); 814 815 // Per ICCCM: http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7 816 // A convention is also required for clients that want to give up the 817 // input focus. There is no safe value set for them to set the input 818 // focus to; therefore, they should ignore input material. 819 // 820 // No way to deactive a GTK window. Pretend panel is deactivated 821 // and ignore input. 822 ActiveWindowChanged(NULL); 823} 824 825bool PanelGtk::IsPanelActive() const { 826 return is_active_; 827} 828 829void PanelGtk::PreventActivationByOS(bool prevent_activation) { 830 gtk_window_set_accept_focus(window_, !prevent_activation); 831} 832 833gfx::NativeWindow PanelGtk::GetNativePanelWindow() { 834 return window_; 835} 836 837void PanelGtk::UpdatePanelTitleBar() { 838 TRACE_EVENT0("ui::gtk", "PanelGtk::UpdatePanelTitleBar"); 839 string16 title = panel_->GetWindowTitle(); 840 gtk_window_set_title(window_, UTF16ToUTF8(title).c_str()); 841 titlebar_->UpdateTitleAndIcon(); 842 843 gfx::Image app_icon = panel_->app_icon(); 844 if (!app_icon.IsEmpty()) 845 gtk_util::SetWindowIcon(window_, panel_->profile(), app_icon.ToGdkPixbuf()); 846} 847 848void PanelGtk::UpdatePanelLoadingAnimations(bool should_animate) { 849 if (should_animate) { 850 if (!loading_animation_timer_.IsRunning()) { 851 // Loads are happening, and the timer isn't running, so start it. 852 loading_animation_timer_.Start(FROM_HERE, 853 base::TimeDelta::FromMilliseconds(kLoadingAnimationFrameTimeMs), 854 this, 855 &PanelGtk::LoadingAnimationCallback); 856 } 857 } else { 858 if (loading_animation_timer_.IsRunning()) { 859 loading_animation_timer_.Stop(); 860 // Loads are now complete, update the state if a task was scheduled. 861 LoadingAnimationCallback(); 862 } 863 } 864} 865 866void PanelGtk::LoadingAnimationCallback() { 867 titlebar_->UpdateThrobber(panel_->GetWebContents()); 868} 869 870void PanelGtk::PanelWebContentsFocused(content::WebContents* contents) { 871 // Nothing to do. 872} 873 874void PanelGtk::PanelCut() { 875 gtk_window_util::DoCut(window_, panel_->GetWebContents()); 876} 877 878void PanelGtk::PanelCopy() { 879 gtk_window_util::DoCopy(window_, panel_->GetWebContents()); 880} 881 882void PanelGtk::PanelPaste() { 883 gtk_window_util::DoPaste(window_, panel_->GetWebContents()); 884} 885 886void PanelGtk::DrawAttention(bool draw_attention) { 887 DCHECK((panel_->attention_mode() & Panel::USE_PANEL_ATTENTION) != 0); 888 889 if (is_drawing_attention_ == draw_attention) 890 return; 891 892 is_drawing_attention_ = draw_attention; 893 894 titlebar_->UpdateTextColor(); 895 InvalidateWindow(); 896 897 if ((panel_->attention_mode() & Panel::USE_SYSTEM_ATTENTION) != 0) { 898 // May not be respected by all window managers. 899 gtk_window_set_urgency_hint(window_, draw_attention); 900 } 901} 902 903bool PanelGtk::IsDrawingAttention() const { 904 return is_drawing_attention_; 905} 906 907void PanelGtk::HandlePanelKeyboardEvent( 908 const NativeWebKeyboardEvent& event) { 909 GdkEventKey* os_event = &event.os_event->key; 910 if (os_event && event.type == WebKit::WebInputEvent::RawKeyDown) 911 gtk_window_activate_key(window_, os_event); 912} 913 914void PanelGtk::FullScreenModeChanged(bool is_full_screen) { 915 // Nothing to do here as z-order rules for panels ensures that they're below 916 // any app running in full screen mode. 917} 918 919void PanelGtk::PanelExpansionStateChanging( 920 Panel::ExpansionState old_state, Panel::ExpansionState new_state) { 921} 922 923void PanelGtk::AttachWebContents(content::WebContents* contents) { 924 if (!contents) 925 return; 926 gfx::NativeView widget = contents->GetView()->GetNativeView(); 927 if (widget) { 928 gtk_container_add(GTK_CONTAINER(contents_expanded_), widget); 929 gtk_widget_show(widget); 930 contents->WasShown(); 931 } 932} 933 934void PanelGtk::DetachWebContents(content::WebContents* contents) { 935 gfx::NativeView widget = contents->GetView()->GetNativeView(); 936 if (widget) { 937 GtkWidget* parent = gtk_widget_get_parent(widget); 938 if (parent) { 939 DCHECK_EQ(parent, contents_expanded_); 940 gtk_container_remove(GTK_CONTAINER(contents_expanded_), widget); 941 } 942 } 943} 944 945gfx::Size PanelGtk::WindowSizeFromContentSize( 946 const gfx::Size& content_size) const { 947 gfx::Size& frame_size = GetFrameSize(); 948 return gfx::Size(content_size.width() + frame_size.width(), 949 content_size.height() + frame_size.height()); 950} 951 952gfx::Size PanelGtk::ContentSizeFromWindowSize( 953 const gfx::Size& window_size) const { 954 gfx::Size& frame_size = GetFrameSize(); 955 return gfx::Size(window_size.width() - frame_size.width(), 956 window_size.height() - frame_size.height()); 957} 958 959int PanelGtk::TitleOnlyHeight() const { 960 gfx::Size& frame_size = GetFrameSize(); 961 if (!frame_size.IsEmpty()) 962 return panel::kTitlebarHeight; 963 964 NOTREACHED() << "Checking title height before window allocated"; 965 return 0; 966} 967 968bool PanelGtk::IsPanelAlwaysOnTop() const { 969 return always_on_top_; 970} 971 972void PanelGtk::SetPanelAlwaysOnTop(bool on_top) { 973 if (always_on_top_ == on_top) 974 return; 975 always_on_top_ = on_top; 976 977 gtk_window_set_keep_above(window_, on_top); 978 979 // Do not show an icon in the task bar for always-on-top windows. 980 // Window operations such as close, minimize etc. can only be done 981 // from the panel UI. 982 gtk_window_set_skip_taskbar_hint(window_, on_top); 983 984 // Show always-on-top windows on all the virtual desktops. 985 if (on_top) 986 gtk_window_stick(window_); 987 else 988 gtk_window_unstick(window_); 989} 990 991void PanelGtk::EnableResizeByMouse(bool enable) { 992} 993 994void PanelGtk::UpdatePanelMinimizeRestoreButtonVisibility() { 995 titlebar_->UpdateMinimizeRestoreButtonVisibility(); 996} 997 998gfx::Size PanelGtk::GetNonClientFrameSize() const { 999 GtkAllocation window_allocation; 1000 gtk_widget_get_allocation(window_container_, &window_allocation); 1001 GtkAllocation contents_allocation; 1002 gtk_widget_get_allocation(contents_expanded_, &contents_allocation); 1003 return gfx::Size(window_allocation.width - contents_allocation.width, 1004 window_allocation.height - contents_allocation.height); 1005} 1006 1007void PanelGtk::InvalidateWindow() { 1008 GtkAllocation allocation; 1009 gtk_widget_get_allocation(GTK_WIDGET(window_), &allocation); 1010 gdk_window_invalidate_rect(gtk_widget_get_window(GTK_WIDGET(window_)), 1011 &allocation, TRUE); 1012} 1013 1014// NativePanelTesting implementation. 1015class GtkNativePanelTesting : public NativePanelTesting { 1016 public: 1017 explicit GtkNativePanelTesting(PanelGtk* panel_gtk); 1018 1019 private: 1020 virtual void PressLeftMouseButtonTitlebar( 1021 const gfx::Point& mouse_location, panel::ClickModifier modifier) OVERRIDE; 1022 virtual void ReleaseMouseButtonTitlebar( 1023 panel::ClickModifier modifier) OVERRIDE; 1024 virtual void DragTitlebar(const gfx::Point& mouse_location) OVERRIDE; 1025 virtual void CancelDragTitlebar() OVERRIDE; 1026 virtual void FinishDragTitlebar() OVERRIDE; 1027 virtual bool VerifyDrawingAttention() const OVERRIDE; 1028 virtual bool VerifyActiveState(bool is_active) OVERRIDE; 1029 virtual bool VerifyAppIcon() const OVERRIDE; 1030 virtual bool VerifySystemMinimizeState() const OVERRIDE; 1031 virtual bool IsWindowSizeKnown() const OVERRIDE; 1032 virtual bool IsAnimatingBounds() const OVERRIDE; 1033 virtual bool IsButtonVisible( 1034 panel::TitlebarButtonType button_type) const OVERRIDE; 1035 virtual panel::CornerStyle GetWindowCornerStyle() const OVERRIDE; 1036 1037 PanelGtk* panel_gtk_; 1038}; 1039 1040NativePanelTesting* PanelGtk::CreateNativePanelTesting() { 1041 return new GtkNativePanelTesting(this); 1042} 1043 1044GtkNativePanelTesting::GtkNativePanelTesting(PanelGtk* panel_gtk) 1045 : panel_gtk_(panel_gtk) { 1046} 1047 1048void GtkNativePanelTesting::PressLeftMouseButtonTitlebar( 1049 const gfx::Point& mouse_location, panel::ClickModifier modifier) { 1050 1051 GdkEvent* event = gdk_event_new(GDK_BUTTON_PRESS); 1052 event->button.button = 1; 1053 event->button.x_root = mouse_location.x(); 1054 event->button.y_root = mouse_location.y(); 1055 if (modifier == panel::APPLY_TO_ALL) 1056 event->button.state |= GDK_CONTROL_MASK; 1057 panel_gtk_->OnTitlebarButtonPressEvent( 1058 NULL, reinterpret_cast<GdkEventButton*>(event)); 1059 gdk_event_free(event); 1060 MessageLoopForUI::current()->RunUntilIdle(); 1061} 1062 1063void GtkNativePanelTesting::ReleaseMouseButtonTitlebar( 1064 panel::ClickModifier modifier) { 1065 GdkEvent* event = gdk_event_new(GDK_BUTTON_RELEASE); 1066 event->button.button = 1; 1067 if (modifier == panel::APPLY_TO_ALL) 1068 event->button.state |= GDK_CONTROL_MASK; 1069 if (panel_gtk_->drag_helper_.get()) { 1070 panel_gtk_->drag_helper_->OnButtonReleaseEvent( 1071 NULL, reinterpret_cast<GdkEventButton*>(event)); 1072 } else { 1073 panel_gtk_->OnTitlebarButtonReleaseEvent( 1074 NULL, reinterpret_cast<GdkEventButton*>(event)); 1075 } 1076 gdk_event_free(event); 1077 MessageLoopForUI::current()->RunUntilIdle(); 1078} 1079 1080void GtkNativePanelTesting::DragTitlebar(const gfx::Point& mouse_location) { 1081 if (!panel_gtk_->drag_helper_.get()) 1082 return; 1083 GdkEvent* event = gdk_event_new(GDK_MOTION_NOTIFY); 1084 event->motion.x_root = mouse_location.x(); 1085 event->motion.y_root = mouse_location.y(); 1086 panel_gtk_->drag_helper_->OnMouseMoveEvent( 1087 NULL, reinterpret_cast<GdkEventMotion*>(event)); 1088 gdk_event_free(event); 1089 MessageLoopForUI::current()->RunUntilIdle(); 1090} 1091 1092void GtkNativePanelTesting::CancelDragTitlebar() { 1093 if (!panel_gtk_->drag_helper_.get()) 1094 return; 1095 panel_gtk_->drag_helper_->OnGrabBrokenEvent(NULL, NULL); 1096 MessageLoopForUI::current()->RunUntilIdle(); 1097} 1098 1099void GtkNativePanelTesting::FinishDragTitlebar() { 1100 if (!panel_gtk_->drag_helper_.get()) 1101 return; 1102 ReleaseMouseButtonTitlebar(panel::NO_MODIFIER); 1103} 1104 1105bool GtkNativePanelTesting::VerifyDrawingAttention() const { 1106 return panel_gtk_->IsDrawingAttention(); 1107} 1108 1109bool GtkNativePanelTesting::VerifyActiveState(bool is_active) { 1110 // TODO(jianli): to be implemented. http://crbug.com/102737 1111 return false; 1112} 1113 1114bool GtkNativePanelTesting::VerifyAppIcon() const { 1115 GdkPixbuf* icon = gtk_window_get_icon(panel_gtk_->GetNativePanelWindow()); 1116 return icon && 1117 gdk_pixbuf_get_width(icon) == panel::kPanelAppIconSize && 1118 gdk_pixbuf_get_height(icon) == panel::kPanelAppIconSize; 1119} 1120 1121bool GtkNativePanelTesting::VerifySystemMinimizeState() const { 1122 // TODO(jianli): to be implemented. 1123 return true; 1124} 1125 1126bool GtkNativePanelTesting::IsWindowSizeKnown() const { 1127 return !GetFrameSize().IsEmpty(); 1128} 1129 1130bool GtkNativePanelTesting::IsAnimatingBounds() const { 1131 return false; 1132} 1133 1134bool GtkNativePanelTesting::IsButtonVisible( 1135 panel::TitlebarButtonType button_type) const { 1136 PanelTitlebarGtk* titlebar = panel_gtk_->titlebar(); 1137 CustomDrawButton* button; 1138 switch (button_type) { 1139 case panel::CLOSE_BUTTON: 1140 button = titlebar->close_button(); 1141 break; 1142 case panel::MINIMIZE_BUTTON: 1143 button = titlebar->minimize_button(); 1144 break; 1145 case panel::RESTORE_BUTTON: 1146 button = titlebar->restore_button(); 1147 break; 1148 default: 1149 NOTREACHED(); 1150 return false; 1151 } 1152 return gtk_widget_get_visible(button->widget()); 1153} 1154 1155panel::CornerStyle GtkNativePanelTesting::GetWindowCornerStyle() const { 1156 return panel_gtk_->corner_style_; 1157} 1158