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

Side by Side Diff: chrome/browser/ui/views/panels/panel_stack_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) 2013 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_stack_view.h"
6
7 #include "base/logging.h"
8 #include "base/macros.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "build/build_config.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/ui/panels/panel.h"
13 #include "chrome/browser/ui/panels/panel_manager.h"
14 #include "chrome/browser/ui/panels/stacked_panel_collection.h"
15 #include "chrome/browser/ui/views/panels/panel_view.h"
16 #include "ui/gfx/animation/linear_animation.h"
17 #include "ui/gfx/geometry/rect.h"
18 #include "ui/gfx/image/image_skia.h"
19 #include "ui/views/widget/widget.h"
20
21 #if defined(OS_WIN)
22 #include "base/win/windows_version.h"
23 #include "chrome/browser/shell_integration_win.h"
24 #include "ui/base/win/shell.h"
25 #include "ui/views/win/hwnd_util.h"
26 #endif
27
28 namespace {
29 // These values are experimental and subjective.
30 const int kDefaultFramerateHz = 50;
31 const int kSetBoundsAnimationMs = 180;
32
33 // The widget window that acts as a background window for the stack of panels.
34 class PanelStackWindow : public views::WidgetObserver,
35 public views::WidgetDelegateView {
36 public:
37 PanelStackWindow(const gfx::Rect& bounds,
38 NativePanelStackWindowDelegate* delegate);
39 ~PanelStackWindow() override;
40
41 // Overridden from views::WidgetDelegate:
42 base::string16 GetWindowTitle() const override;
43 gfx::ImageSkia GetWindowAppIcon() override;
44 gfx::ImageSkia GetWindowIcon() override;
45 views::Widget* GetWidget() override;
46 const views::Widget* GetWidget() const override;
47
48 // Overridden from views::WidgetObserver:
49 void OnWidgetClosing(views::Widget* widget) override;
50 void OnWidgetDestroying(views::Widget* widget) override;
51
52 private:
53 views::Widget* window_; // Weak pointer, own us.
54 NativePanelStackWindowDelegate* delegate_; // Weak pointer.
55
56 DISALLOW_COPY_AND_ASSIGN(PanelStackWindow);
57 };
58
59 PanelStackWindow::PanelStackWindow(const gfx::Rect& bounds,
60 NativePanelStackWindowDelegate* delegate)
61 : window_(NULL),
62 delegate_(delegate) {
63 window_ = new views::Widget;
64 views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
65 params.delegate = this;
66 params.remove_standard_frame = true;
67 params.bounds = bounds;
68 window_->Init(params);
69 window_->set_frame_type(views::Widget::FRAME_TYPE_FORCE_CUSTOM);
70 window_->set_focus_on_creation(false);
71 window_->AddObserver(this);
72 window_->ShowInactive();
73 }
74
75 PanelStackWindow::~PanelStackWindow() {
76 }
77
78 base::string16 PanelStackWindow::GetWindowTitle() const {
79 return delegate_ ? delegate_->GetTitle() : base::string16();
80 }
81
82 gfx::ImageSkia PanelStackWindow::GetWindowAppIcon() {
83 if (delegate_) {
84 gfx::Image app_icon = delegate_->GetIcon();
85 if (!app_icon.IsEmpty())
86 return *app_icon.ToImageSkia();
87 }
88 return gfx::ImageSkia();
89 }
90
91 gfx::ImageSkia PanelStackWindow::GetWindowIcon() {
92 return GetWindowAppIcon();
93 }
94
95 views::Widget* PanelStackWindow::GetWidget() {
96 return window_;
97 }
98
99 const views::Widget* PanelStackWindow::GetWidget() const {
100 return window_;
101 }
102
103 void PanelStackWindow::OnWidgetClosing(views::Widget* widget) {
104 delegate_ = NULL;
105 }
106
107 void PanelStackWindow::OnWidgetDestroying(views::Widget* widget) {
108 window_ = NULL;
109 }
110
111 } // namespace
112
113 // static
114 NativePanelStackWindow* NativePanelStackWindow::Create(
115 NativePanelStackWindowDelegate* delegate) {
116 #if defined(OS_WIN)
117 return new PanelStackView(delegate);
118 #else
119 NOTIMPLEMENTED();
120 return NULL;
121 #endif
122 }
123
124 PanelStackView::PanelStackView(NativePanelStackWindowDelegate* delegate)
125 : delegate_(delegate),
126 window_(NULL),
127 is_drawing_attention_(false),
128 animate_bounds_updates_(false),
129 bounds_updates_started_(false) {
130 DCHECK(delegate);
131 views::WidgetFocusManager::GetInstance()->AddFocusChangeListener(this);
132 }
133
134 PanelStackView::~PanelStackView() {
135 #if defined(OS_WIN)
136 ui::HWNDSubclass::RemoveFilterFromAllTargets(this);
137 #endif
138 }
139
140 void PanelStackView::Close() {
141 delegate_ = NULL;
142 if (bounds_animator_)
143 bounds_animator_.reset();
144 if (window_)
145 window_->Close();
146 views::WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(this);
147 }
148
149 void PanelStackView::AddPanel(Panel* panel) {
150 panels_.push_back(panel);
151
152 EnsureWindowCreated();
153 MakeStackWindowOwnPanelWindow(panel, this);
154 UpdateStackWindowBounds();
155
156 window_->UpdateWindowTitle();
157 window_->UpdateWindowIcon();
158 }
159
160 void PanelStackView::RemovePanel(Panel* panel) {
161 if (IsAnimatingPanelBounds()) {
162 // This panel is gone.
163 bounds_updates_.erase(panel);
164
165 // Abort the ongoing animation.
166 bounds_animator_->Stop();
167 }
168
169 panels_.remove(panel);
170
171 MakeStackWindowOwnPanelWindow(panel, NULL);
172 UpdateStackWindowBounds();
173 }
174
175 void PanelStackView::MergeWith(NativePanelStackWindow* another) {
176 PanelStackView* another_stack = static_cast<PanelStackView*>(another);
177
178 for (Panels::const_iterator iter = another_stack->panels_.begin();
179 iter != another_stack->panels_.end(); ++iter) {
180 Panel* panel = *iter;
181 panels_.push_back(panel);
182 MakeStackWindowOwnPanelWindow(panel, this);
183 }
184 another_stack->panels_.clear();
185
186 UpdateStackWindowBounds();
187 }
188
189 bool PanelStackView::IsEmpty() const {
190 return panels_.empty();
191 }
192
193 bool PanelStackView::HasPanel(Panel* panel) const {
194 return std::find(panels_.begin(), panels_.end(), panel) != panels_.end();
195 }
196
197 void PanelStackView::MovePanelsBy(const gfx::Vector2d& delta) {
198 BeginBatchUpdatePanelBounds(false);
199 for (Panels::const_iterator iter = panels_.begin();
200 iter != panels_.end(); ++iter) {
201 Panel* panel = *iter;
202 AddPanelBoundsForBatchUpdate(panel, panel->GetBounds() + delta);
203 }
204 EndBatchUpdatePanelBounds();
205 }
206
207 void PanelStackView::BeginBatchUpdatePanelBounds(bool animate) {
208 // If the batch animation is still in progress, continue the animation
209 // with the new target bounds even we want to update the bounds instantly
210 // this time.
211 if (!bounds_updates_started_) {
212 animate_bounds_updates_ = animate;
213 bounds_updates_started_ = true;
214 }
215 }
216
217 void PanelStackView::AddPanelBoundsForBatchUpdate(Panel* panel,
218 const gfx::Rect& new_bounds) {
219 DCHECK(bounds_updates_started_);
220
221 // No need to track it if no change is needed.
222 if (panel->GetBounds() == new_bounds)
223 return;
224
225 // Old bounds are stored as the map value.
226 bounds_updates_[panel] = panel->GetBounds();
227
228 // New bounds are directly applied to the valued stored in native panel
229 // window.
230 static_cast<PanelView*>(panel->native_panel())->set_cached_bounds_directly(
231 new_bounds);
232 }
233
234 void PanelStackView::EndBatchUpdatePanelBounds() {
235 DCHECK(bounds_updates_started_);
236
237 if (bounds_updates_.empty() || !animate_bounds_updates_) {
238 if (!bounds_updates_.empty()) {
239 UpdatePanelsBounds();
240 bounds_updates_.clear();
241 }
242
243 bounds_updates_started_ = false;
244 NotifyBoundsUpdateCompleted();
245 return;
246 }
247
248 bounds_animator_.reset(new gfx::LinearAnimation(
249 PanelManager::AdjustTimeInterval(kSetBoundsAnimationMs),
250 kDefaultFramerateHz,
251 this));
252 bounds_animator_->Start();
253 }
254
255 void PanelStackView::NotifyBoundsUpdateCompleted() {
256 delegate_->PanelBoundsBatchUpdateCompleted();
257
258 #if defined(OS_WIN)
259 // Refresh the thumbnail each time when any bounds updates are done.
260 RefreshLivePreviewThumbnail();
261 #endif
262 }
263
264 bool PanelStackView::IsAnimatingPanelBounds() const {
265 return bounds_updates_started_ && animate_bounds_updates_;
266 }
267
268 void PanelStackView::Minimize() {
269 #if defined(OS_WIN)
270 // When the stack window is minimized by the system, its snapshot could not
271 // be obtained. We need to capture the snapshot before the minimization.
272 if (thumbnailer_)
273 thumbnailer_->CaptureSnapshot();
274 #endif
275
276 window_->Minimize();
277 }
278
279 bool PanelStackView::IsMinimized() const {
280 return window_ ? window_->IsMinimized() : false;
281 }
282
283 void PanelStackView::DrawSystemAttention(bool draw_attention) {
284 // The underlying call of FlashFrame, FlashWindowEx, seems not to work
285 // correctly if it is called more than once consecutively.
286 if (draw_attention == is_drawing_attention_)
287 return;
288 is_drawing_attention_ = draw_attention;
289
290 #if defined(OS_WIN)
291 // Refresh the thumbnail when a panel could change something for the
292 // attention.
293 RefreshLivePreviewThumbnail();
294
295 if (draw_attention) {
296 // The default implementation of Widget::FlashFrame only flashes 5 times.
297 // We need more than that.
298 FLASHWINFO fwi;
299 fwi.cbSize = sizeof(fwi);
300 fwi.hwnd = views::HWNDForWidget(window_);
301 fwi.dwFlags = FLASHW_ALL;
302 fwi.uCount = panel::kNumberOfTimesToFlashPanelForAttention;
303 fwi.dwTimeout = 0;
304 ::FlashWindowEx(&fwi);
305 } else {
306 // Calling FlashWindowEx with FLASHW_STOP flag does not always work.
307 // Occasionally the taskbar icon could still remain in the flashed state.
308 // To work around this problem, we recreate the underlying window.
309 views::Widget* old_window = window_;
310 window_ = CreateWindowWithBounds(GetStackWindowBounds());
311
312 // New background window should also be minimized if the old one is.
313 if (old_window->IsMinimized())
314 window_->Minimize();
315
316 // Make sure the new background window stays at the same z-order as the old
317 // one.
318 ::SetWindowPos(views::HWNDForWidget(window_),
319 views::HWNDForWidget(old_window),
320 0, 0, 0, 0,
321 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
322 for (Panels::const_iterator iter = panels_.begin();
323 iter != panels_.end(); ++iter) {
324 MakeStackWindowOwnPanelWindow(*iter, this);
325 }
326
327 // Serve the snapshot to the new backgroud window.
328 if (thumbnailer_.get())
329 thumbnailer_->ReplaceWindow(views::HWNDForWidget(window_));
330
331 window_->UpdateWindowTitle();
332 window_->UpdateWindowIcon();
333 old_window->Close();
334 }
335 #else
336 window_->FlashFrame(draw_attention);
337 #endif
338 }
339
340 void PanelStackView::OnPanelActivated(Panel* panel) {
341 // Nothing to do.
342 }
343
344 void PanelStackView::OnNativeFocusChanged(gfx::NativeView focused_now) {
345 // When the user selects the stacked panels via ALT-TAB or WIN-TAB, the
346 // background stack window, instead of the foreground panel window, receives
347 // WM_SETFOCUS message. To deal with this, we listen to the focus change event
348 // and activate the most recently active panel.
349 // Note that OnNativeFocusChanged might be called when window_ has not been
350 // created yet.
351 #if defined(OS_WIN)
352 if (!panels_.empty() && window_ && focused_now == window_->GetNativeView()) {
353 Panel* panel_to_focus =
354 panels_.front()->stack()->most_recently_active_panel();
355 if (panel_to_focus)
356 panel_to_focus->Activate();
357 }
358 #endif
359 }
360
361 void PanelStackView::AnimationEnded(const gfx::Animation* animation) {
362 bounds_updates_started_ = false;
363
364 PanelManager* panel_manager = PanelManager::GetInstance();
365 for (BoundsUpdates::const_iterator iter = bounds_updates_.begin();
366 iter != bounds_updates_.end(); ++iter) {
367 panel_manager->OnPanelAnimationEnded(iter->first);
368 }
369 bounds_updates_.clear();
370
371 NotifyBoundsUpdateCompleted();
372 }
373
374 void PanelStackView::AnimationCanceled(const gfx::Animation* animation) {
375 // When the animation is aborted due to something like one of panels is gone,
376 // update panels to their taget bounds immediately.
377 UpdatePanelsBounds();
378
379 AnimationEnded(animation);
380 }
381
382 void PanelStackView::AnimationProgressed(const gfx::Animation* animation) {
383 UpdatePanelsBounds();
384 }
385
386 void PanelStackView::UpdatePanelsBounds() {
387 #if defined(OS_WIN)
388 // Add an extra count for the background stack window.
389 HDWP defer_update = ::BeginDeferWindowPos(bounds_updates_.size() + 1);
390 #endif
391
392 // Update the bounds for each panel in the update list.
393 gfx::Rect enclosing_bounds;
394 for (BoundsUpdates::const_iterator iter = bounds_updates_.begin();
395 iter != bounds_updates_.end(); ++iter) {
396 Panel* panel = iter->first;
397 gfx::Rect target_bounds = panel->GetBounds();
398 gfx::Rect current_bounds;
399 if (bounds_animator_ && bounds_animator_->is_animating()) {
400 current_bounds = bounds_animator_->CurrentValueBetween(
401 iter->second, target_bounds);
402 } else {
403 current_bounds = target_bounds;
404 }
405
406 PanelView* panel_view = static_cast<PanelView*>(panel->native_panel());
407 #if defined(OS_WIN)
408 DeferUpdateNativeWindowBounds(defer_update,
409 panel_view->window(),
410 current_bounds);
411 #else
412 panel_view->SetPanelBoundsInstantly(current_bounds);
413 #endif
414
415 enclosing_bounds = UnionRects(enclosing_bounds, current_bounds);
416 }
417
418 // Compute the stack window bounds that enclose those panels that are not
419 // in the batch update list.
420 for (Panels::const_iterator iter = panels_.begin();
421 iter != panels_.end(); ++iter) {
422 Panel* panel = *iter;
423 if (bounds_updates_.find(panel) == bounds_updates_.end())
424 enclosing_bounds = UnionRects(enclosing_bounds, panel->GetBounds());
425 }
426
427 // Update the bounds of the background stack window.
428 #if defined(OS_WIN)
429 DeferUpdateNativeWindowBounds(defer_update, window_, enclosing_bounds);
430 #else
431 window_->SetBounds(enclosing_bounds);
432 #endif
433
434 #if defined(OS_WIN)
435 ::EndDeferWindowPos(defer_update);
436 #endif
437 }
438
439 gfx::Rect PanelStackView::GetStackWindowBounds() const {
440 gfx::Rect enclosing_bounds;
441 for (Panels::const_iterator iter = panels_.begin();
442 iter != panels_.end(); ++iter) {
443 Panel* panel = *iter;
444 enclosing_bounds = UnionRects(enclosing_bounds, panel->GetBounds());
445 }
446 return enclosing_bounds;
447 }
448
449 void PanelStackView::UpdateStackWindowBounds() {
450 window_->SetBounds(GetStackWindowBounds());
451
452 #if defined(OS_WIN)
453 // Refresh the thumbnail each time whne the stack window is changed, due to
454 // adding or removing a panel.
455 RefreshLivePreviewThumbnail();
456 #endif
457 }
458
459 // static
460 void PanelStackView::MakeStackWindowOwnPanelWindow(
461 Panel* panel, PanelStackView* stack_window) {
462 #if defined(OS_WIN)
463 // The panel widget window might already be gone when a panel is closed.
464 views::Widget* panel_window =
465 static_cast<PanelView*>(panel->native_panel())->window();
466 if (!panel_window)
467 return;
468
469 HWND native_panel_window = views::HWNDForWidget(panel_window);
470 HWND native_stack_window =
471 stack_window ? views::HWNDForWidget(stack_window->window_) : NULL;
472
473 // The extended style WS_EX_APPWINDOW is used to force a top-level window onto
474 // the taskbar. In order for multiple stacked panels to appear as one, this
475 // bit needs to be cleared.
476 int value = ::GetWindowLong(native_panel_window, GWL_EXSTYLE);
477 ::SetWindowLong(
478 native_panel_window,
479 GWL_EXSTYLE,
480 native_stack_window ? (value & ~WS_EX_APPWINDOW)
481 : (value | WS_EX_APPWINDOW));
482
483 // All the windows that share the same owner window will appear as a single
484 // window on the taskbar.
485 ::SetWindowLongPtr(native_panel_window,
486 GWLP_HWNDPARENT,
487 reinterpret_cast<LONG_PTR>(native_stack_window));
488
489 // Make sure the background stack window always stays behind the panel window.
490 if (native_stack_window) {
491 ::SetWindowPos(native_stack_window, native_panel_window, 0, 0, 0, 0,
492 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
493 }
494
495 #else
496 NOTIMPLEMENTED();
497 #endif
498 }
499
500 views::Widget* PanelStackView::CreateWindowWithBounds(const gfx::Rect& bounds) {
501 PanelStackWindow* stack_window = new PanelStackWindow(bounds, delegate_);
502 views::Widget* window = stack_window->GetWidget();
503
504 #if defined(OS_WIN)
505 DCHECK(!panels_.empty());
506 Panel* panel = panels_.front();
507 ui::win::SetAppIdForWindow(
508 shell_integration::win::GetAppModelIdForProfile(
509 base::UTF8ToWide(panel->app_name()), panel->profile()->GetPath()),
510 views::HWNDForWidget(window));
511
512 // Remove the filter for old window in case that we're recreating the window.
513 ui::HWNDSubclass::RemoveFilterFromAllTargets(this);
514
515 // Listen to WM_MOVING message in order to move all panels windows on top of
516 // the background window altogether when the background window is being moved
517 // by the user.
518 ui::HWNDSubclass::AddFilterToTarget(views::HWNDForWidget(window), this);
519 #endif
520
521 return window;
522 }
523
524 void PanelStackView::EnsureWindowCreated() {
525 if (window_)
526 return;
527
528 // Empty size is not allowed so a temporary small size is passed. SetBounds
529 // will be called later to update the bounds.
530 window_ = CreateWindowWithBounds(gfx::Rect(0, 0, 1, 1));
531
532 #if defined(OS_WIN)
533 if (base::win::GetVersion() >= base::win::VERSION_WIN7) {
534 HWND native_window = views::HWNDForWidget(window_);
535 thumbnailer_.reset(new TaskbarWindowThumbnailerWin(native_window, this));
536 thumbnailer_->Start();
537 }
538 #endif
539 }
540
541 #if defined(OS_WIN)
542 bool PanelStackView::FilterMessage(HWND hwnd,
543 UINT message,
544 WPARAM w_param,
545 LPARAM l_param,
546 LRESULT* l_result) {
547 switch (message) {
548 case WM_MOVING:
549 // When the background window is being moved by the user, all panels
550 // should also move.
551 gfx::Rect new_stack_bounds(*(reinterpret_cast<LPRECT>(l_param)));
552 MovePanelsBy(
553 new_stack_bounds.origin() - panels_.front()->GetBounds().origin());
554 break;
555 }
556 return false;
557 }
558
559 std::vector<HWND> PanelStackView::GetSnapshotWindowHandles() const {
560 std::vector<HWND> native_panel_windows;
561 for (Panels::const_iterator iter = panels_.begin();
562 iter != panels_.end(); ++iter) {
563 Panel* panel = *iter;
564 native_panel_windows.push_back(
565 views::HWNDForWidget(
566 static_cast<PanelView*>(panel->native_panel())->window()));
567 }
568 return native_panel_windows;
569 }
570
571 void PanelStackView::RefreshLivePreviewThumbnail() {
572 // Don't refresh the thumbnail when the stack window is system minimized
573 // because the snapshot could not be retrieved.
574 if (!thumbnailer_.get() || IsMinimized())
575 return;
576 thumbnailer_->InvalidateSnapshot();
577 }
578
579 void PanelStackView::DeferUpdateNativeWindowBounds(HDWP defer_window_pos_info,
580 views::Widget* window,
581 const gfx::Rect& bounds) {
582 ::DeferWindowPos(defer_window_pos_info,
583 views::HWNDForWidget(window),
584 NULL,
585 bounds.x(),
586 bounds.y(),
587 bounds.width(),
588 bounds.height(),
589 SWP_NOACTIVATE | SWP_NOZORDER);
590 }
591 #endif
OLDNEW
« no previous file with comments | « chrome/browser/ui/views/panels/panel_stack_view.h ('k') | chrome/browser/ui/views/panels/panel_view.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698