Added llvm into exceptions as we can't add README.chromium into 3rd party repository
[chromium-blink-merge.git] / ash / wm / overview / window_selector_item.cc
blob2559362fffdf8dc848974cea9360559c60686dba
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.
5 #include "ash/wm/overview/window_selector_item.h"
7 #include <algorithm>
8 #include <vector>
10 #include "ash/screen_util.h"
11 #include "ash/shell.h"
12 #include "ash/shell_window_ids.h"
13 #include "ash/wm/overview/overview_animation_type.h"
14 #include "ash/wm/overview/scoped_overview_animation_settings.h"
15 #include "ash/wm/overview/scoped_transform_overview_window.h"
16 #include "ash/wm/overview/window_selector_controller.h"
17 #include "ash/wm/window_state.h"
18 #include "base/auto_reset.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/time/time.h"
22 #include "grit/ash_resources.h"
23 #include "grit/ash_strings.h"
24 #include "ui/aura/window.h"
25 #include "ui/base/l10n/l10n_util.h"
26 #include "ui/base/resource/resource_bundle.h"
27 #include "ui/gfx/geometry/vector2d.h"
28 #include "ui/gfx/transform_util.h"
29 #include "ui/strings/grit/ui_strings.h"
30 #include "ui/views/border.h"
31 #include "ui/views/controls/button/image_button.h"
32 #include "ui/views/layout/box_layout.h"
33 #include "ui/views/widget/widget.h"
34 #include "ui/wm/core/window_util.h"
36 namespace ash {
38 namespace {
40 // The minimum fling velocity which will cause a window to be closed. Unit is
41 // pixels per second.
42 const float kMinimumFlingVelocity = 4000.0f;
44 // The minimum opacity used during touch scroll gestures.
45 const float kMinimumOpacity = 0.2f;
47 // In the conceptual overview table, the window margin is the space reserved
48 // around the window within the cell. This margin does not overlap so the
49 // closest distance between adjacent windows will be twice this amount.
50 static const int kWindowMargin = 30;
52 // Foreground label color.
53 static const SkColor kLabelColor = SK_ColorWHITE;
55 // Label shadow color.
56 static const SkColor kLabelShadow = 0xB0000000;
58 // Vertical padding for the label, on top of it.
59 static const int kVerticalLabelPadding = 20;
61 // Solid shadow length from the label
62 static const int kVerticalShadowOffset = 1;
64 // Amount of blur applied to the label shadow
65 static const int kShadowBlur = 10;
67 // Opacity for dimmed items.
68 static const float kDimmedItemOpacity = 0.5f;
70 // Calculates the |window| bounds after being transformed to the selector's
71 // space. The returned Rect is in virtual screen coordinates.
72 gfx::Rect GetTransformedBounds(aura::Window* window) {
73 gfx::RectF bounds(ScreenUtil::ConvertRectToScreen(window->GetRootWindow(),
74 window->layer()->GetTargetBounds()));
75 gfx::Transform new_transform = TransformAboutPivot(
76 gfx::Point(bounds.x(), bounds.y()),
77 window->layer()->GetTargetTransform());
78 new_transform.TransformRect(&bounds);
79 return ToEnclosingRect(bounds);
82 // Convenvience method to fade in a Window with predefined animation settings.
83 // Note: The fade in animation will occur after a delay where the delay is how
84 // long the lay out animations take.
85 void SetupFadeInAfterLayout(aura::Window* window) {
86 ui::Layer* layer = window->layer();
87 layer->SetOpacity(0.0f);
88 ScopedOverviewAnimationSettings animation_settings(
89 OverviewAnimationType::OVERVIEW_ANIMATION_ENTER_OVERVIEW_MODE_FADE_IN,
90 window);
91 layer->SetOpacity(1.0f);
94 // Convenience method to fade out a window using the animation settings defined
95 // by OverviewAnimationType::OVERVIEW_ANIMATION_ENTER_OVERVIEW_MODE_FADE_OUT.
96 void SetupFadeOut(aura::Window* window) {
97 ScopedOverviewAnimationSettings animation_settings(
98 OverviewAnimationType::OVERVIEW_ANIMATION_ENTER_OVERVIEW_MODE_FADE_OUT,
99 window);
100 window->layer()->SetOpacity(0.0f);
103 // Calculates the window opacity from the given scroll |distance| and the
104 // |min opacity_distance|.
105 float CalculateOpacityFromScrollDistance(int distance,
106 int min_opacity_distance) {
107 float opacity =
108 1.0f - static_cast<float>(abs(distance)) / min_opacity_distance;
109 return std::min(1.0f, std::max(kMinimumOpacity, opacity));
112 // An image button with a close window icon.
113 class OverviewCloseButton : public views::ImageButton {
114 public:
115 explicit OverviewCloseButton(views::ButtonListener* listener);
116 ~OverviewCloseButton() override;
118 private:
119 DISALLOW_COPY_AND_ASSIGN(OverviewCloseButton);
122 OverviewCloseButton::OverviewCloseButton(views::ButtonListener* listener)
123 : views::ImageButton(listener) {
124 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
125 SetImage(views::CustomButton::STATE_NORMAL,
126 rb.GetImageSkiaNamed(IDR_AURA_WINDOW_OVERVIEW_CLOSE));
127 SetImage(views::CustomButton::STATE_HOVERED,
128 rb.GetImageSkiaNamed(IDR_AURA_WINDOW_OVERVIEW_CLOSE_H));
129 SetImage(views::CustomButton::STATE_PRESSED,
130 rb.GetImageSkiaNamed(IDR_AURA_WINDOW_OVERVIEW_CLOSE_P));
133 OverviewCloseButton::~OverviewCloseButton() {
136 } // namespace
138 WindowSelectorItem::OverviewLabelButton::OverviewLabelButton(
139 WindowSelectorItem* selector_item,
140 const base::string16& text)
141 : LabelButton(selector_item, text),
142 selector_item_(selector_item),
143 top_padding_(0) {
146 WindowSelectorItem::OverviewLabelButton::~OverviewLabelButton() {
149 gfx::Rect WindowSelectorItem::OverviewLabelButton::GetChildAreaBounds() {
150 gfx::Rect bounds = GetLocalBounds();
151 bounds.Inset(0, top_padding_, 0, 0);
152 return bounds;
155 void WindowSelectorItem::OverviewLabelButton::OnGestureEvent(
156 ui::GestureEvent* event) {
157 selector_item_->OnGestureEvent(event);
158 views::LabelButton::OnGestureEvent(event);
161 WindowSelectorItem::WindowSelectorItem(aura::Window* window)
162 : dimmed_(false),
163 root_window_(window->GetRootWindow()),
164 transform_window_(window),
165 in_bounds_update_(false),
166 window_label_button_view_(nullptr),
167 close_button_(new OverviewCloseButton(this)) {
168 CreateWindowLabel(window->title());
169 views::Widget::InitParams params;
170 params.type = views::Widget::InitParams::TYPE_POPUP;
171 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
172 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
173 params.parent = Shell::GetContainer(root_window_,
174 kShellWindowId_OverlayContainer);
175 close_button_widget_.set_focus_on_creation(false);
176 close_button_widget_.Init(params);
177 close_button_->SetVisible(false);
178 close_button_widget_.SetContentsView(close_button_);
179 close_button_widget_.SetSize(close_button_->GetPreferredSize());
180 close_button_widget_.Show();
182 gfx::Rect close_button_rect(close_button_widget_.GetNativeWindow()->bounds());
183 // Align the center of the button with position (0, 0) so that the
184 // translate transform does not need to take the button dimensions into
185 // account.
186 close_button_rect.set_x(-close_button_rect.width() / 2);
187 close_button_rect.set_y(-close_button_rect.height() / 2);
188 close_button_widget_.GetNativeWindow()->SetBounds(close_button_rect);
190 GetWindow()->AddObserver(this);
193 WindowSelectorItem::~WindowSelectorItem() {
194 GetWindow()->RemoveObserver(this);
197 aura::Window* WindowSelectorItem::GetWindow() {
198 return transform_window_.window();
201 void WindowSelectorItem::RestoreWindow() {
202 transform_window_.RestoreWindow();
205 void WindowSelectorItem::ShowWindowOnExit() {
206 transform_window_.ShowWindowOnExit();
209 void WindowSelectorItem::PrepareForOverview() {
210 transform_window_.PrepareForOverview();
213 bool WindowSelectorItem::Contains(const aura::Window* target) const {
214 return transform_window_.Contains(target);
217 void WindowSelectorItem::SetBounds(const gfx::Rect& target_bounds,
218 OverviewAnimationType animation_type) {
219 if (in_bounds_update_)
220 return;
221 base::AutoReset<bool> auto_reset_in_bounds_update(&in_bounds_update_, true);
222 target_bounds_ = target_bounds;
224 gfx::Rect inset_bounds(target_bounds);
225 inset_bounds.Inset(kWindowMargin, kWindowMargin);
226 SetItemBounds(inset_bounds, animation_type);
228 // SetItemBounds is called before UpdateCloseButtonLayout so the close button
229 // can properly use the updated windows bounds.
230 UpdateCloseButtonLayout(animation_type);
231 UpdateWindowLabel(target_bounds, animation_type);
234 void WindowSelectorItem::RecomputeWindowTransforms() {
235 if (in_bounds_update_ || target_bounds_.IsEmpty())
236 return;
237 base::AutoReset<bool> auto_reset_in_bounds_update(&in_bounds_update_, true);
238 gfx::Rect inset_bounds(target_bounds_);
239 inset_bounds.Inset(kWindowMargin, kWindowMargin);
240 SetItemBounds(inset_bounds, OverviewAnimationType::OVERVIEW_ANIMATION_NONE);
241 UpdateCloseButtonLayout(OverviewAnimationType::OVERVIEW_ANIMATION_NONE);
244 void WindowSelectorItem::SendFocusAlert() const {
245 window_label_button_view_->NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true);
248 void WindowSelectorItem::SetDimmed(bool dimmed) {
249 dimmed_ = dimmed;
250 SetOpacity(dimmed ? kDimmedItemOpacity : 1.0f);
253 void WindowSelectorItem::ButtonPressed(views::Button* sender,
254 const ui::Event& event) {
255 if (sender == close_button_) {
256 transform_window_.Close();
257 return;
259 CHECK(sender == window_label_button_view_);
260 wm::GetWindowState(transform_window_.window())->Activate();
263 void WindowSelectorItem::OnGestureEvent(ui::GestureEvent* event) {
264 if (Shell::GetInstance()->window_selector_controller()->
265 swipe_to_close_disabled())
266 return;
268 int delta_x = 0;
269 if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN)
270 scroll_x_origin_ = event->x();
271 else
272 delta_x = event->x() - scroll_x_origin_;
274 switch (event->type()) {
275 case ui::ET_GESTURE_SCROLL_BEGIN: {
276 // We need to call SetHandled() for the ET_GESTURE_SCROLL_BEGIN event so
277 // that future ET_GESTURE_SCROLL_* events are sent here.
278 event->SetHandled();
279 close_button_->SetEnabled(false);
280 SetupFadeOut(close_button_widget_.GetNativeWindow());
281 break;
283 case ui::ET_GESTURE_SCROLL_UPDATE: {
284 event->SetHandled();
285 ScopedTransformOverviewWindow::ScopedAnimationSettings
286 animation_settings;
287 transform_window_.BeginScopedAnimation(
288 OverviewAnimationType::OVERVIEW_ANIMATION_SCROLL_SELECTOR_ITEM,
289 &animation_settings);
291 gfx::Transform new_transform;
292 new_transform.Translate(delta_x, 0);
293 new_transform.PreconcatTransform(
294 transform_window_.get_overview_transform());
295 transform_window_.SetTransform(root_window(), new_transform);
297 const float opacity = CalculateOpacityFromScrollDistance(delta_x,
298 GetMinimumCloseDistance());
299 transform_window_.SetOpacity(opacity);
300 break;
302 case ui::ET_GESTURE_SCROLL_END: {
303 event->SetHandled();
304 if (abs(delta_x) > GetMinimumCloseDistance()) {
305 transform_window_.Close();
306 break;
308 ResetScrolledWindow();
309 break;
311 case ui::ET_SCROLL_FLING_START: {
312 event->SetHandled();
313 if (abs(delta_x) > GetMinimumCloseDistance() ||
314 fabs(event->details().velocity_x()) > kMinimumFlingVelocity) {
315 transform_window_.Close();
316 break;
318 ResetScrolledWindow();
319 break;
321 case ui::ET_GESTURE_END:
322 scroll_x_origin_ = 0;
323 break;
324 default:
325 break;
329 void WindowSelectorItem::OnWindowDestroying(aura::Window* window) {
330 window->RemoveObserver(this);
331 transform_window_.OnWindowDestroyed();
334 void WindowSelectorItem::OnWindowTitleChanged(aura::Window* window) {
335 // TODO(flackr): Maybe add the new title to a vector of titles so that we can
336 // filter any of the titles the window had while in the overview session.
337 window_label_button_view_->SetText(window->title());
338 UpdateCloseButtonAccessibilityName();
341 void WindowSelectorItem::ResetScrolledWindow() {
342 ScopedTransformOverviewWindow::ScopedAnimationSettings animation_settings;
343 transform_window_.BeginScopedAnimation(
344 OverviewAnimationType::OVERVIEW_ANIMATION_CANCEL_SELECTOR_ITEM_SCROLL,
345 &animation_settings);
347 transform_window_.SetTransform(root_window(),
348 transform_window_.get_overview_transform());
349 transform_window_.SetOpacity(1.0);
351 SetupFadeInAfterLayout(close_button_widget_.GetNativeWindow());
352 close_button_->SetEnabled(true);
355 void WindowSelectorItem::SetItemBounds(const gfx::Rect& target_bounds,
356 OverviewAnimationType animation_type) {
357 DCHECK(root_window_ == GetWindow()->GetRootWindow());
358 gfx::Rect screen_bounds = transform_window_.GetTargetBoundsInScreen();
359 gfx::Rect selector_item_bounds =
360 ScopedTransformOverviewWindow::ShrinkRectToFitPreservingAspectRatio(
361 screen_bounds, target_bounds);
362 gfx::Transform transform =
363 ScopedTransformOverviewWindow::GetTransformForRect(screen_bounds,
364 selector_item_bounds);
365 ScopedTransformOverviewWindow::ScopedAnimationSettings animation_settings;
366 transform_window_.BeginScopedAnimation(animation_type, &animation_settings);
367 transform_window_.SetTransform(root_window_, transform);
368 transform_window_.set_overview_transform(transform);
371 void WindowSelectorItem::SetOpacity(float opacity) {
372 window_label_->GetNativeWindow()->layer()->SetOpacity(opacity);
373 close_button_widget_.GetNativeWindow()->layer()->SetOpacity(opacity);
375 transform_window_.SetOpacity(opacity);
378 void WindowSelectorItem::UpdateWindowLabel(
379 const gfx::Rect& window_bounds,
380 OverviewAnimationType animation_type) {
381 // If the root window has changed, force the window label to be recreated
382 // and faded in on the new root window.
383 DCHECK(!window_label_ ||
384 window_label_->GetNativeWindow()->GetRootWindow() == root_window_);
386 if (!window_label_->IsVisible()) {
387 window_label_->Show();
388 SetupFadeInAfterLayout(window_label_->GetNativeWindow());
391 gfx::Rect converted_bounds =
392 ScreenUtil::ConvertRectFromScreen(root_window_, window_bounds);
393 gfx::Rect label_bounds(converted_bounds.x(), converted_bounds.y(),
394 converted_bounds.width(), converted_bounds.height());
395 window_label_button_view_->set_top_padding(label_bounds.height() -
396 kVerticalLabelPadding);
397 ScopedOverviewAnimationSettings animation_settings(
398 animation_type, window_label_->GetNativeWindow());
400 window_label_->GetNativeWindow()->SetBounds(label_bounds);
403 void WindowSelectorItem::CreateWindowLabel(const base::string16& title) {
404 window_label_.reset(new views::Widget);
405 views::Widget::InitParams params;
406 params.type = views::Widget::InitParams::TYPE_POPUP;
407 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
408 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
409 params.parent =
410 Shell::GetContainer(root_window_, kShellWindowId_OverlayContainer);
411 params.visible_on_all_workspaces = true;
412 window_label_->set_focus_on_creation(false);
413 window_label_->Init(params);
414 window_label_button_view_ = new OverviewLabelButton(this, title);
415 window_label_button_view_->SetBorder(views::Border::NullBorder());
416 window_label_button_view_->SetTextColor(views::LabelButton::STATE_NORMAL,
417 kLabelColor);
418 window_label_button_view_->SetTextColor(views::LabelButton::STATE_HOVERED,
419 kLabelColor);
420 window_label_button_view_->SetTextColor(views::LabelButton::STATE_PRESSED,
421 kLabelColor);
422 window_label_button_view_->set_animate_on_state_change(false);
423 window_label_button_view_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
424 window_label_button_view_->SetTextShadows(gfx::ShadowValues(
425 1, gfx::ShadowValue(gfx::Point(0, kVerticalShadowOffset), kShadowBlur,
426 kLabelShadow)));
427 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
428 window_label_button_view_->SetFontList(
429 bundle.GetFontList(ui::ResourceBundle::BoldFont));
430 window_label_->SetContentsView(window_label_button_view_);
433 void WindowSelectorItem::UpdateCloseButtonLayout(
434 OverviewAnimationType animation_type) {
435 if (!close_button_->visible()) {
436 close_button_->SetVisible(true);
437 SetupFadeInAfterLayout(close_button_widget_.GetNativeWindow());
439 ScopedOverviewAnimationSettings animation_settings(animation_type,
440 close_button_widget_.GetNativeWindow());
442 gfx::Rect transformed_window_bounds = ScreenUtil::ConvertRectFromScreen(
443 close_button_widget_.GetNativeWindow()->GetRootWindow(),
444 GetTransformedBounds(GetWindow()));
446 gfx::Transform close_button_transform;
447 close_button_transform.Translate(transformed_window_bounds.right(),
448 transformed_window_bounds.y());
449 close_button_widget_.GetNativeWindow()->SetTransform(
450 close_button_transform);
453 void WindowSelectorItem::UpdateCloseButtonAccessibilityName() {
454 close_button_->SetAccessibleName(l10n_util::GetStringFUTF16(
455 IDS_ASH_OVERVIEW_CLOSE_ITEM_BUTTON_ACCESSIBLE_NAME,
456 GetWindow()->title()));
459 int WindowSelectorItem::GetMinimumCloseDistance() const {
460 return target_bounds_.size().width() / 2;
463 } // namespace ash