Put WeakPtrFactory member last in USBEventRouter
[chromium-blink-merge.git] / ash / wm / overview / window_selector_item.cc
blob7350ebdeed59394c0325f3d91ffa0909bd9110ed
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/controls/button/image_button.h"
31 #include "ui/views/controls/label.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 // 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 // Background label color.
49 static const SkColor kLabelBackground = SK_ColorTRANSPARENT;
51 // Label shadow color.
52 static const SkColor kLabelShadow = 0xB0000000;
54 // Vertical padding for the label, both over and beneath it.
55 static const int kVerticalLabelPadding = 20;
57 // Solid shadow length from the label
58 static const int kVerticalShadowOffset = 1;
60 // Amount of blur applied to the label shadow
61 static const int kShadowBlur = 10;
63 // Opacity for dimmed items.
64 static const float kDimmedItemOpacity = 0.5f;
66 // Calculates the |window| bounds after being transformed to the selector's
67 // space. The returned Rect is in virtual screen coordinates.
68 gfx::Rect GetTransformedBounds(aura::Window* window) {
69 gfx::RectF bounds(ScreenUtil::ConvertRectToScreen(window->GetRootWindow(),
70 window->layer()->GetTargetBounds()));
71 gfx::Transform new_transform = TransformAboutPivot(
72 gfx::Point(bounds.x(), bounds.y()),
73 window->layer()->GetTargetTransform());
74 new_transform.TransformRect(&bounds);
75 return ToEnclosingRect(bounds);
78 // Convenvience method to fade in a Window with predefined animation settings.
79 // Note: The fade in animation will occur after a delay where the delay is how
80 // long the lay out animations take.
81 void SetupFadeInAfterLayout(aura::Window* window) {
82 ui::Layer* layer = window->layer();
83 layer->SetOpacity(0.0f);
84 ScopedOverviewAnimationSettings animation_settings(
85 OverviewAnimationType::OVERVIEW_ANIMATION_ENTER_OVERVIEW_MODE_FADE_IN,
86 window);
87 layer->SetOpacity(1.0f);
90 // An image button with a close window icon.
91 class OverviewCloseButton : public views::ImageButton {
92 public:
93 explicit OverviewCloseButton(views::ButtonListener* listener);
94 ~OverviewCloseButton() override;
96 private:
97 DISALLOW_COPY_AND_ASSIGN(OverviewCloseButton);
100 OverviewCloseButton::OverviewCloseButton(views::ButtonListener* listener)
101 : views::ImageButton(listener) {
102 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
103 SetImage(views::CustomButton::STATE_NORMAL,
104 rb.GetImageSkiaNamed(IDR_AURA_WINDOW_OVERVIEW_CLOSE));
105 SetImage(views::CustomButton::STATE_HOVERED,
106 rb.GetImageSkiaNamed(IDR_AURA_WINDOW_OVERVIEW_CLOSE_H));
107 SetImage(views::CustomButton::STATE_PRESSED,
108 rb.GetImageSkiaNamed(IDR_AURA_WINDOW_OVERVIEW_CLOSE_P));
111 OverviewCloseButton::~OverviewCloseButton() {
114 } // namespace
116 WindowSelectorItem::WindowSelectorItem(aura::Window* root_window)
117 : dimmed_(false),
118 root_window_(root_window),
119 in_bounds_update_(false),
120 window_label_view_(nullptr),
121 close_button_(new OverviewCloseButton(this)),
122 selector_item_activate_window_button_(
123 new TransparentActivateWindowButton(root_window, this)) {
124 views::Widget::InitParams params;
125 params.type = views::Widget::InitParams::TYPE_POPUP;
126 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
127 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
128 params.parent = Shell::GetContainer(root_window,
129 kShellWindowId_OverlayContainer);
130 close_button_widget_.set_focus_on_creation(false);
131 close_button_widget_.Init(params);
132 close_button_->SetVisible(false);
133 close_button_widget_.SetContentsView(close_button_);
134 close_button_widget_.SetSize(close_button_->GetPreferredSize());
135 close_button_widget_.Show();
137 gfx::Rect close_button_rect(close_button_widget_.GetNativeWindow()->bounds());
138 // Align the center of the button with position (0, 0) so that the
139 // translate transform does not need to take the button dimensions into
140 // account.
141 close_button_rect.set_x(-close_button_rect.width() / 2);
142 close_button_rect.set_y(-close_button_rect.height() / 2);
143 close_button_widget_.GetNativeWindow()->SetBounds(close_button_rect);
146 WindowSelectorItem::~WindowSelectorItem() {
147 for (auto* transform_window : transform_windows_) {
148 transform_window->window()->RemoveObserver(this);
152 void WindowSelectorItem::AddWindow(aura::Window* window) {
153 DCHECK(window->GetRootWindow() == root_window_);
154 window->AddObserver(this);
155 ScopedTransformOverviewWindow* transform_window =
156 new ScopedTransformOverviewWindow(window);
157 transform_windows_.push_back(transform_window);
158 // The transparent overlays are added at the front of the z-order when
159 // created so make sure the selector item's transparent overlay is behind the
160 // overlay for the window that was just added.
161 transform_window->activate_button()->StackAbove(
162 selector_item_activate_window_button_.get());
164 UpdateSelectorButtons();
165 UpdateCloseButtonAccessibilityName();
168 bool WindowSelectorItem::HasSelectableWindow(const aura::Window* window) const {
169 for (auto* transform_window : transform_windows_) {
170 if (transform_window->window() == window)
171 return true;
173 return false;
176 bool WindowSelectorItem::Contains(const aura::Window* target) const {
177 for (auto* transform_window : transform_windows_) {
178 if (transform_window->Contains(target))
179 return true;
181 return false;
184 void WindowSelectorItem::RestoreWindowOnExit(aura::Window* window) {
185 for (auto* transform_window : transform_windows_) {
186 if (transform_window->Contains(window)) {
187 transform_window->RestoreWindowOnExit();
188 break;
193 aura::Window* WindowSelectorItem::SelectionWindow() const {
194 return SelectionTransformWindow()->window();
197 void WindowSelectorItem::RemoveWindow(const aura::Window* window) {
198 bool window_found = false;
200 for (TransformWindows::iterator iter = transform_windows_.begin();
201 iter != transform_windows_.end();
202 ++iter) {
203 ScopedTransformOverviewWindow* transform_window = *iter;
205 if (transform_window->window() == window) {
206 transform_window->window()->RemoveObserver(this);
207 transform_window->OnWindowDestroyed();
208 transform_windows_.erase(iter);
209 window_found = true;
210 break;
213 CHECK(window_found);
216 // If empty WindowSelectorItem will be destroyed immediately after this by
217 // its owner.
218 if (empty())
219 return;
221 UpdateCloseButtonAccessibilityName();
222 window_label_.reset();
223 UpdateWindowLabels(target_bounds_,
224 OverviewAnimationType::OVERVIEW_ANIMATION_NONE);
225 UpdateCloseButtonLayout(OverviewAnimationType::OVERVIEW_ANIMATION_NONE);
226 UpdateSelectorButtons();
229 bool WindowSelectorItem::empty() const {
230 return transform_windows_.empty();
233 void WindowSelectorItem::PrepareForOverview() {
234 for (auto* transform_window : transform_windows_)
235 transform_window->PrepareForOverview();
238 void WindowSelectorItem::SetBounds(aura::Window* root_window,
239 const gfx::Rect& target_bounds,
240 OverviewAnimationType animation_type) {
241 if (in_bounds_update_)
242 return;
243 base::AutoReset<bool> auto_reset_in_bounds_update(&in_bounds_update_, true);
244 target_bounds_ = target_bounds;
246 UpdateWindowLabels(target_bounds, animation_type);
248 gfx::Rect inset_bounds(target_bounds);
249 inset_bounds.Inset(kWindowMargin, kWindowMargin);
250 SetItemBounds(root_window, inset_bounds, animation_type);
252 // SetItemBounds is called before UpdateCloseButtonLayout so the close button
253 // can properly use the updated windows bounds.
254 UpdateCloseButtonLayout(animation_type);
255 UpdateSelectorButtons();
258 void WindowSelectorItem::RecomputeWindowTransforms() {
259 if (in_bounds_update_ || target_bounds_.IsEmpty())
260 return;
261 DCHECK(root_window_);
262 base::AutoReset<bool> auto_reset_in_bounds_update(&in_bounds_update_, true);
263 gfx::Rect inset_bounds(target_bounds_);
264 inset_bounds.Inset(kWindowMargin, kWindowMargin);
265 SetItemBounds(root_window_, inset_bounds,
266 OverviewAnimationType::OVERVIEW_ANIMATION_NONE);
268 UpdateCloseButtonLayout(OverviewAnimationType::OVERVIEW_ANIMATION_NONE);
269 UpdateSelectorButtons();
272 void WindowSelectorItem::SendFocusAlert() const {
273 selector_item_activate_window_button_->SendFocusAlert();
276 void WindowSelectorItem::SetDimmed(bool dimmed) {
277 dimmed_ = dimmed;
278 SetOpacity(dimmed ? kDimmedItemOpacity : 1.0f);
281 void WindowSelectorItem::ButtonPressed(views::Button* sender,
282 const ui::Event& event) {
283 CHECK(!transform_windows_.empty());
284 SelectionTransformWindow()->Close();
287 void WindowSelectorItem::OnWindowTitleChanged(aura::Window* window) {
288 // TODO(flackr): Maybe add the new title to a vector of titles so that we can
289 // filter any of the titles the window had while in the overview session.
290 if (window == SelectionWindow()) {
291 window_label_view_->SetText(window->title());
292 UpdateCloseButtonAccessibilityName();
294 UpdateCloseButtonLayout(OverviewAnimationType::OVERVIEW_ANIMATION_NONE);
295 UpdateSelectorButtons();
298 void WindowSelectorItem::Select() {
299 aura::Window* selection_window = SelectionWindow();
300 if (selection_window)
301 wm::GetWindowState(selection_window)->Activate();
304 void WindowSelectorItem::SetItemBounds(aura::Window* root_window,
305 const gfx::Rect& target_bounds,
306 OverviewAnimationType animation_type) {
307 gfx::Rect bounding_rect;
308 for (auto* transform_window : transform_windows_) {
309 bounding_rect.Union(
310 transform_window->GetTargetBoundsInScreen());
312 gfx::Rect bounds =
313 ScopedTransformOverviewWindow::ShrinkRectToFitPreservingAspectRatio(
314 bounding_rect, target_bounds);
315 gfx::Transform bounding_transform =
316 ScopedTransformOverviewWindow::GetTransformForRect(bounding_rect, bounds);
317 for (auto* transform_window : transform_windows_) {
318 gfx::Rect target_bounds = transform_window->GetTargetBoundsInScreen();
319 gfx::Transform transform = TransformAboutPivot(
320 gfx::Point(bounding_rect.x() - target_bounds.x(),
321 bounding_rect.y() - target_bounds.y()),
322 bounding_transform);
324 ScopedTransformOverviewWindow::ScopedAnimationSettings animation_settings;
325 transform_window->BeginScopedAnimation(animation_type, &animation_settings);
326 transform_window->SetTransform(root_window, transform);
327 transform_window->set_overview_transform(transform);
331 void WindowSelectorItem::SetOpacity(float opacity) {
332 window_label_->GetNativeWindow()->layer()->SetOpacity(opacity);
333 close_button_widget_.GetNativeWindow()->layer()->SetOpacity(opacity);
335 // TODO(flackr): find a way to make panels that are hidden behind other panels
336 // look nice.
337 for (auto* transform_window : transform_windows_) {
338 transform_window->SetOpacity(opacity);
342 void WindowSelectorItem::UpdateWindowLabels(
343 const gfx::Rect& window_bounds,
344 OverviewAnimationType animation_type) {
345 // If the root window has changed, force the window label to be recreated
346 // and faded in on the new root window.
347 DCHECK(!window_label_ ||
348 window_label_->GetNativeWindow()->GetRootWindow() == root_window_);
350 if (!window_label_) {
351 CreateWindowLabel(SelectionWindow()->title());
352 SetupFadeInAfterLayout(window_label_->GetNativeWindow());
355 gfx::Rect converted_bounds = ScreenUtil::ConvertRectFromScreen(root_window_,
356 window_bounds);
357 gfx::Rect label_bounds(converted_bounds.x(),
358 converted_bounds.bottom(),
359 converted_bounds.width(),
361 label_bounds.set_height(window_label_->GetContentsView()->
362 GetPreferredSize().height());
363 label_bounds.set_y(label_bounds.y() - window_label_->
364 GetContentsView()->GetPreferredSize().height());
366 ScopedOverviewAnimationSettings animation_settings(animation_type,
367 window_label_->GetNativeWindow());
369 window_label_->GetNativeWindow()->SetBounds(label_bounds);
372 void WindowSelectorItem::CreateWindowLabel(const base::string16& title) {
373 window_label_.reset(new views::Widget);
374 views::Widget::InitParams params;
375 params.type = views::Widget::InitParams::TYPE_POPUP;
376 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
377 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
378 params.parent = Shell::GetContainer(root_window_,
379 kShellWindowId_OverlayContainer);
380 params.accept_events = false;
381 params.visible_on_all_workspaces = true;
382 window_label_->set_focus_on_creation(false);
383 window_label_->Init(params);
384 window_label_view_ = new views::Label;
385 window_label_view_->SetEnabledColor(kLabelColor);
386 window_label_view_->SetBackgroundColor(kLabelBackground);
387 window_label_view_->SetShadows(gfx::ShadowValues(
389 gfx::ShadowValue(
390 gfx::Point(0, kVerticalShadowOffset), kShadowBlur, kLabelShadow)));
391 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
392 window_label_view_->SetFontList(
393 bundle.GetFontList(ui::ResourceBundle::BoldFont));
394 window_label_view_->SetText(title);
395 views::BoxLayout* layout = new views::BoxLayout(views::BoxLayout::kVertical,
397 kVerticalLabelPadding,
399 window_label_view_->SetLayoutManager(layout);
400 window_label_->SetContentsView(window_label_view_);
401 window_label_->Show();
404 void WindowSelectorItem::UpdateSelectorButtons() {
405 CHECK(!transform_windows_.empty());
407 selector_item_activate_window_button_->SetBounds(target_bounds());
408 selector_item_activate_window_button_->SetAccessibleName(
409 transform_windows_.front()->window()->title());
411 for (auto* transform_window : transform_windows_) {
412 TransparentActivateWindowButton* activate_button =
413 transform_window->activate_button();
415 // If there is only one window in this, then expand the transparent overlay
416 // so that touch exploration in ChromVox only provides spoken feedback once
417 // within |this| selector item's bounds.
418 gfx::Rect bounds = transform_windows_.size() == 1
419 ? target_bounds() : GetTransformedBounds(transform_window->window());
420 activate_button->SetBounds(bounds);
421 activate_button->SetAccessibleName(transform_window->window()->title());
425 void WindowSelectorItem::UpdateCloseButtonLayout(
426 OverviewAnimationType animation_type) {
427 if (!close_button_->visible()) {
428 close_button_->SetVisible(true);
429 SetupFadeInAfterLayout(close_button_widget_.GetNativeWindow());
431 ScopedOverviewAnimationSettings animation_settings(animation_type,
432 close_button_widget_.GetNativeWindow());
434 gfx::Rect transformed_window_bounds = ScreenUtil::ConvertRectFromScreen(
435 close_button_widget_.GetNativeWindow()->GetRootWindow(),
436 GetTransformedBounds(SelectionWindow()));
438 gfx::Transform close_button_transform;
439 close_button_transform.Translate(transformed_window_bounds.right(),
440 transformed_window_bounds.y());
441 close_button_widget_.GetNativeWindow()->SetTransform(
442 close_button_transform);
445 void WindowSelectorItem::UpdateCloseButtonAccessibilityName() {
446 close_button_->SetAccessibleName(l10n_util::GetStringFUTF16(
447 IDS_ASH_OVERVIEW_CLOSE_ITEM_BUTTON_ACCESSIBLE_NAME,
448 SelectionWindow()->title()));
451 ScopedTransformOverviewWindow*
452 WindowSelectorItem::SelectionTransformWindow() const {
453 CHECK(!transform_windows_.empty());
454 return transform_windows_.front();
457 } // namespace ash