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.
5 #include "chrome/browser/ui/views/panels/panel_view.h"
8 #include "base/logging.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/app/chrome_command_ids.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/ui/host_desktop.h"
14 #include "chrome/browser/ui/panels/panel.h"
15 #include "chrome/browser/ui/panels/panel_bounds_animation.h"
16 #include "chrome/browser/ui/panels/panel_manager.h"
17 #include "chrome/browser/ui/panels/stacked_panel_collection.h"
18 #include "chrome/browser/ui/views/auto_keep_alive.h"
19 #include "chrome/browser/ui/views/panels/panel_frame_view.h"
20 #include "content/public/browser/render_view_host.h"
21 #include "content/public/browser/render_widget_host_view.h"
22 #include "content/public/browser/web_contents.h"
23 #include "ui/content_accelerators/accelerator_util.h"
24 #include "ui/gfx/image/image.h"
25 #include "ui/gfx/path.h"
26 #include "ui/gfx/screen.h"
27 #include "ui/views/controls/button/image_button.h"
28 #include "ui/views/controls/webview/webview.h"
29 #include "ui/views/widget/widget.h"
32 #include "base/win/windows_version.h"
33 #include "chrome/browser/shell_integration.h"
34 #include "chrome/browser/ui/views/panels/taskbar_window_thumbnailer_win.h"
35 #include "ui/base/win/shell.h"
36 #include "ui/gfx/icon_util.h"
37 #include "ui/views/win/hwnd_util.h"
40 #if defined(USE_X11) && !defined(OS_CHROMEOS)
41 #include "chrome/browser/shell_integration_linux.h"
42 #include "chrome/browser/ui/views/panels/x11_panel_resizer.h"
43 #include "chrome/browser/web_applications/web_app.h"
44 #include "ui/aura/window.h"
45 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
51 // If the height of a stacked panel shrinks below this threshold during the
52 // user resizing, it will be treated as minimized.
53 const int kStackedPanelHeightShrinkThresholdToBecomeMinimized
=
54 panel::kTitlebarHeight
+ 20;
57 // Supported accelerators.
58 // Note: We can't use the accelerator table defined in chrome/browser/ui/views
59 // due to checkdeps violation.
60 struct AcceleratorMapping
{
61 ui::KeyboardCode keycode
;
65 const AcceleratorMapping kPanelAcceleratorMap
[] = {
66 { ui::VKEY_W
, ui::EF_CONTROL_DOWN
, IDC_CLOSE_WINDOW
},
67 { ui::VKEY_W
, ui::EF_SHIFT_DOWN
| ui::EF_CONTROL_DOWN
, IDC_CLOSE_WINDOW
},
68 { ui::VKEY_F4
, ui::EF_ALT_DOWN
, IDC_CLOSE_WINDOW
},
69 { ui::VKEY_R
, ui::EF_CONTROL_DOWN
, IDC_RELOAD
},
70 { ui::VKEY_F5
, ui::EF_NONE
, IDC_RELOAD
},
71 { ui::VKEY_R
, ui::EF_SHIFT_DOWN
| ui::EF_CONTROL_DOWN
,
72 IDC_RELOAD_IGNORING_CACHE
},
73 { ui::VKEY_F5
, ui::EF_CONTROL_DOWN
, IDC_RELOAD_IGNORING_CACHE
},
74 { ui::VKEY_F5
, ui::EF_SHIFT_DOWN
, IDC_RELOAD_IGNORING_CACHE
},
75 { ui::VKEY_ESCAPE
, ui::EF_NONE
, IDC_STOP
},
76 { ui::VKEY_OEM_MINUS
, ui::EF_CONTROL_DOWN
, IDC_ZOOM_MINUS
},
77 { ui::VKEY_SUBTRACT
, ui::EF_CONTROL_DOWN
, IDC_ZOOM_MINUS
},
78 { ui::VKEY_0
, ui::EF_CONTROL_DOWN
, IDC_ZOOM_NORMAL
},
79 { ui::VKEY_NUMPAD0
, ui::EF_CONTROL_DOWN
, IDC_ZOOM_NORMAL
},
80 { ui::VKEY_OEM_PLUS
, ui::EF_CONTROL_DOWN
, IDC_ZOOM_PLUS
},
81 { ui::VKEY_ADD
, ui::EF_CONTROL_DOWN
, IDC_ZOOM_PLUS
},
82 { ui::VKEY_I
, ui::EF_SHIFT_DOWN
| ui::EF_CONTROL_DOWN
, IDC_DEV_TOOLS
},
83 { ui::VKEY_J
, ui::EF_SHIFT_DOWN
| ui::EF_CONTROL_DOWN
,
84 IDC_DEV_TOOLS_CONSOLE
},
87 const std::map
<ui::Accelerator
, int>& GetAcceleratorTable() {
88 static std::map
<ui::Accelerator
, int>* accelerators
= NULL
;
90 accelerators
= new std::map
<ui::Accelerator
, int>();
91 for (size_t i
= 0; i
< arraysize(kPanelAcceleratorMap
); ++i
) {
92 ui::Accelerator
accelerator(kPanelAcceleratorMap
[i
].keycode
,
93 kPanelAcceleratorMap
[i
].modifiers
);
94 (*accelerators
)[accelerator
] = kPanelAcceleratorMap
[i
].command_id
;
100 // NativePanelTesting implementation.
101 class NativePanelTestingViews
: public NativePanelTesting
{
103 explicit NativePanelTestingViews(PanelView
* panel_view
);
104 ~NativePanelTestingViews() override
;
107 void PressLeftMouseButtonTitlebar(const gfx::Point
& mouse_location
,
108 panel::ClickModifier modifier
) override
;
109 void ReleaseMouseButtonTitlebar(panel::ClickModifier modifier
) override
;
110 void DragTitlebar(const gfx::Point
& mouse_location
) override
;
111 void CancelDragTitlebar() override
;
112 void FinishDragTitlebar() override
;
113 bool VerifyDrawingAttention() const override
;
114 bool VerifyActiveState(bool is_active
) override
;
115 bool VerifyAppIcon() const override
;
116 bool VerifySystemMinimizeState() const override
;
117 bool IsWindowVisible() const override
;
118 bool IsWindowSizeKnown() const override
;
119 bool IsAnimatingBounds() const override
;
120 bool IsButtonVisible(panel::TitlebarButtonType button_type
) const override
;
121 panel::CornerStyle
GetWindowCornerStyle() const override
;
122 bool EnsureApplicationRunOnForeground() override
;
124 PanelView
* panel_view_
;
127 NativePanelTestingViews::NativePanelTestingViews(PanelView
* panel_view
)
128 : panel_view_(panel_view
) {
131 NativePanelTestingViews::~NativePanelTestingViews() {
134 void NativePanelTestingViews::PressLeftMouseButtonTitlebar(
135 const gfx::Point
& mouse_location
, panel::ClickModifier modifier
) {
136 panel_view_
->OnTitlebarMousePressed(mouse_location
);
139 void NativePanelTestingViews::ReleaseMouseButtonTitlebar(
140 panel::ClickModifier modifier
) {
141 panel_view_
->OnTitlebarMouseReleased(modifier
);
144 void NativePanelTestingViews::DragTitlebar(const gfx::Point
& mouse_location
) {
145 panel_view_
->OnTitlebarMouseDragged(mouse_location
);
148 void NativePanelTestingViews::CancelDragTitlebar() {
149 panel_view_
->OnTitlebarMouseCaptureLost();
152 void NativePanelTestingViews::FinishDragTitlebar() {
153 panel_view_
->OnTitlebarMouseReleased(panel::NO_MODIFIER
);
156 bool NativePanelTestingViews::VerifyDrawingAttention() const {
157 base::MessageLoop::current()->RunUntilIdle();
158 return panel_view_
->GetFrameView()->GetPaintState() ==
159 PanelFrameView::PAINT_FOR_ATTENTION
;
162 bool NativePanelTestingViews::VerifyActiveState(bool is_active
) {
163 return panel_view_
->GetFrameView()->GetPaintState() ==
164 (is_active
? PanelFrameView::PAINT_AS_ACTIVE
165 : PanelFrameView::PAINT_AS_INACTIVE
);
168 bool NativePanelTestingViews::VerifyAppIcon() const {
170 // We only care about Windows 7 and later.
171 if (base::win::GetVersion() < base::win::VERSION_WIN7
)
174 HWND native_window
= views::HWNDForWidget(panel_view_
->window());
175 HICON app_icon
= reinterpret_cast<HICON
>(
176 ::SendMessage(native_window
, WM_GETICON
, ICON_BIG
, 0L));
179 scoped_ptr
<SkBitmap
> bitmap(IconUtil::CreateSkBitmapFromHICON(app_icon
));
180 return bitmap
.get() &&
181 bitmap
->width() == panel::kPanelAppIconSize
&&
182 bitmap
->height() == panel::kPanelAppIconSize
;
188 bool NativePanelTestingViews::VerifySystemMinimizeState() const {
190 HWND native_window
= views::HWNDForWidget(panel_view_
->window());
191 WINDOWPLACEMENT placement
;
192 if (!::GetWindowPlacement(native_window
, &placement
))
194 if (placement
.showCmd
== SW_MINIMIZE
|| placement
.showCmd
== SW_SHOWMINIMIZED
)
197 // If the panel window has owner window, as in stacked mode, check its owner
198 // window. Note that owner window, instead of parent window, is returned
199 // though GWL_HWNDPARENT contains 'parent'.
201 reinterpret_cast<HWND
>(::GetWindowLongPtr(native_window
,
203 if (!owner_window
|| !::GetWindowPlacement(owner_window
, &placement
))
205 return placement
.showCmd
== SW_MINIMIZE
||
206 placement
.showCmd
== SW_SHOWMINIMIZED
;
212 bool NativePanelTestingViews::IsWindowVisible() const {
213 return panel_view_
->window()->IsVisible();
216 bool NativePanelTestingViews::IsWindowSizeKnown() const {
220 bool NativePanelTestingViews::IsAnimatingBounds() const {
221 return panel_view_
->IsAnimatingBounds();
224 bool NativePanelTestingViews::IsButtonVisible(
225 panel::TitlebarButtonType button_type
) const {
226 PanelFrameView
* frame_view
= panel_view_
->GetFrameView();
228 switch (button_type
) {
229 case panel::CLOSE_BUTTON
:
230 return frame_view
->close_button()->visible();
231 case panel::MINIMIZE_BUTTON
:
232 return frame_view
->minimize_button()->visible();
233 case panel::RESTORE_BUTTON
:
234 return frame_view
->restore_button()->visible();
241 panel::CornerStyle
NativePanelTestingViews::GetWindowCornerStyle() const {
242 return panel_view_
->GetFrameView()->corner_style();
245 bool NativePanelTestingViews::EnsureApplicationRunOnForeground() {
246 // Not needed on views.
253 NativePanel
* Panel::CreateNativePanel(Panel
* panel
,
254 const gfx::Rect
& bounds
,
255 bool always_on_top
) {
256 return new PanelView(panel
, bounds
, always_on_top
);
259 // The panel window has to be created as always-on-top. We cannot create it
260 // as non-always-on-top and then change it to always-on-top because Windows
261 // system might deny making a window always-on-top if the application is not
262 // a foreground application.
263 PanelView::PanelView(Panel
* panel
, const gfx::Rect
& bounds
, bool always_on_top
)
267 window_closed_(false),
269 always_on_top_(always_on_top
),
271 user_resizing_(false),
273 user_resizing_interior_stacked_panel_edge_(false),
275 mouse_pressed_(false),
276 mouse_dragging_state_(NO_DRAGGING
),
277 is_drawing_attention_(false),
278 force_to_paint_as_inactive_(false),
279 old_focused_view_(NULL
) {
280 window_
= new views::Widget
;
281 views::Widget::InitParams
params(views::Widget::InitParams::TYPE_WINDOW
);
282 params
.delegate
= this;
283 params
.remove_standard_frame
= true;
284 params
.keep_on_top
= always_on_top
;
285 params
.visible_on_all_workspaces
= always_on_top
;
286 params
.bounds
= bounds
;
288 #if defined(USE_X11) && !defined(OS_CHROMEOS)
289 params
.wm_class_name
= web_app::GetWMClassFromAppName(panel
->app_name());
290 params
.wm_class_class
= shell_integration_linux::GetProgramClassName();
293 window_
->Init(params
);
294 window_
->set_frame_type(views::Widget::FRAME_TYPE_FORCE_CUSTOM
);
295 window_
->set_focus_on_creation(false);
296 window_
->AddObserver(this);
298 // Prevent the browser process from shutting down while this window is open.
299 keep_alive_
.reset(new AutoKeepAlive(GetNativePanelWindow()));
301 web_view_
= new views::WebView(NULL
);
302 AddChildView(web_view_
);
304 // Register accelarators supported by panels.
305 views::FocusManager
* focus_manager
= GetFocusManager();
306 const std::map
<ui::Accelerator
, int>& accelerator_table
=
307 GetAcceleratorTable();
308 for (std::map
<ui::Accelerator
, int>::const_iterator iter
=
309 accelerator_table
.begin();
310 iter
!= accelerator_table
.end(); ++iter
) {
311 focus_manager
->RegisterAccelerator(
312 iter
->first
, ui::AcceleratorManager::kNormalPriority
, this);
316 ui::win::SetAppIdForWindow(
317 ShellIntegration::GetAppModelIdForProfile(
318 base::UTF8ToWide(panel
->app_name()), panel
->profile()->GetPath()),
319 views::HWNDForWidget(window_
));
320 ui::win::PreventWindowFromPinning(views::HWNDForWidget(window_
));
323 #if defined(USE_X11) && !defined(OS_CHROMEOS)
324 // Swap the default non client event handler with one which handles resizes
325 // for panels entirely within Chrome. This is needed because it is not
326 // possible to tell when a resize performed by the window manager ends.
327 views::DesktopWindowTreeHostX11
* host
=
328 views::DesktopWindowTreeHostX11::GetHostForXID(
329 window_
->GetNativeView()->GetHost()->GetAcceleratedWidget());
330 scoped_ptr
<ui::EventHandler
> resizer(
331 new X11PanelResizer(panel_
.get(), window_
->GetNativeWindow()));
332 host
->SwapNonClientEventHandler(resizer
.Pass());
336 PanelView::~PanelView() {
339 void PanelView::ShowPanel() {
340 if (window_
->IsVisible())
343 panel_
->manager()->OnPanelAnimationEnded(panel_
.get());
346 void PanelView::ShowPanelInactive() {
347 if (window_
->IsVisible())
349 window_
->ShowInactive();
350 // No animation is used for initial creation of a panel on Win.
351 // Signal immediately that pending actions can be performed.
352 panel_
->manager()->OnPanelAnimationEnded(panel_
.get());
355 gfx::Rect
PanelView::GetPanelBounds() const {
359 void PanelView::SetPanelBounds(const gfx::Rect
& bounds
) {
360 SetBoundsInternal(bounds
, true);
363 void PanelView::SetPanelBoundsInstantly(const gfx::Rect
& bounds
) {
364 SetBoundsInternal(bounds
, false);
367 void PanelView::SetBoundsInternal(const gfx::Rect
& new_bounds
, bool animate
) {
368 if (bounds_
== new_bounds
)
371 bounds_
= new_bounds
;
374 // If no animation is in progress, apply bounds change instantly. Otherwise,
375 // continue the animation with new target bounds.
376 if (!IsAnimatingBounds())
377 SetWidgetBounds(bounds_
);
381 animation_start_bounds_
= window_
->GetWindowBoundsInScreen();
383 bounds_animator_
.reset(new PanelBoundsAnimation(
384 this, panel_
.get(), animation_start_bounds_
, new_bounds
));
385 bounds_animator_
->Start();
389 bool PanelView::FilterMessage(HWND hwnd
,
396 if (w_param
== WMSZ_BOTTOM
)
397 user_resizing_interior_stacked_panel_edge_
= true;
404 void PanelView::AnimationEnded(const gfx::Animation
* animation
) {
405 panel_
->manager()->OnPanelAnimationEnded(panel_
.get());
408 void PanelView::AnimationProgressed(const gfx::Animation
* animation
) {
409 gfx::Rect new_bounds
= bounds_animator_
->CurrentValueBetween(
410 animation_start_bounds_
, bounds_
);
411 SetWidgetBounds(new_bounds
);
414 void PanelView::SetWidgetBounds(const gfx::Rect
& new_bounds
) {
416 // An overlapped window is a top-level window that has a titlebar, border,
417 // and client area. The Windows system will automatically put the shadow
418 // around the whole window. Also the system will enforce the minimum height
419 // (38 pixels based on observation) for the overlapped window such that it
420 // will always has the space for the titlebar.
422 // On contrast, a popup window is a bare minimum window without border and
423 // titlebar by default. It is often used for the popup menu and the window
424 // with short life. The Windows system does not add the shadow around the
425 // whole window though CS_DROPSHADOW class style could be passed to add the
426 // drop shadow which is only around the right and bottom edges.
428 // The height of the title-only or minimized panel is smaller than the minimum
429 // overlapped window height. If the panel still uses the overlapped window
430 // style, Windows system will automatically increase the window height. To
431 // work around this limitation, we temporarily change the window style to
432 // popup when the height to set is smaller than the minimum overlapped window
433 // height and then restore the window style to overlapped when the height
435 static const int kMinimumOverlappedWindowHeight
= 38;
436 gfx::Rect old_bounds
= GetWidget()->GetRestoredBounds();
437 if (old_bounds
.height() > kMinimumOverlappedWindowHeight
&&
438 new_bounds
.height() <= kMinimumOverlappedWindowHeight
) {
439 // When the panel height shrinks below the minimum overlapped window height,
440 // change the window style to popup such that we can show the title-only
441 // and minimized panel without additional height being added by the system.
442 UpdateWindowAttribute(GWL_STYLE
,
444 WS_OVERLAPPED
| WS_THICKFRAME
| WS_SYSMENU
,
446 } else if (old_bounds
.height() <= kMinimumOverlappedWindowHeight
&&
447 new_bounds
.height() > kMinimumOverlappedWindowHeight
) {
448 // Change the window style back to overlappped when the panel height grow
449 // taller than the minimum overlapped window height.
450 UpdateWindowAttribute(GWL_STYLE
,
451 WS_OVERLAPPED
| WS_THICKFRAME
| WS_SYSMENU
,
457 GetWidget()->SetBounds(new_bounds
);
460 void PanelView::ClosePanel() {
461 // We're already closing. Do nothing.
465 if (!panel_
->ShouldCloseWindow())
468 // Cancel any currently running animation since we're closing down.
469 if (bounds_animator_
.get())
470 bounds_animator_
.reset();
472 if (panel_
->GetWebContents()) {
473 // Still have web contents. Allow renderer to shut down.
474 // When web contents are destroyed, we will be called back again.
475 panel_
->OnWindowClosing();
479 panel_
->OnNativePanelClosed();
482 window_closed_
= true;
485 void PanelView::ActivatePanel() {
489 void PanelView::DeactivatePanel() {
494 // Need custom behavior for always-on-top panels to avoid
495 // the OS activating a minimized panel when this one is
497 if (always_on_top_
) {
498 ::SetForegroundWindow(::GetDesktopWindow());
503 window_
->Deactivate();
506 bool PanelView::IsPanelActive() const {
510 void PanelView::PreventActivationByOS(bool prevent_activation
) {
512 // Set the flags "NoActivate" to make sure the minimized panels do not get
513 // activated by the OS. In addition, set "AppWindow" to make sure the
514 // minimized panels do appear in the taskbar and Alt-Tab menu if it is not
516 int value_to_change
= WS_EX_NOACTIVATE
;
517 if (!panel_
->stack())
518 value_to_change
|= WS_EX_APPWINDOW
;
519 if (prevent_activation
)
520 UpdateWindowAttribute(GWL_EXSTYLE
, value_to_change
, 0, false);
522 UpdateWindowAttribute(GWL_EXSTYLE
, 0, value_to_change
, false);
526 gfx::NativeWindow
PanelView::GetNativePanelWindow() {
527 return window_
->GetNativeWindow();
530 void PanelView::UpdatePanelTitleBar() {
535 void PanelView::UpdatePanelLoadingAnimations(bool should_animate
) {
536 GetFrameView()->UpdateThrobber();
539 void PanelView::PanelWebContentsFocused(content::WebContents
* contents
) {
540 web_view_
->OnWebContentsFocused(contents
);
543 void PanelView::PanelCut() {
544 // Nothing to do since we do not have panel-specific system menu.
548 void PanelView::PanelCopy() {
549 // Nothing to do since we do not have panel-specific system menu.
553 void PanelView::PanelPaste() {
554 // Nothing to do since we do not have panel-specific system menu.
558 void PanelView::DrawAttention(bool draw_attention
) {
559 DCHECK((panel_
->attention_mode() & Panel::USE_PANEL_ATTENTION
) != 0);
561 if (is_drawing_attention_
== draw_attention
)
563 is_drawing_attention_
= draw_attention
;
564 GetFrameView()->SchedulePaint();
566 if ((panel_
->attention_mode() & Panel::USE_SYSTEM_ATTENTION
) != 0) {
568 // The default implementation of Widget::FlashFrame only flashes 5 times.
569 // We need more than that.
571 fwi
.cbSize
= sizeof(fwi
);
572 fwi
.hwnd
= views::HWNDForWidget(window_
);
573 if (draw_attention
) {
574 fwi
.dwFlags
= FLASHW_ALL
;
575 fwi
.uCount
= panel::kNumberOfTimesToFlashPanelForAttention
;
578 // TODO(jianli): calling FlashWindowEx with FLASHW_STOP flag for the
579 // panel window has the same problem as the stack window. However,
580 // we cannot take the similar fix since there is no background window
581 // to replace for the regular panel window. More investigation is needed.
582 fwi
.dwFlags
= FLASHW_STOP
;
584 ::FlashWindowEx(&fwi
);
586 window_
->FlashFrame(draw_attention
);
591 bool PanelView::IsDrawingAttention() const {
592 return is_drawing_attention_
;
595 void PanelView::HandlePanelKeyboardEvent(
596 const content::NativeWebKeyboardEvent
& event
) {
597 views::FocusManager
* focus_manager
= GetFocusManager();
598 if (focus_manager
->shortcut_handling_suspended())
601 ui::Accelerator accelerator
=
602 ui::GetAcceleratorFromNativeWebKeyboardEvent(event
);
603 focus_manager
->ProcessAccelerator(accelerator
);
606 void PanelView::FullScreenModeChanged(bool is_full_screen
) {
607 if (is_full_screen
) {
608 if (window_
->IsVisible() && always_on_top_
)
611 if (!window_
->IsVisible()) {
615 // When hiding and showing again a top-most window that belongs to a
616 // background application (i.e. the application is not a foreground one),
617 // the window may loose top-most placement even though its WS_EX_TOPMOST
618 // bit is still set. Re-issuing SetWindowsPos() returns the window to its
619 // top-most placement.
621 window_
->SetAlwaysOnTop(true);
627 bool PanelView::IsPanelAlwaysOnTop() const {
628 return always_on_top_
;
631 void PanelView::SetPanelAlwaysOnTop(bool on_top
) {
632 if (always_on_top_
== on_top
)
634 always_on_top_
= on_top
;
636 window_
->SetAlwaysOnTop(on_top
);
637 window_
->SetVisibleOnAllWorkspaces(on_top
);
638 window_
->non_client_view()->Layout();
639 window_
->client_view()->Layout();
642 void PanelView::UpdatePanelMinimizeRestoreButtonVisibility() {
643 GetFrameView()->UpdateTitlebarMinimizeRestoreButtonVisibility();
646 void PanelView::SetWindowCornerStyle(panel::CornerStyle corner_style
) {
647 GetFrameView()->SetWindowCornerStyle(corner_style
);
650 void PanelView::PanelExpansionStateChanging(Panel::ExpansionState old_state
,
651 Panel::ExpansionState new_state
) {
653 // Live preview is only available since Windows 7.
654 if (base::win::GetVersion() < base::win::VERSION_WIN7
)
657 if (panel_
->collection()->type() != PanelCollection::DOCKED
)
660 bool is_minimized
= old_state
!= Panel::EXPANDED
;
661 bool will_be_minimized
= new_state
!= Panel::EXPANDED
;
662 if (is_minimized
== will_be_minimized
)
665 HWND native_window
= views::HWNDForWidget(window_
);
667 if (!thumbnailer_
.get()) {
668 DCHECK(native_window
);
669 thumbnailer_
.reset(new TaskbarWindowThumbnailerWin(native_window
, NULL
));
672 // Cache the image at this point.
673 if (will_be_minimized
) {
674 // If the panel is still active (we will deactivate the minimizd panel at
675 // later time), we need to paint it immediately as inactive so that we can
676 // take a snapshot of inactive panel.
678 force_to_paint_as_inactive_
= true;
679 ::RedrawWindow(native_window
, NULL
, NULL
,
680 RDW_NOCHILDREN
| RDW_INVALIDATE
| RDW_UPDATENOW
);
683 // Start the thumbnailer and capture the snapshot now.
684 thumbnailer_
->Start();
685 thumbnailer_
->CaptureSnapshot();
687 force_to_paint_as_inactive_
= false;
688 thumbnailer_
->Stop();
694 gfx::Size
PanelView::WindowSizeFromContentSize(
695 const gfx::Size
& content_size
) const {
696 gfx::Size frame
= GetFrameView()->NonClientAreaSize();
697 return gfx::Size(content_size
.width() + frame
.width(),
698 content_size
.height() + frame
.height());
701 gfx::Size
PanelView::ContentSizeFromWindowSize(
702 const gfx::Size
& window_size
) const {
703 gfx::Size frame
= GetFrameView()->NonClientAreaSize();
704 return gfx::Size(window_size
.width() - frame
.width(),
705 window_size
.height() - frame
.height());
708 int PanelView::TitleOnlyHeight() const {
709 return panel::kTitlebarHeight
;
712 void PanelView::MinimizePanelBySystem() {
716 bool PanelView::IsPanelMinimizedBySystem() const {
717 return window_
->IsMinimized();
720 bool PanelView::IsPanelShownOnActiveDesktop() const {
722 // Virtual desktop is not supported by the native Windows system.
730 void PanelView::ShowShadow(bool show
) {
732 // The overlapped window has the shadow while the popup window does not have
734 int overlap_style
= WS_OVERLAPPED
| WS_THICKFRAME
| WS_SYSMENU
;
735 int popup_style
= WS_POPUP
;
736 UpdateWindowAttribute(GWL_STYLE
,
737 show
? overlap_style
: popup_style
,
738 show
? popup_style
: overlap_style
,
743 void PanelView::AttachWebContents(content::WebContents
* contents
) {
744 web_view_
->SetWebContents(contents
);
747 void PanelView::DetachWebContents(content::WebContents
* contents
) {
748 web_view_
->SetWebContents(NULL
);
751 NativePanelTesting
* PanelView::CreateNativePanelTesting() {
752 return new NativePanelTestingViews(this);
755 void PanelView::OnDisplayChanged() {
756 panel_
->manager()->display_settings_provider()->OnDisplaySettingsChanged();
759 void PanelView::OnWorkAreaChanged() {
760 panel_
->manager()->display_settings_provider()->OnDisplaySettingsChanged();
763 bool PanelView::WillProcessWorkAreaChange() const {
767 views::View
* PanelView::GetContentsView() {
771 views::NonClientFrameView
* PanelView::CreateNonClientFrameView(
772 views::Widget
* widget
) {
773 PanelFrameView
* frame_view
= new PanelFrameView(this);
778 bool PanelView::CanResize() const {
782 bool PanelView::CanMaximize() const {
786 bool PanelView::CanMinimize() const {
790 base::string16
PanelView::GetWindowTitle() const {
791 return panel_
->GetWindowTitle();
794 gfx::ImageSkia
PanelView::GetWindowAppIcon() {
795 gfx::Image app_icon
= panel_
->app_icon();
796 if (app_icon
.IsEmpty())
797 return GetWindowIcon();
799 return *app_icon
.ToImageSkia();
802 gfx::ImageSkia
PanelView::GetWindowIcon() {
803 gfx::Image icon
= panel_
->GetCurrentPageIcon();
804 return icon
.IsEmpty() ? gfx::ImageSkia() : *icon
.ToImageSkia();
807 void PanelView::WindowClosing() {
808 // When closing a panel via window.close, API or the close button,
809 // ClosePanel() is called first, destroying the native |window_|
810 // which results in this method being called. ClosePanel() sets
811 // |window_closed_| to NULL.
812 // If we still have a |window_closed_| here, the close was triggered by the
813 // OS, (e.g. clicking on taskbar menu), which destroys the native |window_|
814 // without invoking ClosePanel() beforehand.
815 if (!window_closed_
) {
816 panel_
->OnWindowClosing();
818 DCHECK(window_closed_
);
822 void PanelView::DeleteDelegate() {
826 void PanelView::OnWindowBeginUserBoundsChange() {
827 user_resizing_
= true;
828 panel_
->OnPanelStartUserResizing();
831 StackedPanelCollection
* stack
= panel_
->stack();
833 // Listen to WM_SIZING message in order to find out whether the interior
834 // edge is being resized such that the specific maximum size could be
835 // passed to the system.
836 if (panel_
->stack()->GetPanelBelow(panel_
.get())) {
837 ui::HWNDSubclass::AddFilterToTarget(views::HWNDForWidget(window_
), this);
838 user_resizing_interior_stacked_panel_edge_
= false;
841 // Keep track of the original full size of the resizing panel such that it
842 // can be restored to this size once it is shrunk to minimized state.
843 original_full_size_of_resizing_panel_
= panel_
->full_size();
845 // Keep track of the original full size of the panel below the resizing
846 // panel such that it can be restored to this size once it is shrunk to
848 Panel
* below_panel
= stack
->GetPanelBelow(panel_
.get());
849 if (below_panel
&& !below_panel
->IsMinimized()) {
850 original_full_size_of_panel_below_resizing_panel_
=
851 below_panel
->full_size();
857 void PanelView::OnWindowEndUserBoundsChange() {
858 user_resizing_
= false;
859 panel_
->OnPanelEndUserResizing();
861 // No need to proceed with post-resizing update when there is no size change.
862 gfx::Rect new_bounds
= window_
->GetWindowBoundsInScreen();
863 if (bounds_
== new_bounds
)
865 bounds_
= new_bounds
;
867 panel_
->IncreaseMaxSize(bounds_
.size());
868 panel_
->set_full_size(bounds_
.size());
871 StackedPanelCollection
* stack
= panel_
->stack();
873 // No need to listen to WM_SIZING message any more.
874 ui::HWNDSubclass::RemoveFilterFromAllTargets(this);
876 // If the height of resizing panel shrinks close to the titlebar height,
877 // treate it as minimized. This could occur when the user is dragging
878 // 1) the top edge of the top panel downward to shrink it; or
879 // 2) the bottom edge of any panel upward to shrink it.
880 if (panel_
->GetBounds().height() <
881 kStackedPanelHeightShrinkThresholdToBecomeMinimized
) {
882 stack
->MinimizePanel(panel_
.get());
883 panel_
->set_full_size(original_full_size_of_resizing_panel_
);
886 // If the height of panel below the resizing panel shrinks close to the
887 // titlebar height, treat it as minimized. This could occur when the user
888 // is dragging the bottom edge of non-bottom panel downward to expand it
889 // and also shrink the panel below.
890 Panel
* below_panel
= stack
->GetPanelBelow(panel_
.get());
891 if (below_panel
&& !below_panel
->IsMinimized() &&
892 below_panel
->GetBounds().height() <
893 kStackedPanelHeightShrinkThresholdToBecomeMinimized
) {
894 stack
->MinimizePanel(below_panel
);
895 below_panel
->set_full_size(
896 original_full_size_of_panel_below_resizing_panel_
);
901 panel_
->collection()->RefreshLayout();
904 views::Widget
* PanelView::GetWidget() {
908 const views::Widget
* PanelView::GetWidget() const {
912 void PanelView::UpdateLoadingAnimations(bool should_animate
) {
913 GetFrameView()->UpdateThrobber();
916 void PanelView::UpdateWindowTitle() {
917 window_
->UpdateWindowTitle();
918 GetFrameView()->UpdateTitle();
921 void PanelView::UpdateWindowIcon() {
922 window_
->UpdateWindowIcon();
923 GetFrameView()->UpdateIcon();
926 void PanelView::Layout() {
927 // |web_view_| might not be created yet when the window is first created.
929 web_view_
->SetBounds(0, 0, width(), height());
932 gfx::Size
PanelView::GetMinimumSize() const {
933 // If the panel is minimized, it can be rendered to very small size, like
934 // 4-pixel lines when it is docked. Otherwise, its size should not be less
935 // than its minimum size.
936 return panel_
->IsMinimized() ? gfx::Size() :
937 gfx::Size(panel::kPanelMinWidth
, panel::kPanelMinHeight
);
940 gfx::Size
PanelView::GetMaximumSize() const {
941 // If the user is resizing a stacked panel by its bottom edge, make sure its
942 // height cannot grow more than what the panel below it could offer. This is
943 // because growing a stacked panel by y amount will shrink the panel below it
944 // by same amount and we do not want the panel below it being shrunk to be
945 // smaller than the titlebar.
947 if (panel_
->stack() && user_resizing_interior_stacked_panel_edge_
) {
948 Panel
* below_panel
= panel_
->stack()->GetPanelBelow(panel_
.get());
949 if (below_panel
&& !below_panel
->IsMinimized()) {
950 return gfx::Size(0, below_panel
->GetBounds().bottom() -
951 panel_
->GetBounds().y() - panel::kTitlebarHeight
);
958 bool PanelView::AcceleratorPressed(const ui::Accelerator
& accelerator
) {
959 if (mouse_pressed_
&& accelerator
.key_code() == ui::VKEY_ESCAPE
) {
960 OnTitlebarMouseCaptureLost();
964 // No other accelerator is allowed when the drag begins.
965 if (mouse_dragging_state_
== DRAGGING_STARTED
)
968 const std::map
<ui::Accelerator
, int>& accelerator_table
=
969 GetAcceleratorTable();
970 std::map
<ui::Accelerator
, int>::const_iterator iter
=
971 accelerator_table
.find(accelerator
);
972 DCHECK(iter
!= accelerator_table
.end());
973 return panel_
->ExecuteCommandIfEnabled(iter
->second
);
976 void PanelView::OnWidgetDestroying(views::Widget
* widget
) {
980 void PanelView::OnWidgetActivationChanged(views::Widget
* widget
, bool active
) {
982 // WM_NCACTIVATED could be sent when an active window is being destroyed on
983 // Windows. We need to guard against this.
987 bool focused
= active
;
988 if (chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_NATIVE
) {
989 // The panel window is in focus (actually accepting keystrokes) if it is
990 // active and belongs to a foreground application.
992 views::HWNDForWidget(widget
) == ::GetForegroundWindow();
995 bool focused
= active
;
998 if (focused_
== focused
)
1002 // Expand the panel if the minimized panel is activated by means other than
1003 // clicking on its titlebar. This is the workaround to support restoring the
1004 // minimized panel by other means, like alt-tabbing, win-tabbing, or clicking
1005 // the taskbar icon. Note that this workaround does not work for one edge
1006 // case: the mouse happens to be at the minimized panel when the user tries to
1007 // bring up the panel with the above alternatives.
1008 // When the user clicks on the minimized panel, the panel expansion will be
1009 // done when we process the mouse button pressed message.
1011 if (focused_
&& panel_
->IsMinimized() &&
1012 panel_
->collection()->type() == PanelCollection::DOCKED
&&
1013 gfx::Screen::GetScreenFor(widget
->GetNativeWindow())->
1014 GetWindowUnderCursor() != widget
->GetNativeWindow()) {
1019 panel()->OnActiveStateChanged(focused
);
1021 // Give web contents view a chance to set focus to the appropriate element.
1023 content::WebContents
* web_contents
= panel_
->GetWebContents();
1025 web_contents
->RestoreFocus();
1029 void PanelView::OnWidgetBoundsChanged(views::Widget
* widget
,
1030 const gfx::Rect
& new_bounds
) {
1032 panel()->collection()->OnPanelResizedByMouse(panel(), new_bounds
);
1035 bool PanelView::OnTitlebarMousePressed(const gfx::Point
& mouse_location
) {
1036 mouse_pressed_
= true;
1037 mouse_dragging_state_
= NO_DRAGGING
;
1038 last_mouse_location_
= mouse_location
;
1042 bool PanelView::OnTitlebarMouseDragged(const gfx::Point
& mouse_location
) {
1043 if (!mouse_pressed_
)
1046 if (mouse_dragging_state_
== NO_DRAGGING
&&
1047 ExceededDragThreshold(mouse_location
- last_mouse_location_
)) {
1048 // When a drag begins, we do not want to the client area to still receive
1049 // the focus. We do not need to do this for the unfocused minimized panel.
1050 if (!panel_
->IsMinimized()) {
1051 old_focused_view_
= GetFocusManager()->GetFocusedView();
1052 GetFocusManager()->SetFocusedView(GetFrameView());
1055 panel_
->manager()->StartDragging(panel_
.get(), last_mouse_location_
);
1056 mouse_dragging_state_
= DRAGGING_STARTED
;
1058 if (mouse_dragging_state_
== DRAGGING_STARTED
) {
1059 panel_
->manager()->Drag(mouse_location
);
1061 // Once in drag, update |last_mouse_location_| on each drag fragment, since
1062 // we already dragged the panel up to the current mouse location.
1063 last_mouse_location_
= mouse_location
;
1068 bool PanelView::OnTitlebarMouseReleased(panel::ClickModifier modifier
) {
1069 if (mouse_dragging_state_
!= NO_DRAGGING
) {
1070 // Ensure dragging a minimized panel does not leave it activated.
1071 // Windows activates a panel on mouse-down, regardless of our attempts
1072 // to prevent activation of a minimized panel. Now that we know mouse-down
1073 // resulted in a mouse-drag, we need to ensure the minimized panel is
1075 if (panel_
->IsMinimized() && focused_
)
1076 panel_
->Deactivate();
1078 if (mouse_dragging_state_
== DRAGGING_STARTED
) {
1079 // When a drag ends, restore the focus.
1080 if (old_focused_view_
) {
1081 GetFocusManager()->SetFocusedView(old_focused_view_
);
1082 old_focused_view_
= NULL
;
1084 return EndDragging(false);
1087 // The panel drag was cancelled before the mouse is released. Do not
1088 // treat this as a click.
1092 panel_
->OnTitlebarClicked(modifier
);
1096 bool PanelView::OnTitlebarMouseCaptureLost() {
1097 if (mouse_dragging_state_
== DRAGGING_STARTED
)
1098 return EndDragging(true);
1102 bool PanelView::EndDragging(bool cancelled
) {
1103 // Only handle clicks that started in our window.
1104 if (!mouse_pressed_
)
1106 mouse_pressed_
= false;
1108 mouse_dragging_state_
= DRAGGING_ENDED
;
1109 panel_
->manager()->EndDragging(cancelled
);
1113 PanelFrameView
* PanelView::GetFrameView() const {
1114 return static_cast<PanelFrameView
*>(window_
->non_client_view()->frame_view());
1117 bool PanelView::IsAnimatingBounds() const {
1118 if (bounds_animator_
.get() && bounds_animator_
->is_animating())
1120 StackedPanelCollection
* stack
= panel_
->stack();
1123 return stack
->IsAnimatingPanelBounds(panel_
.get());
1127 void PanelView::UpdateWindowAttribute(int attribute_index
,
1128 int attribute_value_to_set
,
1129 int attribute_value_to_reset
,
1130 bool update_frame
) {
1131 HWND native_window
= views::HWNDForWidget(window_
);
1132 int value
= ::GetWindowLong(native_window
, attribute_index
);
1133 int expected_value
= value
;
1134 if (attribute_value_to_set
)
1135 expected_value
|= attribute_value_to_set
;
1136 if (attribute_value_to_reset
)
1137 expected_value
&= ~attribute_value_to_reset
;
1138 if (value
!= expected_value
)
1139 ::SetWindowLong(native_window
, attribute_index
, expected_value
);
1141 // Per MSDN, if any of the frame styles is changed, SetWindowPos with the
1142 // SWP_FRAMECHANGED flag must be called in order for the cached window data
1143 // to be updated properly.
1144 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms633591(v=vs.85).aspx
1146 ::SetWindowPos(native_window
, NULL
, 0, 0, 0, 0,
1147 SWP_FRAMECHANGED
| SWP_NOMOVE
| SWP_NOSIZE
|
1148 SWP_NOZORDER
| SWP_NOACTIVATE
);