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