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