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