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