OLD | NEW |
| (Empty) |
1 // Copyright 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 "ash/common/wm/overview/window_selector_item.h" | |
6 | |
7 #include <algorithm> | |
8 #include <vector> | |
9 | |
10 #include "ash/common/material_design/material_design_controller.h" | |
11 #include "ash/common/metrics/user_metrics_action.h" | |
12 #include "ash/common/wm/overview/cleanup_animation_observer.h" | |
13 #include "ash/common/wm/overview/overview_animation_type.h" | |
14 #include "ash/common/wm/overview/scoped_overview_animation_settings.h" | |
15 #include "ash/common/wm/overview/scoped_overview_animation_settings_factory.h" | |
16 #include "ash/common/wm/overview/scoped_transform_overview_window.h" | |
17 #include "ash/common/wm/overview/window_selector.h" | |
18 #include "ash/common/wm/overview/window_selector_controller.h" | |
19 #include "ash/common/wm/window_state.h" | |
20 #include "ash/common/wm_shell.h" | |
21 #include "ash/common/wm_window.h" | |
22 #include "ash/public/cpp/shell_window_ids.h" | |
23 #include "ash/resources/vector_icons/vector_icons.h" | |
24 #include "ash/root_window_controller.h" | |
25 #include "ash/strings/grit/ash_strings.h" | |
26 #include "base/auto_reset.h" | |
27 #include "base/strings/string_util.h" | |
28 #include "base/strings/utf_string_conversions.h" | |
29 #include "base/time/time.h" | |
30 #include "ui/base/l10n/l10n_util.h" | |
31 #include "ui/compositor/layer_animation_sequence.h" | |
32 #include "ui/compositor/scoped_animation_duration_scale_mode.h" | |
33 #include "ui/gfx/animation/slide_animation.h" | |
34 #include "ui/gfx/canvas.h" | |
35 #include "ui/gfx/color_utils.h" | |
36 #include "ui/gfx/geometry/safe_integer_conversions.h" | |
37 #include "ui/gfx/paint_vector_icon.h" | |
38 #include "ui/gfx/transform_util.h" | |
39 #include "ui/strings/grit/ui_strings.h" | |
40 #include "ui/views/background.h" | |
41 #include "ui/views/border.h" | |
42 #include "ui/views/layout/box_layout.h" | |
43 #include "ui/views/window/non_client_view.h" | |
44 #include "ui/wm/core/shadow.h" | |
45 #include "ui/wm/core/window_util.h" | |
46 | |
47 namespace ash { | |
48 | |
49 namespace { | |
50 | |
51 // In the conceptual overview table, the window margin is the space reserved | |
52 // around the window within the cell. This margin does not overlap so the | |
53 // closest distance between adjacent windows will be twice this amount. | |
54 static const int kWindowMargin = 5; | |
55 | |
56 // Cover the transformed window including the gaps between the windows with a | |
57 // transparent shield to block the input events from reaching the transformed | |
58 // window while in overview. | |
59 static const int kWindowSelectorMargin = kWindowMargin * 2; | |
60 | |
61 // Foreground label color. | |
62 static const SkColor kLabelColor = SK_ColorWHITE; | |
63 | |
64 // TODO(tdanderson): Move this to a central location. | |
65 static const SkColor kCloseButtonColor = SK_ColorWHITE; | |
66 | |
67 // Label background color once in overview mode. | |
68 static const SkColor kLabelBackgroundColor = SkColorSetARGB(25, 255, 255, 255); | |
69 | |
70 // Label background color when exiting overview mode. | |
71 static const SkColor kLabelExitColor = SkColorSetARGB(255, 90, 90, 90); | |
72 | |
73 // Corner radius for the selection tiles. | |
74 static int kLabelBackgroundRadius = 2; | |
75 | |
76 // Horizontal padding for the label, on both sides. | |
77 static const int kHorizontalLabelPadding = 8; | |
78 | |
79 // Height of an item header. | |
80 static const int kHeaderHeight = 32; | |
81 | |
82 // Opacity for dimmed items. | |
83 static const float kDimmedItemOpacity = 0.5f; | |
84 | |
85 // Opacity for fading out during closing a window. | |
86 static const float kClosingItemOpacity = 0.8f; | |
87 | |
88 // Opacity for the item header. | |
89 static const float kHeaderOpacity = | |
90 (SkColorGetA(kLabelBackgroundColor) / 255.f); | |
91 | |
92 // Duration it takes for the header to shift from opaque header color to | |
93 // |kLabelBackgroundColor|. | |
94 static const int kSelectorColorSlideMilliseconds = 240; | |
95 | |
96 // Duration of background opacity transition for the selected label. | |
97 static const int kSelectorFadeInMilliseconds = 350; | |
98 | |
99 // Duration of background opacity transition when exiting overview mode. | |
100 static const int kExitFadeInMilliseconds = 30; | |
101 | |
102 // Before closing a window animate both the window and the caption to shrink by | |
103 // this fraction of size. | |
104 static const float kPreCloseScale = 0.02f; | |
105 | |
106 // Convenience method to fade in a Window with predefined animation settings. | |
107 // Note: The fade in animation will occur after a delay where the delay is how | |
108 // long the lay out animations take. | |
109 void SetupFadeInAfterLayout(views::Widget* widget) { | |
110 WmWindow* window = WmWindow::Get(widget->GetNativeWindow()); | |
111 window->SetOpacity(0.0f); | |
112 std::unique_ptr<ScopedOverviewAnimationSettings> | |
113 scoped_overview_animation_settings = | |
114 ScopedOverviewAnimationSettingsFactory::Get() | |
115 ->CreateOverviewAnimationSettings( | |
116 OverviewAnimationType:: | |
117 OVERVIEW_ANIMATION_ENTER_OVERVIEW_MODE_FADE_IN, | |
118 window); | |
119 window->SetOpacity(1.0f); | |
120 } | |
121 | |
122 // A Button that has a listener and listens to mouse clicks on the visible part | |
123 // of an overview window. | |
124 class ShieldButton : public views::CustomButton { | |
125 public: | |
126 ShieldButton(views::ButtonListener* listener, const base::string16& name) | |
127 : views::CustomButton(listener) { | |
128 SetAccessibleName(name); | |
129 } | |
130 ~ShieldButton() override {} | |
131 | |
132 // When WindowSelectorItem (which is a ButtonListener) is destroyed, its | |
133 // |item_widget_| is allowed to stay around to complete any animations. | |
134 // Resetting the listener in all views that are targeted by events is | |
135 // necessary to prevent a crash when a user clicks on the fading out widget | |
136 // after the WindowSelectorItem has been destroyed. | |
137 void ResetListener() { listener_ = nullptr; } | |
138 | |
139 protected: | |
140 // views::View: | |
141 const char* GetClassName() const override { return "ShieldButton"; } | |
142 | |
143 private: | |
144 DISALLOW_COPY_AND_ASSIGN(ShieldButton); | |
145 }; | |
146 | |
147 } // namespace | |
148 | |
149 WindowSelectorItem::OverviewCloseButton::OverviewCloseButton( | |
150 views::ButtonListener* listener) | |
151 : views::ImageButton(listener) { | |
152 SetImage(views::CustomButton::STATE_NORMAL, | |
153 gfx::CreateVectorIcon(kWindowControlCloseIcon, kCloseButtonColor)); | |
154 SetImageAlignment(views::ImageButton::ALIGN_CENTER, | |
155 views::ImageButton::ALIGN_MIDDLE); | |
156 SetMinimumImageSize(gfx::Size(kHeaderHeight, kHeaderHeight)); | |
157 } | |
158 | |
159 WindowSelectorItem::OverviewCloseButton::~OverviewCloseButton() {} | |
160 | |
161 // A View having rounded top corners and a specified background color which is | |
162 // only painted within the bounds defined by the rounded corners. | |
163 // This class coordinates the transitions of the overview mode header when | |
164 // entering the overview mode. Those animations are: | |
165 // - Opacity animation. The header is initially same color as the original | |
166 // window's header. It starts as transparent and is faded in. When the full | |
167 // opacity is reached the original header is hidden (which is nearly | |
168 // imperceptable because this view obscures the original header) and a color | |
169 // animation starts. | |
170 // - Color animation is used to change the color from the opaque color of the | |
171 // original window's header to semi-transparent color of the overview mode | |
172 // header (on entry to overview). It is also used on exit from overview to | |
173 // quickly change the color to a close opaque color in parallel with an | |
174 // opacity transition to mask the original header reappearing. | |
175 class WindowSelectorItem::RoundedContainerView | |
176 : public views::View, | |
177 public gfx::AnimationDelegate, | |
178 public ui::LayerAnimationObserver { | |
179 public: | |
180 RoundedContainerView(WindowSelectorItem* item, | |
181 WmWindow* item_window, | |
182 int corner_radius, | |
183 SkColor background) | |
184 : item_(item), | |
185 item_window_(item_window), | |
186 corner_radius_(corner_radius), | |
187 initial_color_(background), | |
188 target_color_(background), | |
189 current_value_(0), | |
190 layer_(nullptr), | |
191 animation_(new gfx::SlideAnimation(this)) { | |
192 SetPaintToLayer(); | |
193 layer()->SetFillsBoundsOpaquely(false); | |
194 } | |
195 | |
196 ~RoundedContainerView() override { StopObservingLayerAnimations(); } | |
197 | |
198 void OnItemRestored() { | |
199 item_ = nullptr; | |
200 item_window_ = nullptr; | |
201 } | |
202 | |
203 // Starts observing layer animations so that actions can be taken when | |
204 // particular animations (opacity) complete. It should only be called once | |
205 // when the initial fade in animation is started. | |
206 void ObserveLayerAnimations(ui::Layer* layer) { | |
207 DCHECK(!layer_); | |
208 layer_ = layer; | |
209 layer_->GetAnimator()->AddObserver(this); | |
210 } | |
211 | |
212 // Stops observing layer animations | |
213 void StopObservingLayerAnimations() { | |
214 if (!layer_) | |
215 return; | |
216 layer_->GetAnimator()->RemoveObserver(this); | |
217 layer_ = nullptr; | |
218 } | |
219 | |
220 // Used by tests to set animation state. | |
221 gfx::SlideAnimation* animation() { return animation_.get(); } | |
222 | |
223 void set_color(SkColor target_color) { target_color_ = target_color; } | |
224 | |
225 // Starts a color animation using |tween_type|. The animation will change the | |
226 // color from |initial_color_| to |target_color_| over |duration| specified | |
227 // in milliseconds. | |
228 // This animation can start once the implicit layer fade-in opacity animation | |
229 // is completed. It is used to transition color from the opaque original | |
230 // window header color to |kLabelBackgroundColor| on entry into overview mode | |
231 // and from |kLabelBackgroundColor| back to the original window header color | |
232 // on exit from the overview mode. | |
233 void AnimateColor(gfx::Tween::Type tween_type, int duration) { | |
234 DCHECK(!layer_); // layer animations should be completed. | |
235 animation_->SetSlideDuration(duration); | |
236 animation_->SetTweenType(tween_type); | |
237 animation_->Reset(0); | |
238 animation_->Show(); | |
239 | |
240 // Tests complete animations immediately. Emulate by invoking the callback. | |
241 if (ui::ScopedAnimationDurationScaleMode::duration_scale_mode() == | |
242 ui::ScopedAnimationDurationScaleMode::ZERO_DURATION) { | |
243 AnimationEnded(animation_.get()); | |
244 } | |
245 } | |
246 | |
247 // Changes the view opacity by animating its background color. The animation | |
248 // will change the alpha value in |target_color_| from its current value to | |
249 // |opacity| * 255 but preserve the RGB values. | |
250 void AnimateBackgroundOpacity(float opacity) { | |
251 animation_->SetSlideDuration(kSelectorFadeInMilliseconds); | |
252 animation_->SetTweenType(gfx::Tween::EASE_OUT); | |
253 animation_->Reset(0); | |
254 animation_->Show(); | |
255 target_color_ = SkColorSetA(target_color_, opacity * 255); | |
256 } | |
257 | |
258 // views::View: | |
259 void OnPaint(gfx::Canvas* canvas) override { | |
260 views::View::OnPaint(canvas); | |
261 SkScalar radius = SkIntToScalar(corner_radius_); | |
262 const SkScalar kRadius[8] = {radius, radius, radius, radius, 0, 0, 0, 0}; | |
263 SkPath path; | |
264 gfx::Rect bounds(size()); | |
265 path.addRoundRect(gfx::RectToSkRect(bounds), kRadius); | |
266 | |
267 cc::PaintFlags flags; | |
268 flags.setAntiAlias(true); | |
269 canvas->ClipPath(path, true); | |
270 | |
271 SkColor target_color = initial_color_; | |
272 if (target_color_ != target_color) { | |
273 target_color = color_utils::AlphaBlend(target_color_, initial_color_, | |
274 current_value_); | |
275 } | |
276 canvas->DrawColor(target_color); | |
277 } | |
278 | |
279 const char* GetClassName() const override { return "RoundedContainerView"; } | |
280 | |
281 private: | |
282 // gfx::AnimationDelegate: | |
283 void AnimationEnded(const gfx::Animation* animation) override { | |
284 initial_color_ = target_color_; | |
285 // Tabbed browser windows show the overview mode header behind the window | |
286 // during the initial animation. Once the initial fade-in completes and the | |
287 // overview header is fully exposed update stacking to keep the label above | |
288 // the item which prevents input events from reaching the window. | |
289 WmWindow* widget_window = WmWindow::Get(GetWidget()->GetNativeWindow()); | |
290 if (widget_window && item_window_) | |
291 widget_window->GetParent()->StackChildAbove(widget_window, item_window_); | |
292 item_window_ = nullptr; | |
293 } | |
294 | |
295 void AnimationProgressed(const gfx::Animation* animation) override { | |
296 current_value_ = animation_->CurrentValueBetween(0, 255); | |
297 SchedulePaint(); | |
298 } | |
299 | |
300 void AnimationCanceled(const gfx::Animation* animation) override { | |
301 item_window_ = nullptr; | |
302 initial_color_ = target_color_; | |
303 current_value_ = 255; | |
304 SchedulePaint(); | |
305 } | |
306 | |
307 // ui::LayerAnimationObserver: | |
308 void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override { | |
309 if (0 != (sequence->properties() & | |
310 ui::LayerAnimationElement::AnimatableProperty::OPACITY)) { | |
311 if (item_) | |
312 item_->HideHeader(); | |
313 StopObservingLayerAnimations(); | |
314 AnimateColor(gfx::Tween::EASE_IN, kSelectorColorSlideMilliseconds); | |
315 } | |
316 } | |
317 | |
318 void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override { | |
319 if (0 != (sequence->properties() & | |
320 ui::LayerAnimationElement::AnimatableProperty::OPACITY)) { | |
321 StopObservingLayerAnimations(); | |
322 } | |
323 } | |
324 | |
325 void OnLayerAnimationScheduled( | |
326 ui::LayerAnimationSequence* sequence) override {} | |
327 | |
328 WindowSelectorItem* item_; | |
329 WmWindow* item_window_; | |
330 int corner_radius_; | |
331 SkColor initial_color_; | |
332 SkColor target_color_; | |
333 int current_value_; | |
334 ui::Layer* layer_; | |
335 std::unique_ptr<gfx::SlideAnimation> animation_; | |
336 | |
337 DISALLOW_COPY_AND_ASSIGN(RoundedContainerView); | |
338 }; | |
339 | |
340 // A Container View that has a ShieldButton to listen to events. The | |
341 // ShieldButton covers most of the View except for the transparent gap between | |
342 // the windows and is visually transparent. The ShieldButton owns a background | |
343 // non-transparent view positioned at the ShieldButton top. The background view | |
344 // in its turn owns an item text label and a close button. | |
345 // The text label does not receive events, however the close button is higher in | |
346 // Z-order than its parent and receives events forwarding them to the same | |
347 // |listener| (i.e. WindowSelectorItem::ButtonPressed()). | |
348 class WindowSelectorItem::CaptionContainerView : public views::View { | |
349 public: | |
350 CaptionContainerView(ButtonListener* listener, | |
351 views::Label* label, | |
352 views::ImageButton* close_button, | |
353 WindowSelectorItem::RoundedContainerView* background) | |
354 : listener_button_(new ShieldButton(listener, label->text())), | |
355 background_(background), | |
356 label_(label), | |
357 close_button_(close_button) { | |
358 background_->AddChildView(label_); | |
359 background_->AddChildView(close_button_); | |
360 listener_button_->AddChildView(background_); | |
361 AddChildView(listener_button_); | |
362 } | |
363 | |
364 ShieldButton* listener_button() { return listener_button_; } | |
365 | |
366 protected: | |
367 // views::View: | |
368 void Layout() override { | |
369 // Position close button in the top right corner sized to its icon size and | |
370 // the label in the top left corner as tall as the button and extending to | |
371 // the button's left edge. | |
372 // The rest of this container view serves as a shield to prevent input | |
373 // events from reaching the transformed window in overview. | |
374 gfx::Rect bounds(GetLocalBounds()); | |
375 bounds.Inset(kWindowSelectorMargin, kWindowSelectorMargin); | |
376 listener_button_->SetBoundsRect(bounds); | |
377 | |
378 const int visible_height = close_button_->GetPreferredSize().height(); | |
379 gfx::Rect background_bounds(gfx::Rect(bounds.size())); | |
380 background_bounds.set_height(visible_height); | |
381 background_->SetBoundsRect(background_bounds); | |
382 | |
383 bounds = background_bounds; | |
384 bounds.Inset(kHorizontalLabelPadding, 0, | |
385 kHorizontalLabelPadding + visible_height, 0); | |
386 label_->SetBoundsRect(bounds); | |
387 | |
388 bounds = background_bounds; | |
389 bounds.set_x(bounds.width() - visible_height); | |
390 bounds.set_width(visible_height); | |
391 close_button_->SetBoundsRect(bounds); | |
392 } | |
393 | |
394 const char* GetClassName() const override { return "CaptionContainerView"; } | |
395 | |
396 private: | |
397 ShieldButton* listener_button_; | |
398 WindowSelectorItem::RoundedContainerView* background_; | |
399 views::Label* label_; | |
400 views::ImageButton* close_button_; | |
401 | |
402 DISALLOW_COPY_AND_ASSIGN(CaptionContainerView); | |
403 }; | |
404 | |
405 WindowSelectorItem::WindowSelectorItem(WmWindow* window, | |
406 WindowSelector* window_selector) | |
407 : dimmed_(false), | |
408 root_window_(window->GetRootWindow()), | |
409 transform_window_(window), | |
410 in_bounds_update_(false), | |
411 selected_(false), | |
412 caption_container_view_(nullptr), | |
413 label_view_(nullptr), | |
414 close_button_(new OverviewCloseButton(this)), | |
415 window_selector_(window_selector), | |
416 background_view_(nullptr) { | |
417 CreateWindowLabel(window->GetTitle()); | |
418 GetWindow()->aura_window()->AddObserver(this); | |
419 } | |
420 | |
421 WindowSelectorItem::~WindowSelectorItem() { | |
422 GetWindow()->aura_window()->RemoveObserver(this); | |
423 } | |
424 | |
425 WmWindow* WindowSelectorItem::GetWindow() { | |
426 return transform_window_.window(); | |
427 } | |
428 | |
429 void WindowSelectorItem::RestoreWindow() { | |
430 caption_container_view_->listener_button()->ResetListener(); | |
431 close_button_->ResetListener(); | |
432 transform_window_.RestoreWindow(); | |
433 if (background_view_) { | |
434 background_view_->OnItemRestored(); | |
435 background_view_ = nullptr; | |
436 } | |
437 UpdateHeaderLayout( | |
438 HeaderFadeInMode::EXIT, | |
439 OverviewAnimationType::OVERVIEW_ANIMATION_LAY_OUT_SELECTOR_ITEMS); | |
440 } | |
441 | |
442 void WindowSelectorItem::EnsureVisible() { | |
443 transform_window_.EnsureVisible(); | |
444 } | |
445 | |
446 void WindowSelectorItem::Shutdown() { | |
447 if (transform_window_.GetTopInset()) { | |
448 // Activating a window (even when it is the window that was active before | |
449 // overview) results in stacking it at the top. Maintain the label window | |
450 // stacking position above the item to make the header transformation more | |
451 // gradual upon exiting the overview mode. | |
452 WmWindow* widget_window = WmWindow::Get(item_widget_->GetNativeWindow()); | |
453 | |
454 // |widget_window| was originally created in the same container as the | |
455 // |transform_window_| but when closing overview the |transform_window_| | |
456 // could have been reparented if a drag was active. Only change stacking | |
457 // if the windows still belong to the same container. | |
458 if (widget_window->GetParent() == transform_window_.window()->GetParent()) { | |
459 widget_window->GetParent()->StackChildAbove(widget_window, | |
460 transform_window_.window()); | |
461 } | |
462 } | |
463 if (background_view_) { | |
464 background_view_->OnItemRestored(); | |
465 background_view_ = nullptr; | |
466 } | |
467 FadeOut(std::move(item_widget_)); | |
468 } | |
469 | |
470 void WindowSelectorItem::PrepareForOverview() { | |
471 transform_window_.PrepareForOverview(); | |
472 UpdateHeaderLayout(HeaderFadeInMode::ENTER, | |
473 OverviewAnimationType::OVERVIEW_ANIMATION_NONE); | |
474 } | |
475 | |
476 bool WindowSelectorItem::Contains(const WmWindow* target) const { | |
477 return transform_window_.Contains(target); | |
478 } | |
479 | |
480 void WindowSelectorItem::SetBounds(const gfx::Rect& target_bounds, | |
481 OverviewAnimationType animation_type) { | |
482 if (in_bounds_update_) | |
483 return; | |
484 base::AutoReset<bool> auto_reset_in_bounds_update(&in_bounds_update_, true); | |
485 target_bounds_ = target_bounds; | |
486 | |
487 gfx::Rect inset_bounds(target_bounds); | |
488 inset_bounds.Inset(kWindowMargin, kWindowMargin); | |
489 SetItemBounds(inset_bounds, animation_type); | |
490 | |
491 // SetItemBounds is called before UpdateHeaderLayout so the header can | |
492 // properly use the updated windows bounds. | |
493 UpdateHeaderLayout(HeaderFadeInMode::UPDATE, animation_type); | |
494 } | |
495 | |
496 void WindowSelectorItem::SetSelected(bool selected) { | |
497 selected_ = selected; | |
498 background_view_->AnimateBackgroundOpacity(selected ? 0.f : kHeaderOpacity); | |
499 } | |
500 | |
501 void WindowSelectorItem::SendAccessibleSelectionEvent() { | |
502 caption_container_view_->listener_button()->NotifyAccessibilityEvent( | |
503 ui::AX_EVENT_SELECTION, true); | |
504 } | |
505 | |
506 void WindowSelectorItem::CloseWindow() { | |
507 gfx::Rect inset_bounds(target_bounds_); | |
508 inset_bounds.Inset(target_bounds_.width() * kPreCloseScale, | |
509 target_bounds_.height() * kPreCloseScale); | |
510 OverviewAnimationType animation_type = | |
511 OverviewAnimationType::OVERVIEW_ANIMATION_CLOSING_SELECTOR_ITEM; | |
512 // Scale down both the window and label. | |
513 SetBounds(inset_bounds, animation_type); | |
514 // First animate opacity to an intermediate value concurrently with the | |
515 // scaling animation. | |
516 AnimateOpacity(kClosingItemOpacity, animation_type); | |
517 | |
518 // Fade out the window and the label, effectively hiding them. | |
519 AnimateOpacity(0.0, | |
520 OverviewAnimationType::OVERVIEW_ANIMATION_CLOSE_SELECTOR_ITEM); | |
521 transform_window_.Close(); | |
522 } | |
523 | |
524 void WindowSelectorItem::HideHeader() { | |
525 transform_window_.HideHeader(); | |
526 } | |
527 | |
528 void WindowSelectorItem::OnMinimizedStateChanged() { | |
529 transform_window_.UpdateMirrorWindowForMinimizedState(); | |
530 } | |
531 | |
532 void WindowSelectorItem::SetDimmed(bool dimmed) { | |
533 dimmed_ = dimmed; | |
534 SetOpacity(dimmed ? kDimmedItemOpacity : 1.0f); | |
535 } | |
536 | |
537 void WindowSelectorItem::ButtonPressed(views::Button* sender, | |
538 const ui::Event& event) { | |
539 if (sender == close_button_) { | |
540 WmShell::Get()->RecordUserMetricsAction(UMA_WINDOW_OVERVIEW_CLOSE_BUTTON); | |
541 CloseWindow(); | |
542 return; | |
543 } | |
544 CHECK(sender == caption_container_view_->listener_button()); | |
545 window_selector_->SelectWindow(this); | |
546 } | |
547 | |
548 void WindowSelectorItem::OnWindowDestroying(aura::Window* window) { | |
549 window->RemoveObserver(this); | |
550 transform_window_.OnWindowDestroyed(); | |
551 } | |
552 | |
553 void WindowSelectorItem::OnWindowTitleChanged(aura::Window* window) { | |
554 // TODO(flackr): Maybe add the new title to a vector of titles so that we can | |
555 // filter any of the titles the window had while in the overview session. | |
556 label_view_->SetText(window->GetTitle()); | |
557 UpdateAccessibilityName(); | |
558 } | |
559 | |
560 float WindowSelectorItem::GetItemScale(const gfx::Size& size) { | |
561 gfx::Size inset_size(size.width(), size.height() - 2 * kWindowMargin); | |
562 return ScopedTransformOverviewWindow::GetItemScale( | |
563 transform_window_.GetTargetBoundsInScreen().size(), inset_size, | |
564 transform_window_.GetTopInset(), | |
565 close_button_->GetPreferredSize().height()); | |
566 } | |
567 | |
568 gfx::Rect WindowSelectorItem::GetTargetBoundsInScreen() const { | |
569 return transform_window_.GetTargetBoundsInScreen(); | |
570 } | |
571 | |
572 void WindowSelectorItem::SetItemBounds(const gfx::Rect& target_bounds, | |
573 OverviewAnimationType animation_type) { | |
574 DCHECK(root_window_ == GetWindow()->GetRootWindow()); | |
575 gfx::Rect screen_rect = transform_window_.GetTargetBoundsInScreen(); | |
576 | |
577 // Avoid division by zero by ensuring screen bounds is not empty. | |
578 gfx::Size screen_size(screen_rect.size()); | |
579 screen_size.SetToMax(gfx::Size(1, 1)); | |
580 screen_rect.set_size(screen_size); | |
581 | |
582 const int top_view_inset = transform_window_.GetTopInset(); | |
583 const int title_height = close_button_->GetPreferredSize().height(); | |
584 gfx::Rect selector_item_bounds = | |
585 ScopedTransformOverviewWindow::ShrinkRectToFitPreservingAspectRatio( | |
586 screen_rect, target_bounds, top_view_inset, title_height); | |
587 gfx::Transform transform = ScopedTransformOverviewWindow::GetTransformForRect( | |
588 screen_rect, selector_item_bounds); | |
589 ScopedTransformOverviewWindow::ScopedAnimationSettings animation_settings; | |
590 transform_window_.BeginScopedAnimation(animation_type, &animation_settings); | |
591 transform_window_.SetTransform(root_window_, transform); | |
592 } | |
593 | |
594 void WindowSelectorItem::SetOpacity(float opacity) { | |
595 item_widget_->SetOpacity(opacity); | |
596 if (background_view_) { | |
597 background_view_->AnimateBackgroundOpacity( | |
598 selected_ ? 0.f : kHeaderOpacity * opacity); | |
599 } | |
600 transform_window_.SetOpacity(opacity); | |
601 } | |
602 | |
603 void WindowSelectorItem::CreateWindowLabel(const base::string16& title) { | |
604 background_view_ = new RoundedContainerView(this, transform_window_.window(), | |
605 kLabelBackgroundRadius, | |
606 transform_window_.GetTopColor()); | |
607 // |background_view_| will get added as a child to CaptionContainerView. | |
608 views::Widget::InitParams params_label; | |
609 params_label.type = views::Widget::InitParams::TYPE_POPUP; | |
610 params_label.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
611 params_label.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; | |
612 params_label.visible_on_all_workspaces = true; | |
613 params_label.layer_type = ui::LAYER_NOT_DRAWN; | |
614 params_label.name = "OverviewModeLabel"; | |
615 params_label.activatable = | |
616 views::Widget::InitParams::Activatable::ACTIVATABLE_DEFAULT; | |
617 params_label.accept_events = true; | |
618 item_widget_.reset(new views::Widget); | |
619 root_window_->GetRootWindowController() | |
620 ->ConfigureWidgetInitParamsForContainer( | |
621 item_widget_.get(), | |
622 transform_window_.window()->GetParent()->GetShellWindowId(), | |
623 ¶ms_label); | |
624 item_widget_->set_focus_on_creation(false); | |
625 item_widget_->Init(params_label); | |
626 WmWindow* widget_window = WmWindow::Get(item_widget_->GetNativeWindow()); | |
627 if (transform_window_.GetTopInset()) { | |
628 // For windows with headers the overview header fades in above the | |
629 // original window header. | |
630 widget_window->GetParent()->StackChildAbove(widget_window, | |
631 transform_window_.window()); | |
632 } else { | |
633 // For tabbed windows the overview header slides from behind. The stacking | |
634 // is then corrected when the animation completes. | |
635 widget_window->GetParent()->StackChildBelow(widget_window, | |
636 transform_window_.window()); | |
637 } | |
638 label_view_ = new views::Label(title); | |
639 label_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT); | |
640 label_view_->SetAutoColorReadabilityEnabled(false); | |
641 label_view_->SetEnabledColor(kLabelColor); | |
642 // Tell the label what color it will be drawn onto. It will use whether the | |
643 // background color is opaque or transparent to decide whether to use | |
644 // subpixel rendering. Does not actually set the label's background color. | |
645 label_view_->SetBackgroundColor(kLabelBackgroundColor); | |
646 | |
647 caption_container_view_ = new CaptionContainerView( | |
648 this, label_view_, close_button_, background_view_); | |
649 item_widget_->SetContentsView(caption_container_view_); | |
650 label_view_->SetVisible(false); | |
651 item_widget_->SetOpacity(0); | |
652 item_widget_->Show(); | |
653 item_widget_->GetLayer()->SetMasksToBounds(false); | |
654 } | |
655 | |
656 void WindowSelectorItem::UpdateHeaderLayout( | |
657 HeaderFadeInMode mode, | |
658 OverviewAnimationType animation_type) { | |
659 gfx::Rect transformed_window_bounds = root_window_->ConvertRectFromScreen( | |
660 transform_window_.GetTransformedBounds()); | |
661 | |
662 gfx::Rect label_rect(close_button_->GetPreferredSize()); | |
663 label_rect.set_width(transformed_window_bounds.width()); | |
664 // For tabbed windows the initial bounds of the caption are set such that it | |
665 // appears to be "growing" up from the window content area. | |
666 label_rect.set_y( | |
667 (mode != HeaderFadeInMode::ENTER || transform_window_.GetTopInset()) | |
668 ? -label_rect.height() | |
669 : 0); | |
670 if (background_view_) { | |
671 if (mode == HeaderFadeInMode::ENTER) { | |
672 background_view_->ObserveLayerAnimations(item_widget_->GetLayer()); | |
673 background_view_->set_color(kLabelBackgroundColor); | |
674 // The color will be animated only once the label widget is faded in. | |
675 } else if (mode == HeaderFadeInMode::EXIT) { | |
676 // Normally the observer is disconnected when the fade-in animations | |
677 // complete but some tests invoke animations with |NON_ZERO_DURATION| | |
678 // without waiting for completion so do it here. | |
679 background_view_->StopObservingLayerAnimations(); | |
680 // Make the header visible above the window. It will be faded out when | |
681 // the Shutdown() is called. | |
682 background_view_->AnimateColor(gfx::Tween::EASE_OUT, | |
683 kExitFadeInMilliseconds); | |
684 background_view_->set_color(kLabelExitColor); | |
685 } | |
686 } | |
687 if (!label_view_->visible()) { | |
688 label_view_->SetVisible(true); | |
689 SetupFadeInAfterLayout(item_widget_.get()); | |
690 } | |
691 WmWindow* widget_window = WmWindow::Get(item_widget_->GetNativeWindow()); | |
692 std::unique_ptr<ScopedOverviewAnimationSettings> animation_settings = | |
693 ScopedOverviewAnimationSettingsFactory::Get() | |
694 ->CreateOverviewAnimationSettings(animation_type, widget_window); | |
695 // |widget_window| covers both the transformed window and the header | |
696 // as well as the gap between the windows to prevent events from reaching | |
697 // the window including its sizing borders. | |
698 if (mode != HeaderFadeInMode::ENTER) { | |
699 label_rect.set_height(close_button_->GetPreferredSize().height() + | |
700 transformed_window_bounds.height()); | |
701 } | |
702 label_rect.Inset(-kWindowSelectorMargin, -kWindowSelectorMargin); | |
703 widget_window->SetBounds(label_rect); | |
704 gfx::Transform label_transform; | |
705 label_transform.Translate(transformed_window_bounds.x(), | |
706 transformed_window_bounds.y()); | |
707 widget_window->SetTransform(label_transform); | |
708 } | |
709 | |
710 void WindowSelectorItem::AnimateOpacity(float opacity, | |
711 OverviewAnimationType animation_type) { | |
712 DCHECK_GE(opacity, 0.f); | |
713 DCHECK_LE(opacity, 1.f); | |
714 ScopedTransformOverviewWindow::ScopedAnimationSettings animation_settings; | |
715 transform_window_.BeginScopedAnimation(animation_type, &animation_settings); | |
716 transform_window_.SetOpacity(opacity); | |
717 | |
718 const float header_opacity = selected_ ? 0.f : kHeaderOpacity * opacity; | |
719 WmWindow* widget_window = WmWindow::Get(item_widget_->GetNativeWindow()); | |
720 std::unique_ptr<ScopedOverviewAnimationSettings> animation_settings_label = | |
721 ScopedOverviewAnimationSettingsFactory::Get() | |
722 ->CreateOverviewAnimationSettings(animation_type, widget_window); | |
723 widget_window->SetOpacity(header_opacity); | |
724 } | |
725 | |
726 void WindowSelectorItem::UpdateAccessibilityName() { | |
727 caption_container_view_->listener_button()->SetAccessibleName( | |
728 GetWindow()->GetTitle()); | |
729 } | |
730 | |
731 void WindowSelectorItem::FadeOut(std::unique_ptr<views::Widget> widget) { | |
732 widget->SetOpacity(1.f); | |
733 | |
734 // Fade out the widget. This animation continues past the lifetime of |this|. | |
735 WmWindow* widget_window = WmWindow::Get(widget->GetNativeWindow()); | |
736 std::unique_ptr<ScopedOverviewAnimationSettings> animation_settings = | |
737 ScopedOverviewAnimationSettingsFactory::Get() | |
738 ->CreateOverviewAnimationSettings( | |
739 OverviewAnimationType:: | |
740 OVERVIEW_ANIMATION_EXIT_OVERVIEW_MODE_FADE_OUT, | |
741 widget_window); | |
742 // CleanupAnimationObserver will delete itself (and the widget) when the | |
743 // opacity animation is complete. | |
744 // Ownership over the observer is passed to the window_selector_->delegate() | |
745 // which has longer lifetime so that animations can continue even after the | |
746 // overview mode is shut down. | |
747 views::Widget* widget_ptr = widget.get(); | |
748 std::unique_ptr<CleanupAnimationObserver> observer( | |
749 new CleanupAnimationObserver(std::move(widget))); | |
750 animation_settings->AddObserver(observer.get()); | |
751 window_selector_->delegate()->AddDelayedAnimationObserver( | |
752 std::move(observer)); | |
753 widget_ptr->SetOpacity(0.f); | |
754 } | |
755 | |
756 gfx::SlideAnimation* WindowSelectorItem::GetBackgroundViewAnimation() { | |
757 return background_view_ ? background_view_->animation() : nullptr; | |
758 } | |
759 | |
760 WmWindow* WindowSelectorItem::GetOverviewWindowForMinimizedStateForTest() { | |
761 return transform_window_.GetOverviewWindowForMinimizedState(); | |
762 } | |
763 | |
764 } // namespace ash | |
OLD | NEW |