Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(257)

Side by Side Diff: chrome/browser/ui/views/panels/panel_view.cc

Issue 2263863002: Remove implementation of Panels on OSes other than ChromeOS. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: CR feedback Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW
« no previous file with comments | « chrome/browser/ui/views/panels/panel_view.h ('k') | chrome/browser/ui/views/panels/panel_view_browsertest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698