panel_view.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/views/panels/panel_view.h" 6 7#include <map> 8#include "base/logging.h" 9#include "base/message_loop.h" 10#include "base/utf_string_conversions.h" 11#include "chrome/app/chrome_command_ids.h" 12#include "chrome/browser/profiles/profile.h" 13#include "chrome/browser/ui/panels/panel.h" 14#include "chrome/browser/ui/panels/panel_bounds_animation.h" 15#include "chrome/browser/ui/panels/panel_manager.h" 16#include "chrome/browser/ui/panels/stacked_panel_collection.h" 17#include "chrome/browser/ui/views/panels/panel_frame_view.h" 18#include "content/public/browser/render_view_host.h" 19#include "content/public/browser/render_widget_host_view.h" 20#include "content/public/browser/web_contents.h" 21#include "content/public/browser/web_contents_view.h" 22#include "ui/gfx/image/image.h" 23#include "ui/gfx/path.h" 24#include "ui/gfx/screen.h" 25#include "ui/views/controls/button/image_button.h" 26#include "ui/views/controls/webview/webview.h" 27#include "ui/views/widget/widget.h" 28 29#if defined(OS_WIN) 30#include "base/win/windows_version.h" 31#include "chrome/browser/shell_integration.h" 32#include "chrome/browser/ui/views/panels/taskbar_window_thumbnailer_win.h" 33#include "ui/base/win/shell.h" 34#include "ui/gfx/icon_util.h" 35#include "ui/views/win/hwnd_util.h" 36#endif 37 38namespace { 39 40// If the height of a stacked panel shrinks below this threshold during the 41// user resizing, it will be treated as minimized. 42const int kStackedPanelHeightShrinkThresholdToBecomeMinimized = 43 panel::kTitlebarHeight + 20; 44 45// Supported accelerators. 46// Note: We can't use the acclerator table defined in chrome/browser/ui/views 47// due to checkdeps violation. 48struct AcceleratorMapping { 49 ui::KeyboardCode keycode; 50 int modifiers; 51 int command_id; 52}; 53const AcceleratorMapping kPanelAcceleratorMap[] = { 54 { ui::VKEY_W, ui::EF_CONTROL_DOWN, IDC_CLOSE_WINDOW }, 55 { ui::VKEY_W, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, IDC_CLOSE_WINDOW }, 56 { ui::VKEY_F4, ui::EF_ALT_DOWN, IDC_CLOSE_WINDOW }, 57 { ui::VKEY_R, ui::EF_CONTROL_DOWN, IDC_RELOAD }, 58 { ui::VKEY_F5, ui::EF_NONE, IDC_RELOAD }, 59 { ui::VKEY_R, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, 60 IDC_RELOAD_IGNORING_CACHE }, 61 { ui::VKEY_F5, ui::EF_CONTROL_DOWN, IDC_RELOAD_IGNORING_CACHE }, 62 { ui::VKEY_F5, ui::EF_SHIFT_DOWN, IDC_RELOAD_IGNORING_CACHE }, 63 { ui::VKEY_ESCAPE, ui::EF_NONE, IDC_STOP }, 64 { ui::VKEY_OEM_MINUS, ui::EF_CONTROL_DOWN, IDC_ZOOM_MINUS }, 65 { ui::VKEY_SUBTRACT, ui::EF_CONTROL_DOWN, IDC_ZOOM_MINUS }, 66 { ui::VKEY_0, ui::EF_CONTROL_DOWN, IDC_ZOOM_NORMAL }, 67 { ui::VKEY_NUMPAD0, ui::EF_CONTROL_DOWN, IDC_ZOOM_NORMAL }, 68 { ui::VKEY_OEM_PLUS, ui::EF_CONTROL_DOWN, IDC_ZOOM_PLUS }, 69 { ui::VKEY_ADD, ui::EF_CONTROL_DOWN, IDC_ZOOM_PLUS }, 70 { ui::VKEY_I, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, IDC_DEV_TOOLS }, 71 { ui::VKEY_J, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, 72 IDC_DEV_TOOLS_CONSOLE }, 73}; 74 75const std::map<ui::Accelerator, int>& GetAcceleratorTable() { 76 static std::map<ui::Accelerator, int>* accelerators = NULL; 77 if (!accelerators) { 78 accelerators = new std::map<ui::Accelerator, int>(); 79 for (size_t i = 0; i < arraysize(kPanelAcceleratorMap); ++i) { 80 ui::Accelerator accelerator(kPanelAcceleratorMap[i].keycode, 81 kPanelAcceleratorMap[i].modifiers); 82 (*accelerators)[accelerator] = kPanelAcceleratorMap[i].command_id; 83 } 84 } 85 return *accelerators; 86} 87 88// NativePanelTesting implementation. 89class NativePanelTestingWin : public NativePanelTesting { 90 public: 91 explicit NativePanelTestingWin(PanelView* panel_view); 92 93 private: 94 virtual void PressLeftMouseButtonTitlebar( 95 const gfx::Point& mouse_location, panel::ClickModifier modifier) OVERRIDE; 96 virtual void ReleaseMouseButtonTitlebar( 97 panel::ClickModifier modifier) OVERRIDE; 98 virtual void DragTitlebar(const gfx::Point& mouse_location) OVERRIDE; 99 virtual void CancelDragTitlebar() OVERRIDE; 100 virtual void FinishDragTitlebar() OVERRIDE; 101 virtual bool VerifyDrawingAttention() const OVERRIDE; 102 virtual bool VerifyActiveState(bool is_active) OVERRIDE; 103 virtual bool VerifyAppIcon() const OVERRIDE; 104 virtual bool VerifySystemMinimizeState() const OVERRIDE; 105 virtual bool IsWindowSizeKnown() const OVERRIDE; 106 virtual bool IsAnimatingBounds() const OVERRIDE; 107 virtual bool IsButtonVisible( 108 panel::TitlebarButtonType button_type) const OVERRIDE; 109 virtual panel::CornerStyle GetWindowCornerStyle() const OVERRIDE; 110 111 PanelView* panel_view_; 112}; 113 114NativePanelTestingWin::NativePanelTestingWin(PanelView* panel_view) 115 : panel_view_(panel_view) { 116} 117 118void NativePanelTestingWin::PressLeftMouseButtonTitlebar( 119 const gfx::Point& mouse_location, panel::ClickModifier modifier) { 120 panel_view_->OnTitlebarMousePressed(mouse_location); 121} 122 123void NativePanelTestingWin::ReleaseMouseButtonTitlebar( 124 panel::ClickModifier modifier) { 125 panel_view_->OnTitlebarMouseReleased(modifier); 126} 127 128void NativePanelTestingWin::DragTitlebar(const gfx::Point& mouse_location) { 129 panel_view_->OnTitlebarMouseDragged(mouse_location); 130} 131 132void NativePanelTestingWin::CancelDragTitlebar() { 133 panel_view_->OnTitlebarMouseCaptureLost(); 134} 135 136void NativePanelTestingWin::FinishDragTitlebar() { 137 panel_view_->OnTitlebarMouseReleased(panel::NO_MODIFIER); 138} 139 140bool NativePanelTestingWin::VerifyDrawingAttention() const { 141 MessageLoop::current()->RunUntilIdle(); 142 return panel_view_->GetFrameView()->GetPaintState() == 143 PanelFrameView::PAINT_FOR_ATTENTION; 144} 145 146bool NativePanelTestingWin::VerifyActiveState(bool is_active) { 147 return panel_view_->GetFrameView()->GetPaintState() == 148 (is_active ? PanelFrameView::PAINT_AS_ACTIVE 149 : PanelFrameView::PAINT_AS_INACTIVE); 150} 151 152bool NativePanelTestingWin::VerifyAppIcon() const { 153#if defined(OS_WIN) 154 // We only care about Windows 7 and later. 155 if (base::win::GetVersion() < base::win::VERSION_WIN7) 156 return true; 157 158 HWND native_window = views::HWNDForWidget(panel_view_->window()); 159 HICON app_icon = reinterpret_cast<HICON>( 160 ::SendMessage(native_window, WM_GETICON, ICON_BIG, 0L)); 161 if (!app_icon) 162 return false; 163 scoped_ptr<SkBitmap> bitmap(IconUtil::CreateSkBitmapFromHICON(app_icon)); 164 return bitmap.get() && 165 bitmap->width() == panel::kPanelAppIconSize && 166 bitmap->height() == panel::kPanelAppIconSize; 167#else 168 return true; 169#endif 170} 171 172bool NativePanelTestingWin::VerifySystemMinimizeState() const { 173#if defined(OS_WIN) 174 HWND native_window = views::HWNDForWidget(panel_view_->window()); 175 WINDOWPLACEMENT placement; 176 if (!::GetWindowPlacement(native_window, &placement)) 177 return false; 178 if (placement.showCmd == SW_MINIMIZE || placement.showCmd == SW_SHOWMINIMIZED) 179 return true; 180 181 // If the panel window has owner window, as in stacked mode, check its owner 182 // window. Note that owner window, instead of parent window, is returned 183 // though GWL_HWNDPARENT contains 'parent'. 184 HWND owner_window = 185 reinterpret_cast<HWND>(::GetWindowLongPtr(native_window, 186 GWLP_HWNDPARENT)); 187 if (!owner_window || !::GetWindowPlacement(owner_window, &placement)) 188 return false; 189 return placement.showCmd == SW_MINIMIZE || 190 placement.showCmd == SW_SHOWMINIMIZED; 191#else 192 return true; 193#endif 194} 195 196bool NativePanelTestingWin::IsWindowSizeKnown() const { 197 return true; 198} 199 200bool NativePanelTestingWin::IsAnimatingBounds() const { 201 return panel_view_->IsAnimatingBounds(); 202} 203 204bool NativePanelTestingWin::IsButtonVisible( 205 panel::TitlebarButtonType button_type) const { 206 PanelFrameView* frame_view = panel_view_->GetFrameView(); 207 208 switch (button_type) { 209 case panel::CLOSE_BUTTON: 210 return frame_view->close_button()->visible(); 211 case panel::MINIMIZE_BUTTON: 212 return frame_view->minimize_button()->visible(); 213 case panel::RESTORE_BUTTON: 214 return frame_view->restore_button()->visible(); 215 default: 216 NOTREACHED(); 217 } 218 return false; 219} 220 221panel::CornerStyle NativePanelTestingWin::GetWindowCornerStyle() const { 222 return panel_view_->GetFrameView()->corner_style(); 223} 224 225} // namespace 226 227// static 228NativePanel* Panel::CreateNativePanel(Panel* panel, const gfx::Rect& bounds) { 229 return new PanelView(panel, bounds); 230} 231 232// The panel window has to be created as always-on-top. We cannot create it 233// as non-always-on-top and then change it to always-on-top because Windows 234// system might deny making a window always-on-top if the application is not 235// a foreground application. In addition, we do not know if the panel should 236// be created as always-on-top at its creation time. To solve this issue, 237// always_on_top_ is default to true because we can always change from 238// always-on-top to not always-on-top but not the other way around. 239PanelView::PanelView(Panel* panel, const gfx::Rect& bounds) 240 : panel_(panel), 241 bounds_(bounds), 242 window_(NULL), 243 window_closed_(false), 244 web_view_(NULL), 245 always_on_top_(true), 246 focused_(false), 247 user_resizing_(false), 248#if defined(OS_WIN) 249 user_resizing_interior_stacked_panel_edge_(false), 250#endif 251 mouse_pressed_(false), 252 mouse_dragging_state_(NO_DRAGGING), 253 is_drawing_attention_(false), 254 force_to_paint_as_inactive_(false), 255 old_focused_view_(NULL) { 256 window_ = new views::Widget; 257 views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW); 258 params.delegate = this; 259 params.remove_standard_frame = true; 260 params.keep_on_top = true; 261 params.bounds = bounds; 262 window_->Init(params); 263 window_->set_frame_type(views::Widget::FRAME_TYPE_FORCE_CUSTOM); 264 window_->set_focus_on_creation(false); 265 window_->AddObserver(this); 266 267 web_view_ = new views::WebView(NULL); 268 AddChildView(web_view_); 269 270 OnViewWasResized(); 271 272 // Register accelarators supported by panels. 273 views::FocusManager* focus_manager = GetFocusManager(); 274 const std::map<ui::Accelerator, int>& accelerator_table = 275 GetAcceleratorTable(); 276 for (std::map<ui::Accelerator, int>::const_iterator iter = 277 accelerator_table.begin(); 278 iter != accelerator_table.end(); ++iter) { 279 focus_manager->RegisterAccelerator( 280 iter->first, ui::AcceleratorManager::kNormalPriority, this); 281 } 282 283#if defined(OS_WIN) 284 ui::win::SetAppIdForWindow( 285 ShellIntegration::GetAppModelIdForProfile(UTF8ToWide(panel->app_name()), 286 panel->profile()->GetPath()), 287 views::HWNDForWidget(window_)); 288#endif 289 290 views::WidgetFocusManager::GetInstance()->AddFocusChangeListener(this); 291} 292 293PanelView::~PanelView() { 294} 295 296void PanelView::ShowPanel() { 297 ShowPanelInactive(); 298 ActivatePanel(); 299} 300 301void PanelView::ShowPanelInactive() { 302 if (window_->IsVisible()) 303 return; 304 window_->ShowInactive(); 305 // No animation is used for initial creation of a panel on Win. 306 // Signal immediately that pending actions can be performed. 307 panel_->manager()->OnPanelAnimationEnded(panel_.get()); 308} 309 310gfx::Rect PanelView::GetPanelBounds() const { 311 return bounds_; 312} 313 314void PanelView::SetPanelBounds(const gfx::Rect& bounds) { 315 SetBoundsInternal(bounds, true); 316} 317 318void PanelView::SetPanelBoundsInstantly(const gfx::Rect& bounds) { 319 SetBoundsInternal(bounds, false); 320} 321 322void PanelView::SetBoundsInternal(const gfx::Rect& new_bounds, bool animate) { 323 if (bounds_ == new_bounds) 324 return; 325 326 bounds_ = new_bounds; 327 328 if (!animate) { 329 // If no animation is in progress, apply bounds change instantly. Otherwise, 330 // continue the animation with new target bounds. 331 if (!IsAnimatingBounds()) 332 SetWidgetBounds(bounds_); 333 return; 334 } 335 336 animation_start_bounds_ = window_->GetWindowBoundsInScreen(); 337 338 bounds_animator_.reset(new PanelBoundsAnimation( 339 this, panel_.get(), animation_start_bounds_, new_bounds)); 340 bounds_animator_->Start(); 341} 342 343#if defined(OS_WIN) 344bool PanelView::FilterMessage(HWND hwnd, 345 UINT message, 346 WPARAM w_param, 347 LPARAM l_param, 348 LRESULT* l_result) { 349 switch (message) { 350 case WM_SIZING: 351 if (w_param == WMSZ_BOTTOM) 352 user_resizing_interior_stacked_panel_edge_ = true; 353 break; 354 } 355 return false; 356} 357#endif 358 359void PanelView::AnimationEnded(const ui::Animation* animation) { 360 panel_->manager()->OnPanelAnimationEnded(panel_.get()); 361} 362 363void PanelView::AnimationProgressed(const ui::Animation* animation) { 364 gfx::Rect new_bounds = bounds_animator_->CurrentValueBetween( 365 animation_start_bounds_, bounds_); 366 SetWidgetBounds(new_bounds); 367} 368 369void PanelView::SetWidgetBounds(const gfx::Rect& new_bounds) { 370#if defined(OS_WIN) 371 // An overlapped window is a top-level window that has a titlebar, border, 372 // and client area. The Windows system will automatically put the shadow 373 // around the whole window. Also the system will enforce the minimum height 374 // (38 pixels based on observation) for the overlapped window such that it 375 // will always has the space for the titlebar. 376 // 377 // On contrast, a popup window is a bare minimum window without border and 378 // titlebar by default. It is often used for the popup menu and the window 379 // with short life. The Windows system does not add the shadow around the 380 // whole window though CS_DROPSHADOW class style could be passed to add the 381 // drop shadow which is only around the right and bottom edges. 382 // 383 // The height of the title-only or minimized panel is smaller than the minimum 384 // overlapped window height. If the panel still uses the overlapped window 385 // style, Windows system will automatically increase the window height. To 386 // work around this limitation, we temporarily change the window style to 387 // popup when the height to set is smaller than the minimum overlapped window 388 // height and then restore the window style to overlapped when the height 389 // grows. 390 static const int kMinimumOverlappedWindowHeight = 38; 391 gfx::Rect old_bounds = GetWidget()->GetRestoredBounds(); 392 if (old_bounds.height() > kMinimumOverlappedWindowHeight && 393 new_bounds.height() <= kMinimumOverlappedWindowHeight) { 394 // When the panel height shrinks below the minimum overlapped window height, 395 // change the window style to popup such that we can show the title-only 396 // and minimized panel without additional height being added by the system. 397 UpdateWindowAttribute(GWL_STYLE, 398 WS_POPUP, 399 WS_OVERLAPPED | WS_THICKFRAME | WS_SYSMENU, 400 true); 401 } else if (old_bounds.height() <= kMinimumOverlappedWindowHeight && 402 new_bounds.height() > kMinimumOverlappedWindowHeight) { 403 // Change the window style back to overlappped when the panel height grow 404 // taller than the minimum overlapped window height. 405 UpdateWindowAttribute(GWL_STYLE, 406 WS_OVERLAPPED | WS_THICKFRAME | WS_SYSMENU, 407 WS_POPUP, 408 true); 409 } 410#endif 411 412 GetWidget()->SetBounds(new_bounds); 413} 414 415void PanelView::ClosePanel() { 416 // We're already closing. Do nothing. 417 if (window_closed_) 418 return; 419 420 if (!panel_->ShouldCloseWindow()) 421 return; 422 423 // Cancel any currently running animation since we're closing down. 424 if (bounds_animator_.get()) 425 bounds_animator_.reset(); 426 427 if (panel_->GetWebContents()) { 428 // Still have web contents. Allow renderer to shut down. 429 // When web contents are destroyed, we will be called back again. 430 panel_->OnWindowClosing(); 431 return; 432 } 433 434 views::WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(this); 435 436 panel_->OnNativePanelClosed(); 437 if (window_) 438 window_->Close(); 439 window_closed_ = true; 440} 441 442void PanelView::ActivatePanel() { 443 window_->Activate(); 444} 445 446void PanelView::DeactivatePanel() { 447 if (!focused_) 448 return; 449 450#if defined(OS_WIN) 451 // Need custom behavior for always-on-top panels to avoid 452 // the OS activating a minimized panel when this one is 453 // deactivated. 454 if (always_on_top_) { 455 ::SetForegroundWindow(::GetDesktopWindow()); 456 return; 457 } 458#endif 459 460 window_->Deactivate(); 461} 462 463bool PanelView::IsPanelActive() const { 464 return focused_; 465} 466 467void PanelView::PreventActivationByOS(bool prevent_activation) { 468#if defined(OS_WIN) 469 // Set the flags "NoActivate" to make sure the minimized panels do not get 470 // activated by the OS. In addition, set "AppWindow" to make sure the 471 // minimized panels do appear in the taskbar and Alt-Tab menu if it is not 472 // in a stack. 473 int value_to_change = WS_EX_NOACTIVATE; 474 if (!panel_->stack()) 475 value_to_change |= WS_EX_APPWINDOW; 476 if (prevent_activation) 477 UpdateWindowAttribute(GWL_EXSTYLE, value_to_change, 0, false); 478 else 479 UpdateWindowAttribute(GWL_EXSTYLE, 0, value_to_change, false); 480#endif 481} 482 483gfx::NativeWindow PanelView::GetNativePanelWindow() { 484 return window_->GetNativeWindow(); 485} 486 487void PanelView::UpdatePanelTitleBar() { 488 UpdateWindowTitle(); 489 UpdateWindowIcon(); 490} 491 492void PanelView::UpdatePanelLoadingAnimations(bool should_animate) { 493 GetFrameView()->UpdateThrobber(); 494} 495 496void PanelView::PanelWebContentsFocused(content::WebContents* contents) { 497 web_view_->OnWebContentsFocused(contents); 498} 499 500void PanelView::PanelCut() { 501 // Nothing to do since we do not have panel-specific system menu. 502 NOTREACHED(); 503} 504 505void PanelView::PanelCopy() { 506 // Nothing to do since we do not have panel-specific system menu. 507 NOTREACHED(); 508} 509 510void PanelView::PanelPaste() { 511 // Nothing to do since we do not have panel-specific system menu. 512 NOTREACHED(); 513} 514 515void PanelView::DrawAttention(bool draw_attention) { 516 DCHECK((panel_->attention_mode() & Panel::USE_PANEL_ATTENTION) != 0); 517 518 if (is_drawing_attention_ == draw_attention) 519 return; 520 is_drawing_attention_ = draw_attention; 521 GetFrameView()->SchedulePaint(); 522 523 if ((panel_->attention_mode() & Panel::USE_SYSTEM_ATTENTION) != 0) 524 window_->FlashFrame(draw_attention); 525} 526 527bool PanelView::IsDrawingAttention() const { 528 return is_drawing_attention_; 529} 530 531void PanelView::HandlePanelKeyboardEvent( 532 const content::NativeWebKeyboardEvent& event) { 533 views::FocusManager* focus_manager = GetFocusManager(); 534 if (focus_manager->shortcut_handling_suspended()) 535 return; 536 537 ui::Accelerator accelerator( 538 static_cast<ui::KeyboardCode>(event.windowsKeyCode), 539 content::GetModifiersFromNativeWebKeyboardEvent(event)); 540 if (event.type == WebKit::WebInputEvent::KeyUp) 541 accelerator.set_type(ui::ET_KEY_RELEASED); 542 focus_manager->ProcessAccelerator(accelerator); 543} 544 545void PanelView::FullScreenModeChanged(bool is_full_screen) { 546 if (is_full_screen) { 547 if (window_->IsVisible()) 548 window_->Hide(); 549 } else { 550 ShowPanelInactive(); 551 } 552} 553 554bool PanelView::IsPanelAlwaysOnTop() const { 555 return always_on_top_; 556} 557 558void PanelView::SetPanelAlwaysOnTop(bool on_top) { 559 if (always_on_top_ == on_top) 560 return; 561 always_on_top_ = on_top; 562 563 window_->SetAlwaysOnTop(on_top); 564 window_->non_client_view()->Layout(); 565 window_->client_view()->Layout(); 566} 567 568void PanelView::EnableResizeByMouse(bool enable) { 569 // Nothing to do since we use system resizing. 570} 571 572void PanelView::UpdatePanelMinimizeRestoreButtonVisibility() { 573 GetFrameView()->UpdateTitlebarMinimizeRestoreButtonVisibility(); 574} 575 576void PanelView::SetWindowCornerStyle(panel::CornerStyle corner_style) { 577 GetFrameView()->SetWindowCornerStyle(corner_style); 578} 579 580void PanelView::PanelExpansionStateChanging(Panel::ExpansionState old_state, 581 Panel::ExpansionState new_state) { 582#if defined(OS_WIN) 583 // Live preview is only available since Windows 7. 584 if (base::win::GetVersion() < base::win::VERSION_WIN7) 585 return; 586 587 bool is_minimized = old_state != Panel::EXPANDED; 588 bool will_be_minimized = new_state != Panel::EXPANDED; 589 if (is_minimized == will_be_minimized) 590 return; 591 592 HWND native_window = views::HWNDForWidget(window_); 593 594 if (!thumbnailer_.get()) { 595 DCHECK(native_window); 596 thumbnailer_.reset(new TaskbarWindowThumbnailerWin(native_window)); 597 ui::HWNDSubclass::AddFilterToTarget(native_window, thumbnailer_.get()); 598 } 599 600 // Cache the image at this point. 601 if (will_be_minimized) { 602 // If the panel is still active (we will deactivate the minimizd panel at 603 // later time), we need to paint it immediately as inactive so that we can 604 // take a snapshot of inactive panel. 605 if (focused_) { 606 force_to_paint_as_inactive_ = true; 607 ::RedrawWindow(native_window, NULL, NULL, 608 RDW_NOCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW); 609 } 610 611 std::vector<HWND> snapshot_hwnds; 612 thumbnailer_->Start(snapshot_hwnds); 613 } else { 614 force_to_paint_as_inactive_ = false; 615 thumbnailer_->Stop(); 616 } 617 618#endif 619} 620 621gfx::Size PanelView::WindowSizeFromContentSize( 622 const gfx::Size& content_size) const { 623 gfx::Size frame = GetFrameView()->NonClientAreaSize(); 624 return gfx::Size(content_size.width() + frame.width(), 625 content_size.height() + frame.height()); 626} 627 628gfx::Size PanelView::ContentSizeFromWindowSize( 629 const gfx::Size& window_size) const { 630 gfx::Size frame = GetFrameView()->NonClientAreaSize(); 631 return gfx::Size(window_size.width() - frame.width(), 632 window_size.height() - frame.height()); 633} 634 635int PanelView::TitleOnlyHeight() const { 636 return panel::kTitlebarHeight; 637} 638 639void PanelView::MinimizePanelBySystem() { 640 window_->Minimize(); 641} 642 643bool PanelView::IsPanelMinimizedBySystem() const { 644 return window_->IsMinimized(); 645} 646 647void PanelView::AttachWebContents(content::WebContents* contents) { 648 web_view_->SetWebContents(contents); 649} 650 651void PanelView::DetachWebContents(content::WebContents* contents) { 652 web_view_->SetWebContents(NULL); 653} 654 655NativePanelTesting* PanelView::CreateNativePanelTesting() { 656 return new NativePanelTestingWin(this); 657} 658 659void PanelView::OnDisplayChanged() { 660 panel_->manager()->display_settings_provider()->OnDisplaySettingsChanged(); 661} 662 663void PanelView::OnWorkAreaChanged() { 664 panel_->manager()->display_settings_provider()->OnDisplaySettingsChanged(); 665} 666 667bool PanelView::WillProcessWorkAreaChange() const { 668 return true; 669} 670 671views::View* PanelView::GetContentsView() { 672 return this; 673} 674 675views::NonClientFrameView* PanelView::CreateNonClientFrameView( 676 views::Widget* widget) { 677 PanelFrameView* frame_view = new PanelFrameView(this); 678 frame_view->Init(); 679 return frame_view; 680} 681 682bool PanelView::CanResize() const { 683 return true; 684} 685 686bool PanelView::CanMaximize() const { 687 return false; 688} 689 690string16 PanelView::GetWindowTitle() const { 691 return panel_->GetWindowTitle(); 692} 693 694gfx::ImageSkia PanelView::GetWindowAppIcon() { 695 gfx::Image app_icon = panel_->app_icon(); 696 if (app_icon.IsEmpty()) 697 return GetWindowIcon(); 698 else 699 return *app_icon.ToImageSkia(); 700} 701 702gfx::ImageSkia PanelView::GetWindowIcon() { 703 gfx::Image icon = panel_->GetCurrentPageIcon(); 704 return icon.IsEmpty() ? gfx::ImageSkia() : *icon.ToImageSkia(); 705} 706 707void PanelView::WindowClosing() { 708 // When closing a panel via window.close, API or the close button, 709 // ClosePanel() is called first, destroying the native |window_| 710 // which results in this method being called. ClosePanel() sets 711 // |window_closed_| to NULL. 712 // If we still have a |window_closed_| here, the close was triggered by the 713 // OS, (e.g. clicking on taskbar menu), which destroys the native |window_| 714 // without invoking ClosePanel() beforehand. 715 if (!window_closed_) { 716 panel_->OnWindowClosing(); 717 ClosePanel(); 718 DCHECK(window_closed_); 719 } 720} 721 722void PanelView::DeleteDelegate() { 723 delete this; 724} 725 726void PanelView::OnWindowBeginUserBoundsChange() { 727 user_resizing_ = true; 728 panel_->OnPanelStartUserResizing(); 729 730#if defined(OS_WIN) 731 StackedPanelCollection* stack = panel_->stack(); 732 if (stack) { 733 // Listen to WM_SIZING message in order to find out whether the interior 734 // edge is being resized such that the specific maximum size could be 735 // passed to the system. 736 if (panel_->stack()->GetPanelBelow(panel_.get())) { 737 ui::HWNDSubclass::AddFilterToTarget(views::HWNDForWidget(window_), this); 738 user_resizing_interior_stacked_panel_edge_ = false; 739 } 740 741 // Keep track of the original full size of the resizing panel such that it 742 // can be restored to this size once it is shrunk to minimized state. 743 original_full_size_of_resizing_panel_ = panel_->full_size(); 744 745 // Keep track of the original full size of the panel below the resizing 746 // panel such that it can be restored to this size once it is shrunk to 747 // minimized state. 748 Panel* below_panel = stack->GetPanelBelow(panel_.get()); 749 if (below_panel && !below_panel->IsMinimized()) { 750 original_full_size_of_panel_below_resizing_panel_ = 751 below_panel->full_size(); 752 } 753 } 754#endif 755} 756 757void PanelView::OnWindowEndUserBoundsChange() { 758 user_resizing_ = false; 759 panel_->OnPanelEndUserResizing(); 760 761 // No need to proceed with post-resizing update when there is no size change. 762 gfx::Rect new_bounds = window_->GetWindowBoundsInScreen(); 763 if (bounds_ == new_bounds) 764 return; 765 bounds_ = new_bounds; 766 767 panel_->IncreaseMaxSize(bounds_.size()); 768 panel_->set_full_size(bounds_.size()); 769 770#if defined(OS_WIN) 771 StackedPanelCollection* stack = panel_->stack(); 772 if (stack) { 773 // No need to listen to WM_SIZING message any more. 774 ui::HWNDSubclass::RemoveFilterFromAllTargets(this); 775 776 // If the height of resizing panel shrinks close to the titlebar height, 777 // treate it as minimized. This could occur when the user is dragging 778 // 1) the top edge of the top panel downward to shrink it; or 779 // 2) the bottom edge of any panel upward to shrink it. 780 if (panel_->GetBounds().height() < 781 kStackedPanelHeightShrinkThresholdToBecomeMinimized) { 782 stack->MinimizePanel(panel_.get()); 783 panel_->set_full_size(original_full_size_of_resizing_panel_); 784 } 785 786 // If the height of panel below the resizing panel shrinks close to the 787 // titlebar height, treat it as minimized. This could occur when the user 788 // is dragging the bottom edge of non-bottom panel downward to expand it 789 // and also shrink the panel below. 790 Panel* below_panel = stack->GetPanelBelow(panel_.get()); 791 if (below_panel && !below_panel->IsMinimized() && 792 below_panel->GetBounds().height() < 793 kStackedPanelHeightShrinkThresholdToBecomeMinimized) { 794 stack->MinimizePanel(below_panel); 795 below_panel->set_full_size( 796 original_full_size_of_panel_below_resizing_panel_); 797 } 798 } 799#endif 800 801 panel_->collection()->RefreshLayout(); 802} 803 804views::Widget* PanelView::GetWidget() { 805 return window_; 806} 807 808const views::Widget* PanelView::GetWidget() const { 809 return window_; 810} 811 812void PanelView::UpdateLoadingAnimations(bool should_animate) { 813 GetFrameView()->UpdateThrobber(); 814} 815 816void PanelView::UpdateWindowTitle() { 817 window_->UpdateWindowTitle(); 818 GetFrameView()->UpdateTitle(); 819} 820 821void PanelView::UpdateWindowIcon() { 822 window_->UpdateWindowIcon(); 823 GetFrameView()->UpdateIcon(); 824} 825 826void PanelView::Layout() { 827 // |web_view_| might not be created yet when the window is first created. 828 if (web_view_) 829 web_view_->SetBounds(0, 0, width(), height()); 830 OnViewWasResized(); 831} 832 833gfx::Size PanelView::GetMinimumSize() { 834 // If the panel is minimized, it can be rendered to very small size, like 835 // 4-pixel lines when it is docked. Otherwise, its height should not be less 836 // than its titlebar height. 837 return panel_->IsMinimized() ? gfx::Size() : 838 gfx::Size(panel_->min_size().width(), panel::kTitlebarHeight); 839} 840 841gfx::Size PanelView::GetMaximumSize() { 842 // If the user is resizing a stacked panel by its bottom edge, make sure its 843 // height cannot grow more than what the panel below it could offer. This is 844 // because growing a stacked panel by y amount will shrink the panel below it 845 // by same amount and we do not want the panel below it being shrunk to be 846 // smaller than the titlebar. 847#if defined(OS_WIN) 848 if (panel_->stack() && user_resizing_interior_stacked_panel_edge_) { 849 Panel* below_panel = panel_->stack()->GetPanelBelow(panel_.get()); 850 if (below_panel && !below_panel->IsMinimized()) { 851 return gfx::Size(0, below_panel->GetBounds().bottom() - 852 panel_->GetBounds().y() - panel::kTitlebarHeight); 853 } 854 } 855#endif 856 return gfx::Size(); 857} 858 859bool PanelView::AcceleratorPressed(const ui::Accelerator& accelerator) { 860 if (mouse_pressed_ && accelerator.key_code() == ui::VKEY_ESCAPE) { 861 OnTitlebarMouseCaptureLost(); 862 return true; 863 } 864 865 // No other accelerator is allowed when the drag begins. 866 if (mouse_dragging_state_ == DRAGGING_STARTED) 867 return true; 868 869 const std::map<ui::Accelerator, int>& accelerator_table = 870 GetAcceleratorTable(); 871 std::map<ui::Accelerator, int>::const_iterator iter = 872 accelerator_table.find(accelerator); 873 DCHECK(iter != accelerator_table.end()); 874 return panel_->ExecuteCommandIfEnabled(iter->second); 875} 876 877void PanelView::OnWidgetDestroying(views::Widget* widget) { 878 window_ = NULL; 879} 880 881void PanelView::OnWidgetActivationChanged(views::Widget* widget, bool active) { 882#if defined(OS_WIN) 883 // The panel window is in focus (actually accepting keystrokes) if it is 884 // active and belongs to a foreground application. 885 bool focused = active && 886 views::HWNDForWidget(widget) == ::GetForegroundWindow(); 887#else 888 NOTIMPLEMENTED(); 889 bool focused = active; 890#endif 891 892 if (focused_ == focused) 893 return; 894 focused_ = focused; 895 896 // Expand the panel if the minimized panel is activated by means other than 897 // clicking on its titlebar. This is the workaround to support restoring the 898 // minimized panel by other means, like alt-tabbing, win-tabbing, or clicking 899 // the taskbar icon. Note that this workaround does not work for one edge 900 // case: the mouse happens to be at the minimized panel when the user tries to 901 // bring up the panel with the above alternatives. 902 // When the user clicks on the minimized panel, the panel expansion will be 903 // done when we process the mouse button pressed message. 904 if (focused_ && panel_->IsMinimized() && 905 panel_->collection()->type() == PanelCollection::DOCKED && 906 gfx::Screen::GetScreenFor(widget->GetNativeWindow())-> 907 GetWindowAtCursorScreenPoint() != widget->GetNativeWindow()) { 908 panel_->Restore(); 909 } 910 911 panel()->OnActiveStateChanged(focused); 912} 913 914void PanelView::OnWidgetBoundsChanged(views::Widget* widget, 915 const gfx::Rect& new_bounds) { 916 if (user_resizing_) 917 panel()->collection()->OnPanelResizedByMouse(panel(), new_bounds); 918} 919 920void PanelView::OnNativeFocusChange(gfx::NativeView focused_before, 921 gfx::NativeView focused_now) { 922 if (focused_now != window_->GetNativeView()) 923 return; 924 925 // Give web contents view a chance to set focus to the appropriate element. 926 content::WebContents* web_contents = panel_->GetWebContents(); 927 if (web_contents) 928 web_contents->GetView()->RestoreFocus(); 929} 930 931bool PanelView::OnTitlebarMousePressed(const gfx::Point& mouse_location) { 932 mouse_pressed_ = true; 933 mouse_dragging_state_ = NO_DRAGGING; 934 last_mouse_location_ = mouse_location; 935 return true; 936} 937 938bool PanelView::OnTitlebarMouseDragged(const gfx::Point& mouse_location) { 939 if (!mouse_pressed_) 940 return false; 941 942 if (mouse_dragging_state_ == NO_DRAGGING && 943 ExceededDragThreshold(mouse_location - last_mouse_location_)) { 944 // When a drag begins, we do not want to the client area to still receive 945 // the focus. We do not need to do this for the unfocused minimized panel. 946 if (!panel_->IsMinimized()) { 947 old_focused_view_ = GetFocusManager()->GetFocusedView(); 948 GetFocusManager()->SetFocusedView(GetFrameView()); 949 } 950 951 panel_->manager()->StartDragging(panel_.get(), last_mouse_location_); 952 mouse_dragging_state_ = DRAGGING_STARTED; 953 } 954 if (mouse_dragging_state_ == DRAGGING_STARTED) { 955 panel_->manager()->Drag(mouse_location); 956 957 // Once in drag, update |last_mouse_location_| on each drag fragment, since 958 // we already dragged the panel up to the current mouse location. 959 last_mouse_location_ = mouse_location; 960 } 961 return true; 962} 963 964bool PanelView::OnTitlebarMouseReleased(panel::ClickModifier modifier) { 965 if (mouse_dragging_state_ != NO_DRAGGING) { 966 // Ensure dragging a minimized panel does not leave it activated. 967 // Windows activates a panel on mouse-down, regardless of our attempts 968 // to prevent activation of a minimized panel. Now that we know mouse-down 969 // resulted in a mouse-drag, we need to ensure the minimized panel is 970 // deactivated. 971 if (panel_->IsMinimized() && focused_) 972 panel_->Deactivate(); 973 974 if (mouse_dragging_state_ == DRAGGING_STARTED) { 975 // When a drag ends, restore the focus. 976 if (old_focused_view_) { 977 GetFocusManager()->SetFocusedView(old_focused_view_); 978 old_focused_view_ = NULL; 979 } 980 return EndDragging(false); 981 } 982 983 // The panel drag was cancelled before the mouse is released. Do not 984 // treat this as a click. 985 return true; 986 } 987 988 panel_->OnTitlebarClicked(modifier); 989 return true; 990} 991 992bool PanelView::OnTitlebarMouseCaptureLost() { 993 if (mouse_dragging_state_ == DRAGGING_STARTED) 994 return EndDragging(true); 995 return true; 996} 997 998bool PanelView::EndDragging(bool cancelled) { 999 // Only handle clicks that started in our window. 1000 if (!mouse_pressed_) 1001 return false; 1002 mouse_pressed_ = false; 1003 1004 mouse_dragging_state_ = DRAGGING_ENDED; 1005 panel_->manager()->EndDragging(cancelled); 1006 return true; 1007} 1008 1009PanelFrameView* PanelView::GetFrameView() const { 1010 return static_cast<PanelFrameView*>(window_->non_client_view()->frame_view()); 1011} 1012 1013bool PanelView::IsAnimatingBounds() const { 1014 return bounds_animator_.get() && bounds_animator_->is_animating(); 1015} 1016 1017bool PanelView::IsWithinResizingArea(const gfx::Point& mouse_location) const { 1018 gfx::Rect bounds = window_->GetWindowBoundsInScreen(); 1019 DCHECK(bounds.Contains(mouse_location)); 1020 return mouse_location.x() < bounds.x() + kResizeInsideBoundsSize || 1021 mouse_location.x() >= bounds.right() - kResizeInsideBoundsSize || 1022 mouse_location.y() < bounds.y() + kResizeInsideBoundsSize || 1023 mouse_location.y() >= bounds.bottom() - kResizeInsideBoundsSize; 1024} 1025 1026#if defined(OS_WIN) 1027void PanelView::UpdateWindowAttribute(int attribute_index, 1028 int attribute_value_to_set, 1029 int attribute_value_to_reset, 1030 bool update_frame) { 1031 HWND native_window = views::HWNDForWidget(window_); 1032 int value = ::GetWindowLong(native_window, attribute_index); 1033 int expected_value = value; 1034 if (attribute_value_to_set) 1035 expected_value |= attribute_value_to_set; 1036 if (attribute_value_to_reset) 1037 expected_value &= ~attribute_value_to_reset; 1038 if (value != expected_value) 1039 ::SetWindowLong(native_window, attribute_index, expected_value); 1040 1041 // Per MSDN, if any of the frame styles is changed, SetWindowPos with the 1042 // SWP_FRAMECHANGED flag must be called in order for the cached window data 1043 // to be updated properly. 1044 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms633591(v=vs.85).aspx 1045 if (update_frame) { 1046 ::SetWindowPos(native_window, NULL, 0, 0, 0, 0, 1047 SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | 1048 SWP_NOZORDER | SWP_NOACTIVATE); 1049 } 1050} 1051#endif 1052 1053void PanelView::OnViewWasResized() { 1054#if defined(OS_WIN) && !defined(USE_AURA) 1055 content::WebContents* web_contents = panel_->GetWebContents(); 1056 if (!web_view_ || !web_contents) 1057 return; 1058 1059 // When the panel is frameless or has thin frame, the mouse resizing should 1060 // also be triggered from the part of client area that is close to the window 1061 // frame. 1062 int width = web_view_->size().width(); 1063 int height = web_view_->size().height(); 1064 // Compute the thickness of the client area that needs to be counted towards 1065 // mouse resizing. 1066 int thickness_for_mouse_resizing = 1067 kResizeInsideBoundsSize - GetFrameView()->BorderThickness(); 1068 DCHECK(thickness_for_mouse_resizing > 0); 1069 SkRegion* region = new SkRegion; 1070 region->op(0, 0, thickness_for_mouse_resizing, height, SkRegion::kUnion_Op); 1071 region->op(width - thickness_for_mouse_resizing, 0, width, height, 1072 SkRegion::kUnion_Op); 1073 region->op(0, height - thickness_for_mouse_resizing, width, height, 1074 SkRegion::kUnion_Op); 1075 web_contents->GetRenderViewHost()->GetView()->SetClickthroughRegion(region); 1076#endif 1077} 1078