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"
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"
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
,
85 layer
->SetOpacity(1.0f
);
88 // An image button with a close window icon.
89 class OverviewCloseButton
: public views::ImageButton
{
91 explicit OverviewCloseButton(views::ButtonListener
* listener
);
92 ~OverviewCloseButton() override
;
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() {
114 WindowSelectorItem::OverviewLabelButton::OverviewLabelButton(
115 views::ButtonListener
* listener
,
116 const base::string16
& text
)
117 : LabelButton(listener
, text
),
121 WindowSelectorItem::OverviewLabelButton::~OverviewLabelButton() {
124 gfx::Rect
WindowSelectorItem::OverviewLabelButton::GetChildAreaBounds() {
125 gfx::Rect bounds
= GetLocalBounds();
126 bounds
.Inset(0, top_padding_
, 0, 0);
130 WindowSelectorItem::WindowSelectorItem(aura::Window
* window
,
131 WindowSelector
* window_selector
)
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
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_
)
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())
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
) {
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();
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
;
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
,
309 window_label_button_view_
->SetTextColor(views::LabelButton::STATE_HOVERED
,
311 window_label_button_view_
->SetTextColor(views::LabelButton::STATE_PRESSED
,
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
,
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()));