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_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"
40 // In the conceptual overview table, the window margin is the space reserved
41 // around the window within the cell. This margin does not overlap so the
42 // closest distance between adjacent windows will be twice this amount.
43 static const int kWindowMargin
= 30;
45 // Foreground label color.
46 static const SkColor kLabelColor
= SK_ColorWHITE
;
48 // Label shadow color.
49 static const SkColor kLabelShadow
= 0xB0000000;
51 // Vertical padding for the label, on top of it.
52 static const int kVerticalLabelPadding
= 20;
54 // Solid shadow length from the label
55 static const int kVerticalShadowOffset
= 1;
57 // Amount of blur applied to the label shadow
58 static const int kShadowBlur
= 10;
60 // Opacity for dimmed items.
61 static const float kDimmedItemOpacity
= 0.5f
;
63 // Calculates the |window| bounds after being transformed to the selector's
64 // space. The returned Rect is in virtual screen coordinates.
65 gfx::Rect
GetTransformedBounds(aura::Window
* window
) {
66 gfx::RectF
bounds(ScreenUtil::ConvertRectToScreen(window
->GetRootWindow(),
67 window
->layer()->GetTargetBounds()));
68 gfx::Transform new_transform
= TransformAboutPivot(
69 gfx::Point(bounds
.x(), bounds
.y()),
70 window
->layer()->GetTargetTransform());
71 new_transform
.TransformRect(&bounds
);
72 return ToEnclosingRect(bounds
);
75 // Convenvience method to fade in a Window with predefined animation settings.
76 // Note: The fade in animation will occur after a delay where the delay is how
77 // long the lay out animations take.
78 void SetupFadeInAfterLayout(aura::Window
* window
) {
79 ui::Layer
* layer
= window
->layer();
80 layer
->SetOpacity(0.0f
);
81 ScopedOverviewAnimationSettings
animation_settings(
82 OverviewAnimationType::OVERVIEW_ANIMATION_ENTER_OVERVIEW_MODE_FADE_IN
,
84 layer
->SetOpacity(1.0f
);
87 // An image button with a close window icon.
88 class OverviewCloseButton
: public views::ImageButton
{
90 explicit OverviewCloseButton(views::ButtonListener
* listener
);
91 ~OverviewCloseButton() override
;
94 DISALLOW_COPY_AND_ASSIGN(OverviewCloseButton
);
97 OverviewCloseButton::OverviewCloseButton(views::ButtonListener
* listener
)
98 : views::ImageButton(listener
) {
99 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
100 SetImage(views::CustomButton::STATE_NORMAL
,
101 rb
.GetImageSkiaNamed(IDR_AURA_WINDOW_OVERVIEW_CLOSE
));
102 SetImage(views::CustomButton::STATE_HOVERED
,
103 rb
.GetImageSkiaNamed(IDR_AURA_WINDOW_OVERVIEW_CLOSE_H
));
104 SetImage(views::CustomButton::STATE_PRESSED
,
105 rb
.GetImageSkiaNamed(IDR_AURA_WINDOW_OVERVIEW_CLOSE_P
));
108 OverviewCloseButton::~OverviewCloseButton() {
113 WindowSelectorItem::OverviewLabelButton::OverviewLabelButton(
114 views::ButtonListener
* listener
,
115 const base::string16
& text
)
116 : LabelButton(listener
, text
),
120 WindowSelectorItem::OverviewLabelButton::~OverviewLabelButton() {
123 gfx::Rect
WindowSelectorItem::OverviewLabelButton::GetChildAreaBounds() {
124 gfx::Rect bounds
= GetLocalBounds();
125 bounds
.Inset(0, top_padding_
, 0, 0);
129 WindowSelectorItem::WindowSelectorItem(aura::Window
* window
)
131 root_window_(window
->GetRootWindow()),
132 transform_window_(window
),
133 in_bounds_update_(false),
134 window_label_button_view_(nullptr),
135 close_button_(new OverviewCloseButton(this)) {
136 CreateWindowLabel(window
->title());
137 views::Widget::InitParams params
;
138 params
.type
= views::Widget::InitParams::TYPE_POPUP
;
139 params
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
140 params
.opacity
= views::Widget::InitParams::TRANSLUCENT_WINDOW
;
141 params
.parent
= Shell::GetContainer(root_window_
,
142 kShellWindowId_OverlayContainer
);
143 close_button_widget_
.set_focus_on_creation(false);
144 close_button_widget_
.Init(params
);
145 close_button_
->SetVisible(false);
146 close_button_widget_
.SetContentsView(close_button_
);
147 close_button_widget_
.SetSize(close_button_
->GetPreferredSize());
148 close_button_widget_
.Show();
150 gfx::Rect
close_button_rect(close_button_widget_
.GetNativeWindow()->bounds());
151 // Align the center of the button with position (0, 0) so that the
152 // translate transform does not need to take the button dimensions into
154 close_button_rect
.set_x(-close_button_rect
.width() / 2);
155 close_button_rect
.set_y(-close_button_rect
.height() / 2);
156 close_button_widget_
.GetNativeWindow()->SetBounds(close_button_rect
);
158 GetWindow()->AddObserver(this);
161 WindowSelectorItem::~WindowSelectorItem() {
162 GetWindow()->RemoveObserver(this);
165 aura::Window
* WindowSelectorItem::GetWindow() {
166 return transform_window_
.window();
169 void WindowSelectorItem::RestoreWindow() {
170 transform_window_
.RestoreWindow();
173 void WindowSelectorItem::ShowWindowOnExit() {
174 transform_window_
.ShowWindowOnExit();
177 void WindowSelectorItem::PrepareForOverview() {
178 transform_window_
.PrepareForOverview();
181 bool WindowSelectorItem::Contains(const aura::Window
* target
) const {
182 return transform_window_
.Contains(target
);
185 void WindowSelectorItem::SetBounds(const gfx::Rect
& target_bounds
,
186 OverviewAnimationType animation_type
) {
187 if (in_bounds_update_
)
189 base::AutoReset
<bool> auto_reset_in_bounds_update(&in_bounds_update_
, true);
190 target_bounds_
= target_bounds
;
192 gfx::Rect
inset_bounds(target_bounds
);
193 inset_bounds
.Inset(kWindowMargin
, kWindowMargin
);
194 SetItemBounds(inset_bounds
, animation_type
);
196 // SetItemBounds is called before UpdateCloseButtonLayout so the close button
197 // can properly use the updated windows bounds.
198 UpdateCloseButtonLayout(animation_type
);
199 UpdateWindowLabel(target_bounds
, animation_type
);
202 void WindowSelectorItem::RecomputeWindowTransforms() {
203 if (in_bounds_update_
|| target_bounds_
.IsEmpty())
205 base::AutoReset
<bool> auto_reset_in_bounds_update(&in_bounds_update_
, true);
206 gfx::Rect
inset_bounds(target_bounds_
);
207 inset_bounds
.Inset(kWindowMargin
, kWindowMargin
);
208 SetItemBounds(inset_bounds
, OverviewAnimationType::OVERVIEW_ANIMATION_NONE
);
209 UpdateCloseButtonLayout(OverviewAnimationType::OVERVIEW_ANIMATION_NONE
);
212 void WindowSelectorItem::SendFocusAlert() const {
213 window_label_button_view_
->NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS
, true);
216 void WindowSelectorItem::SetDimmed(bool dimmed
) {
218 SetOpacity(dimmed
? kDimmedItemOpacity
: 1.0f
);
221 void WindowSelectorItem::ButtonPressed(views::Button
* sender
,
222 const ui::Event
& event
) {
223 if (sender
== close_button_
) {
224 transform_window_
.Close();
227 CHECK(sender
== window_label_button_view_
);
228 wm::GetWindowState(transform_window_
.window())->Activate();
231 void WindowSelectorItem::OnWindowDestroying(aura::Window
* window
) {
232 window
->RemoveObserver(this);
233 transform_window_
.OnWindowDestroyed();
236 void WindowSelectorItem::OnWindowTitleChanged(aura::Window
* window
) {
237 // TODO(flackr): Maybe add the new title to a vector of titles so that we can
238 // filter any of the titles the window had while in the overview session.
239 window_label_button_view_
->SetText(window
->title());
240 UpdateCloseButtonAccessibilityName();
243 void WindowSelectorItem::SetItemBounds(const gfx::Rect
& target_bounds
,
244 OverviewAnimationType animation_type
) {
245 DCHECK(root_window_
== GetWindow()->GetRootWindow());
246 gfx::Rect screen_bounds
= transform_window_
.GetTargetBoundsInScreen();
247 gfx::Rect selector_item_bounds
=
248 ScopedTransformOverviewWindow::ShrinkRectToFitPreservingAspectRatio(
249 screen_bounds
, target_bounds
);
250 gfx::Transform transform
=
251 ScopedTransformOverviewWindow::GetTransformForRect(screen_bounds
,
252 selector_item_bounds
);
253 ScopedTransformOverviewWindow::ScopedAnimationSettings animation_settings
;
254 transform_window_
.BeginScopedAnimation(animation_type
, &animation_settings
);
255 transform_window_
.SetTransform(root_window_
, transform
);
256 transform_window_
.set_overview_transform(transform
);
259 void WindowSelectorItem::SetOpacity(float opacity
) {
260 window_label_
->GetNativeWindow()->layer()->SetOpacity(opacity
);
261 close_button_widget_
.GetNativeWindow()->layer()->SetOpacity(opacity
);
263 transform_window_
.SetOpacity(opacity
);
266 void WindowSelectorItem::UpdateWindowLabel(
267 const gfx::Rect
& window_bounds
,
268 OverviewAnimationType animation_type
) {
269 // If the root window has changed, force the window label to be recreated
270 // and faded in on the new root window.
271 DCHECK(!window_label_
||
272 window_label_
->GetNativeWindow()->GetRootWindow() == root_window_
);
274 if (!window_label_
->IsVisible()) {
275 window_label_
->Show();
276 SetupFadeInAfterLayout(window_label_
->GetNativeWindow());
279 gfx::Rect converted_bounds
=
280 ScreenUtil::ConvertRectFromScreen(root_window_
, window_bounds
);
281 gfx::Rect
label_bounds(converted_bounds
.x(), converted_bounds
.y(),
282 converted_bounds
.width(), converted_bounds
.height());
283 window_label_button_view_
->set_top_padding(label_bounds
.height() -
284 kVerticalLabelPadding
);
285 ScopedOverviewAnimationSettings
animation_settings(
286 animation_type
, window_label_
->GetNativeWindow());
288 window_label_
->GetNativeWindow()->SetBounds(label_bounds
);
291 void WindowSelectorItem::CreateWindowLabel(const base::string16
& title
) {
292 window_label_
.reset(new views::Widget
);
293 views::Widget::InitParams params
;
294 params
.type
= views::Widget::InitParams::TYPE_POPUP
;
295 params
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
296 params
.opacity
= views::Widget::InitParams::TRANSLUCENT_WINDOW
;
298 Shell::GetContainer(root_window_
, kShellWindowId_OverlayContainer
);
299 params
.visible_on_all_workspaces
= true;
300 window_label_
->set_focus_on_creation(false);
301 window_label_
->Init(params
);
302 window_label_button_view_
= new OverviewLabelButton(this, title
);
303 window_label_button_view_
->SetBorder(views::Border::NullBorder());
304 window_label_button_view_
->SetTextColor(views::LabelButton::STATE_NORMAL
,
306 window_label_button_view_
->SetTextColor(views::LabelButton::STATE_HOVERED
,
308 window_label_button_view_
->SetTextColor(views::LabelButton::STATE_PRESSED
,
310 window_label_button_view_
->set_animate_on_state_change(false);
311 window_label_button_view_
->SetHorizontalAlignment(gfx::ALIGN_CENTER
);
312 window_label_button_view_
->SetTextShadows(gfx::ShadowValues(
313 1, gfx::ShadowValue(gfx::Vector2d(0, kVerticalShadowOffset
), kShadowBlur
,
315 ui::ResourceBundle
& bundle
= ui::ResourceBundle::GetSharedInstance();
316 window_label_button_view_
->SetFontList(
317 bundle
.GetFontList(ui::ResourceBundle::BoldFont
));
318 window_label_
->SetContentsView(window_label_button_view_
);
321 void WindowSelectorItem::UpdateCloseButtonLayout(
322 OverviewAnimationType animation_type
) {
323 if (!close_button_
->visible()) {
324 close_button_
->SetVisible(true);
325 SetupFadeInAfterLayout(close_button_widget_
.GetNativeWindow());
327 ScopedOverviewAnimationSettings
animation_settings(animation_type
,
328 close_button_widget_
.GetNativeWindow());
330 gfx::Rect transformed_window_bounds
= ScreenUtil::ConvertRectFromScreen(
331 close_button_widget_
.GetNativeWindow()->GetRootWindow(),
332 GetTransformedBounds(GetWindow()));
334 gfx::Transform close_button_transform
;
335 close_button_transform
.Translate(transformed_window_bounds
.right(),
336 transformed_window_bounds
.y());
337 close_button_widget_
.GetNativeWindow()->SetTransform(
338 close_button_transform
);
341 void WindowSelectorItem::UpdateCloseButtonAccessibilityName() {
342 close_button_
->SetAccessibleName(l10n_util::GetStringFUTF16(
343 IDS_ASH_OVERVIEW_CLOSE_ITEM_BUTTON_ACCESSIBLE_NAME
,
344 GetWindow()->title()));