Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / ash / wm / overview / window_selector_item.cc
blob02a9bb2ae8fddb14a718996ed5c5fad24089d0ce
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.h"
17 #include "ash/wm/overview/window_selector_controller.h"
18 #include "ash/wm/window_state.h"
19 #include "base/auto_reset.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/time/time.h"
23 #include "grit/ash_resources.h"
24 #include "grit/ash_strings.h"
25 #include "ui/aura/window.h"
26 #include "ui/base/l10n/l10n_util.h"
27 #include "ui/base/resource/resource_bundle.h"
28 #include "ui/gfx/geometry/vector2d.h"
29 #include "ui/gfx/transform_util.h"
30 #include "ui/strings/grit/ui_strings.h"
31 #include "ui/views/border.h"
32 #include "ui/views/controls/button/image_button.h"
33 #include "ui/views/layout/box_layout.h"
34 #include "ui/views/widget/widget.h"
35 #include "ui/wm/core/window_util.h"
37 namespace ash {
39 namespace {
41 // In the conceptual overview table, the window margin is the space reserved
42 // around the window within the cell. This margin does not overlap so the
43 // closest distance between adjacent windows will be twice this amount.
44 static const int kWindowMargin = 30;
46 // Foreground label color.
47 static const SkColor kLabelColor = SK_ColorWHITE;
49 // Label shadow color.
50 static const SkColor kLabelShadow = 0xB0000000;
52 // Vertical padding for the label, on top of it.
53 static const int kVerticalLabelPadding = 20;
55 // Solid shadow length from the label
56 static const int kVerticalShadowOffset = 1;
58 // Amount of blur applied to the label shadow
59 static const int kShadowBlur = 10;
61 // Opacity for dimmed items.
62 static const float kDimmedItemOpacity = 0.5f;
64 // Calculates the |window| bounds after being transformed to the selector's
65 // space. The returned Rect is in virtual screen coordinates.
66 gfx::Rect GetTransformedBounds(aura::Window* window) {
67 gfx::RectF bounds(ScreenUtil::ConvertRectToScreen(window->GetRootWindow(),
68 window->layer()->GetTargetBounds()));
69 gfx::Transform new_transform = TransformAboutPivot(
70 gfx::Point(bounds.x(), bounds.y()),
71 window->layer()->GetTargetTransform());
72 new_transform.TransformRect(&bounds);
73 return ToEnclosingRect(bounds);
76 // Convenvience method to fade in a Window with predefined animation settings.
77 // Note: The fade in animation will occur after a delay where the delay is how
78 // long the lay out animations take.
79 void SetupFadeInAfterLayout(aura::Window* window) {
80 ui::Layer* layer = window->layer();
81 layer->SetOpacity(0.0f);
82 ScopedOverviewAnimationSettings animation_settings(
83 OverviewAnimationType::OVERVIEW_ANIMATION_ENTER_OVERVIEW_MODE_FADE_IN,
84 window);
85 layer->SetOpacity(1.0f);
88 // An image button with a close window icon.
89 class OverviewCloseButton : public views::ImageButton {
90 public:
91 explicit OverviewCloseButton(views::ButtonListener* listener);
92 ~OverviewCloseButton() override;
94 private:
95 DISALLOW_COPY_AND_ASSIGN(OverviewCloseButton);
98 OverviewCloseButton::OverviewCloseButton(views::ButtonListener* listener)
99 : views::ImageButton(listener) {
100 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
101 SetImage(views::CustomButton::STATE_NORMAL,
102 rb.GetImageSkiaNamed(IDR_AURA_WINDOW_OVERVIEW_CLOSE));
103 SetImage(views::CustomButton::STATE_HOVERED,
104 rb.GetImageSkiaNamed(IDR_AURA_WINDOW_OVERVIEW_CLOSE_H));
105 SetImage(views::CustomButton::STATE_PRESSED,
106 rb.GetImageSkiaNamed(IDR_AURA_WINDOW_OVERVIEW_CLOSE_P));
109 OverviewCloseButton::~OverviewCloseButton() {
112 } // namespace
114 WindowSelectorItem::OverviewLabelButton::OverviewLabelButton(
115 views::ButtonListener* listener,
116 const base::string16& text)
117 : LabelButton(listener, text),
118 top_padding_(0) {
121 WindowSelectorItem::OverviewLabelButton::~OverviewLabelButton() {
124 gfx::Rect WindowSelectorItem::OverviewLabelButton::GetChildAreaBounds() {
125 gfx::Rect bounds = GetLocalBounds();
126 bounds.Inset(0, top_padding_, 0, 0);
127 return bounds;
130 WindowSelectorItem::WindowSelectorItem(aura::Window* window,
131 WindowSelector* window_selector)
132 : dimmed_(false),
133 root_window_(window->GetRootWindow()),
134 transform_window_(window),
135 in_bounds_update_(false),
136 window_label_button_view_(nullptr),
137 close_button_(new OverviewCloseButton(this)),
138 window_selector_(window_selector) {
139 CreateWindowLabel(window->title());
140 views::Widget::InitParams params;
141 params.type = views::Widget::InitParams::TYPE_POPUP;
142 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
143 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
144 params.parent = Shell::GetContainer(root_window_,
145 kShellWindowId_OverlayContainer);
146 close_button_widget_.set_focus_on_creation(false);
147 close_button_widget_.Init(params);
148 close_button_->SetVisible(false);
149 close_button_widget_.SetContentsView(close_button_);
150 close_button_widget_.SetSize(close_button_->GetPreferredSize());
151 close_button_widget_.Show();
153 gfx::Rect close_button_rect(close_button_widget_.GetNativeWindow()->bounds());
154 // Align the center of the button with position (0, 0) so that the
155 // translate transform does not need to take the button dimensions into
156 // account.
157 close_button_rect.set_x(-close_button_rect.width() / 2);
158 close_button_rect.set_y(-close_button_rect.height() / 2);
159 close_button_widget_.GetNativeWindow()->SetBounds(close_button_rect);
161 GetWindow()->AddObserver(this);
164 WindowSelectorItem::~WindowSelectorItem() {
165 GetWindow()->RemoveObserver(this);
168 aura::Window* WindowSelectorItem::GetWindow() {
169 return transform_window_.window();
172 void WindowSelectorItem::RestoreWindow() {
173 transform_window_.RestoreWindow();
176 void WindowSelectorItem::ShowWindowOnExit() {
177 transform_window_.ShowWindowOnExit();
180 void WindowSelectorItem::PrepareForOverview() {
181 transform_window_.PrepareForOverview();
184 bool WindowSelectorItem::Contains(const aura::Window* target) const {
185 return transform_window_.Contains(target);
188 void WindowSelectorItem::SetBounds(const gfx::Rect& target_bounds,
189 OverviewAnimationType animation_type) {
190 if (in_bounds_update_)
191 return;
192 base::AutoReset<bool> auto_reset_in_bounds_update(&in_bounds_update_, true);
193 target_bounds_ = target_bounds;
195 gfx::Rect inset_bounds(target_bounds);
196 inset_bounds.Inset(kWindowMargin, kWindowMargin);
197 SetItemBounds(inset_bounds, animation_type);
199 // SetItemBounds is called before UpdateCloseButtonLayout so the close button
200 // can properly use the updated windows bounds.
201 UpdateCloseButtonLayout(animation_type);
202 UpdateWindowLabel(target_bounds, animation_type);
205 void WindowSelectorItem::RecomputeWindowTransforms() {
206 if (in_bounds_update_ || target_bounds_.IsEmpty())
207 return;
208 base::AutoReset<bool> auto_reset_in_bounds_update(&in_bounds_update_, true);
209 gfx::Rect inset_bounds(target_bounds_);
210 inset_bounds.Inset(kWindowMargin, kWindowMargin);
211 SetItemBounds(inset_bounds, OverviewAnimationType::OVERVIEW_ANIMATION_NONE);
212 UpdateCloseButtonLayout(OverviewAnimationType::OVERVIEW_ANIMATION_NONE);
215 void WindowSelectorItem::SendFocusAlert() const {
216 window_label_button_view_->NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true);
219 void WindowSelectorItem::SetDimmed(bool dimmed) {
220 dimmed_ = dimmed;
221 SetOpacity(dimmed ? kDimmedItemOpacity : 1.0f);
224 void WindowSelectorItem::ButtonPressed(views::Button* sender,
225 const ui::Event& event) {
226 if (sender == close_button_) {
227 transform_window_.Close();
228 return;
230 CHECK(sender == window_label_button_view_);
231 window_selector_->SelectWindow(transform_window_.window());
234 void WindowSelectorItem::OnWindowDestroying(aura::Window* window) {
235 window->RemoveObserver(this);
236 transform_window_.OnWindowDestroyed();
239 void WindowSelectorItem::OnWindowTitleChanged(aura::Window* window) {
240 // TODO(flackr): Maybe add the new title to a vector of titles so that we can
241 // filter any of the titles the window had while in the overview session.
242 window_label_button_view_->SetText(window->title());
243 UpdateCloseButtonAccessibilityName();
246 void WindowSelectorItem::SetItemBounds(const gfx::Rect& target_bounds,
247 OverviewAnimationType animation_type) {
248 DCHECK(root_window_ == GetWindow()->GetRootWindow());
249 gfx::Rect screen_bounds = transform_window_.GetTargetBoundsInScreen();
250 gfx::Rect selector_item_bounds =
251 ScopedTransformOverviewWindow::ShrinkRectToFitPreservingAspectRatio(
252 screen_bounds, target_bounds);
253 gfx::Transform transform =
254 ScopedTransformOverviewWindow::GetTransformForRect(screen_bounds,
255 selector_item_bounds);
256 ScopedTransformOverviewWindow::ScopedAnimationSettings animation_settings;
257 transform_window_.BeginScopedAnimation(animation_type, &animation_settings);
258 transform_window_.SetTransform(root_window_, transform);
259 transform_window_.set_overview_transform(transform);
262 void WindowSelectorItem::SetOpacity(float opacity) {
263 window_label_->GetNativeWindow()->layer()->SetOpacity(opacity);
264 close_button_widget_.GetNativeWindow()->layer()->SetOpacity(opacity);
266 transform_window_.SetOpacity(opacity);
269 void WindowSelectorItem::UpdateWindowLabel(
270 const gfx::Rect& window_bounds,
271 OverviewAnimationType animation_type) {
272 // If the root window has changed, force the window label to be recreated
273 // and faded in on the new root window.
274 DCHECK(!window_label_ ||
275 window_label_->GetNativeWindow()->GetRootWindow() == root_window_);
277 if (!window_label_->IsVisible()) {
278 window_label_->Show();
279 SetupFadeInAfterLayout(window_label_->GetNativeWindow());
282 gfx::Rect converted_bounds =
283 ScreenUtil::ConvertRectFromScreen(root_window_, window_bounds);
284 gfx::Rect label_bounds(converted_bounds.x(), converted_bounds.y(),
285 converted_bounds.width(), converted_bounds.height());
286 window_label_button_view_->set_top_padding(label_bounds.height() -
287 kVerticalLabelPadding);
288 ScopedOverviewAnimationSettings animation_settings(
289 animation_type, window_label_->GetNativeWindow());
291 window_label_->GetNativeWindow()->SetBounds(label_bounds);
294 void WindowSelectorItem::CreateWindowLabel(const base::string16& title) {
295 window_label_.reset(new views::Widget);
296 views::Widget::InitParams params;
297 params.type = views::Widget::InitParams::TYPE_POPUP;
298 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
299 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
300 params.parent =
301 Shell::GetContainer(root_window_, kShellWindowId_OverlayContainer);
302 params.visible_on_all_workspaces = true;
303 window_label_->set_focus_on_creation(false);
304 window_label_->Init(params);
305 window_label_button_view_ = new OverviewLabelButton(this, title);
306 window_label_button_view_->SetBorder(views::Border::NullBorder());
307 window_label_button_view_->SetTextColor(views::LabelButton::STATE_NORMAL,
308 kLabelColor);
309 window_label_button_view_->SetTextColor(views::LabelButton::STATE_HOVERED,
310 kLabelColor);
311 window_label_button_view_->SetTextColor(views::LabelButton::STATE_PRESSED,
312 kLabelColor);
313 window_label_button_view_->set_animate_on_state_change(false);
314 window_label_button_view_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
315 window_label_button_view_->SetTextShadows(gfx::ShadowValues(
316 1, gfx::ShadowValue(gfx::Vector2d(0, kVerticalShadowOffset), kShadowBlur,
317 kLabelShadow)));
318 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
319 window_label_button_view_->SetFontList(
320 bundle.GetFontList(ui::ResourceBundle::BoldFont));
321 window_label_->SetContentsView(window_label_button_view_);
324 void WindowSelectorItem::UpdateCloseButtonLayout(
325 OverviewAnimationType animation_type) {
326 if (!close_button_->visible()) {
327 close_button_->SetVisible(true);
328 SetupFadeInAfterLayout(close_button_widget_.GetNativeWindow());
330 ScopedOverviewAnimationSettings animation_settings(animation_type,
331 close_button_widget_.GetNativeWindow());
333 gfx::Rect transformed_window_bounds = ScreenUtil::ConvertRectFromScreen(
334 close_button_widget_.GetNativeWindow()->GetRootWindow(),
335 GetTransformedBounds(GetWindow()));
337 gfx::Transform close_button_transform;
338 close_button_transform.Translate(transformed_window_bounds.right(),
339 transformed_window_bounds.y());
340 close_button_widget_.GetNativeWindow()->SetTransform(
341 close_button_transform);
344 void WindowSelectorItem::UpdateCloseButtonAccessibilityName() {
345 close_button_->SetAccessibleName(l10n_util::GetStringFUTF16(
346 IDS_ASH_OVERVIEW_CLOSE_ITEM_BUTTON_ACCESSIBLE_NAME,
347 GetWindow()->title()));
350 } // namespace ash