• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "ash/wm/overview/window_selector_item.h"
6 
7 #include "ash/screen_util.h"
8 #include "ash/shell.h"
9 #include "ash/shell_window_ids.h"
10 #include "ash/wm/overview/scoped_transform_overview_window.h"
11 #include "ash/wm/overview/transparent_activate_window_button.h"
12 #include "base/auto_reset.h"
13 #include "grit/ash_resources.h"
14 #include "ui/aura/window.h"
15 #include "ui/base/resource/resource_bundle.h"
16 #include "ui/compositor/scoped_layer_animation_settings.h"
17 #include "ui/views/controls/button/image_button.h"
18 #include "ui/views/controls/label.h"
19 #include "ui/views/layout/box_layout.h"
20 #include "ui/views/widget/widget.h"
21 
22 namespace ash {
23 
24 namespace {
25 
CreateCloseWindowButton(aura::Window * root_window,views::ButtonListener * listener)26 views::Widget* CreateCloseWindowButton(aura::Window* root_window,
27                                        views::ButtonListener* listener) {
28   views::Widget* widget = new views::Widget;
29   views::Widget::InitParams params;
30   params.type = views::Widget::InitParams::TYPE_POPUP;
31   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
32   params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
33   params.parent =
34       Shell::GetContainer(root_window, ash::kShellWindowId_OverlayContainer);
35   widget->set_focus_on_creation(false);
36   widget->Init(params);
37   views::ImageButton* button = new views::ImageButton(listener);
38   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
39   button->SetImage(views::CustomButton::STATE_NORMAL,
40                    rb.GetImageSkiaNamed(IDR_AURA_WINDOW_OVERVIEW_CLOSE));
41   button->SetImage(views::CustomButton::STATE_HOVERED,
42                    rb.GetImageSkiaNamed(IDR_AURA_WINDOW_OVERVIEW_CLOSE_H));
43   button->SetImage(views::CustomButton::STATE_PRESSED,
44                    rb.GetImageSkiaNamed(IDR_AURA_WINDOW_OVERVIEW_CLOSE_P));
45   widget->SetContentsView(button);
46   widget->SetSize(rb.GetImageSkiaNamed(IDR_AURA_WINDOW_OVERVIEW_CLOSE)->size());
47   widget->Show();
48   return widget;
49 }
50 
51 }  // namespace
52 
53 // In the conceptual overview table, the window margin is the space reserved
54 // around the window within the cell. This margin does not overlap so the
55 // closest distance between adjacent windows will be twice this amount.
56 static const int kWindowMargin = 30;
57 
58 // Foreground label color.
59 static const SkColor kLabelColor = SK_ColorWHITE;
60 
61 // Background label color.
62 static const SkColor kLabelBackground = SK_ColorTRANSPARENT;
63 
64 // Label shadow color.
65 static const SkColor kLabelShadow = 0xB0000000;
66 
67 // Vertical padding for the label, both over and beneath it.
68 static const int kVerticalLabelPadding = 20;
69 
70 // Solid shadow length from the label
71 static const int kVerticalShadowOffset = 1;
72 
73 // Amount of blur applied to the label shadow
74 static const int kShadowBlur = 10;
75 
76 const int WindowSelectorItem::kFadeInMilliseconds = 80;
77 
78 // Opacity for dimmed items.
79 static const float kDimmedItemOpacity = 0.5f;
80 
WindowSelectorItem()81 WindowSelectorItem::WindowSelectorItem()
82     : dimmed_(false),
83       root_window_(NULL),
84       in_bounds_update_(false),
85       window_label_view_(NULL) {
86 }
87 
~WindowSelectorItem()88 WindowSelectorItem::~WindowSelectorItem() {
89 }
90 
RemoveWindow(const aura::Window * window)91 void WindowSelectorItem::RemoveWindow(const aura::Window* window) {
92   // If empty WindowSelectorItem will be destroyed immediately after this by
93   // its owner.
94   if (empty())
95     return;
96   window_label_.reset();
97   UpdateWindowLabels(target_bounds_, root_window_, false);
98   UpdateCloseButtonBounds(root_window_, false);
99 }
100 
SetBounds(aura::Window * root_window,const gfx::Rect & target_bounds,bool animate)101 void WindowSelectorItem::SetBounds(aura::Window* root_window,
102                                    const gfx::Rect& target_bounds,
103                                    bool animate) {
104   if (in_bounds_update_)
105     return;
106   base::AutoReset<bool> auto_reset_in_bounds_update(&in_bounds_update_, true);
107   root_window_ = root_window;
108   target_bounds_ = target_bounds;
109 
110   // Set the bounds of the transparent window handler to cover the entire
111   // bounding box area.
112   if (!activate_window_button_) {
113     activate_window_button_.reset(
114         new TransparentActivateWindowButton(SelectionWindow()));
115   }
116   activate_window_button_->SetBounds(target_bounds);
117 
118   UpdateWindowLabels(target_bounds, root_window, animate);
119 
120   gfx::Rect inset_bounds(target_bounds);
121   inset_bounds.Inset(kWindowMargin, kWindowMargin);
122   SetItemBounds(root_window, inset_bounds, animate);
123   UpdateCloseButtonBounds(root_window, animate);
124 }
125 
RecomputeWindowTransforms()126 void WindowSelectorItem::RecomputeWindowTransforms() {
127   if (in_bounds_update_ || target_bounds_.IsEmpty())
128     return;
129   DCHECK(root_window_);
130   base::AutoReset<bool> auto_reset_in_bounds_update(&in_bounds_update_, true);
131   gfx::Rect inset_bounds(target_bounds_);
132   inset_bounds.Inset(kWindowMargin, kWindowMargin);
133   SetItemBounds(root_window_, inset_bounds, false);
134   UpdateCloseButtonBounds(root_window_, false);
135 }
136 
SendFocusAlert() const137 void WindowSelectorItem::SendFocusAlert() const {
138   activate_window_button_->SendFocusAlert();
139 }
140 
SetDimmed(bool dimmed)141 void WindowSelectorItem::SetDimmed(bool dimmed) {
142   dimmed_ = dimmed;
143   SetOpacity(dimmed ? kDimmedItemOpacity : 1.0f);
144 }
145 
ButtonPressed(views::Button * sender,const ui::Event & event)146 void WindowSelectorItem::ButtonPressed(views::Button* sender,
147                                        const ui::Event& event) {
148   views::Widget::GetWidgetForNativeView(SelectionWindow())->Close();
149 }
150 
OnWindowTitleChanged(aura::Window * window)151 void WindowSelectorItem::OnWindowTitleChanged(aura::Window* window) {
152   // TODO(flackr): Maybe add the new title to a vector of titles so that we can
153   // filter any of the titles the window had while in the overview session.
154   if (window == SelectionWindow())
155     window_label_view_->SetText(window->title());
156 }
157 
UpdateCloseButtonBounds(aura::Window * root_window,bool animate)158 void WindowSelectorItem::UpdateCloseButtonBounds(aura::Window* root_window,
159                                                  bool animate) {
160   gfx::RectF align_bounds(SelectionWindow()->layer()->bounds());
161   gfx::Transform window_transform;
162   window_transform.Translate(align_bounds.x(), align_bounds.y());
163   window_transform.PreconcatTransform(SelectionWindow()->layer()->
164                                           GetTargetTransform());
165   window_transform.Translate(-align_bounds.x(), -align_bounds.y());
166   window_transform.TransformRect(&align_bounds);
167   gfx::Rect target_bounds = ToEnclosingRect(align_bounds);
168 
169   gfx::Transform close_button_transform;
170   close_button_transform.Translate(target_bounds.right(), target_bounds.y());
171 
172   // If the root window has changed, force the close button to be recreated
173   // and faded in on the new root window.
174   if (close_button_ &&
175       close_button_->GetNativeWindow()->GetRootWindow() != root_window) {
176     close_button_.reset();
177   }
178 
179   if (!close_button_) {
180     close_button_.reset(CreateCloseWindowButton(root_window, this));
181     gfx::Rect close_button_rect(close_button_->GetNativeWindow()->bounds());
182     // Align the center of the button with position (0, 0) so that the
183     // translate transform does not need to take the button dimensions into
184     // account.
185     close_button_rect.set_x(-close_button_rect.width() / 2);
186     close_button_rect.set_y(-close_button_rect.height() / 2);
187     close_button_->GetNativeWindow()->SetBounds(close_button_rect);
188     close_button_->GetNativeWindow()->SetTransform(close_button_transform);
189     // The close button is initialized when entering overview, fade the button
190     // in after the window should be in place.
191     ui::Layer* layer = close_button_->GetNativeWindow()->layer();
192     layer->SetOpacity(0);
193     layer->GetAnimator()->StopAnimating();
194     layer->GetAnimator()->SchedulePauseForProperties(
195         base::TimeDelta::FromMilliseconds(
196             ScopedTransformOverviewWindow::kTransitionMilliseconds),
197         ui::LayerAnimationElement::OPACITY);
198     {
199       ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
200       settings.SetPreemptionStrategy(
201           ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
202       settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
203           WindowSelectorItem::kFadeInMilliseconds));
204       layer->SetOpacity(1);
205     }
206   } else {
207     if (animate) {
208       ui::ScopedLayerAnimationSettings settings(
209           close_button_->GetNativeWindow()->layer()->GetAnimator());
210       settings.SetPreemptionStrategy(
211           ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
212       settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
213           ScopedTransformOverviewWindow::kTransitionMilliseconds));
214       close_button_->GetNativeWindow()->SetTransform(close_button_transform);
215     } else {
216       close_button_->GetNativeWindow()->SetTransform(close_button_transform);
217     }
218   }
219 }
220 
SetOpacity(float opacity)221 void WindowSelectorItem::SetOpacity(float opacity) {
222   window_label_->GetNativeWindow()->layer()->SetOpacity(opacity);
223   close_button_->GetNativeWindow()->layer()->SetOpacity(opacity);
224 }
225 
UpdateWindowLabels(const gfx::Rect & window_bounds,aura::Window * root_window,bool animate)226 void WindowSelectorItem::UpdateWindowLabels(const gfx::Rect& window_bounds,
227                                             aura::Window* root_window,
228                                             bool animate) {
229   gfx::Rect converted_bounds = ScreenUtil::ConvertRectFromScreen(root_window,
230                                                                  window_bounds);
231   gfx::Rect label_bounds(converted_bounds.x(),
232                          converted_bounds.bottom(),
233                          converted_bounds.width(),
234                          0);
235 
236   // If the root window has changed, force the window label to be recreated
237   // and faded in on the new root window.
238   if (window_label_ &&
239       window_label_->GetNativeWindow()->GetRootWindow() != root_window) {
240     window_label_.reset();
241   }
242 
243   if (!window_label_) {
244     CreateWindowLabel(SelectionWindow()->title());
245     label_bounds.set_height(window_label_view_->GetPreferredSize().height());
246     label_bounds.set_y(label_bounds.y() - window_label_view_->
247                            GetPreferredSize().height());
248     window_label_->GetNativeWindow()->SetBounds(label_bounds);
249     ui::Layer* layer = window_label_->GetNativeWindow()->layer();
250 
251     layer->SetOpacity(0);
252     layer->GetAnimator()->StopAnimating();
253 
254     layer->GetAnimator()->SchedulePauseForProperties(
255         base::TimeDelta::FromMilliseconds(
256             ScopedTransformOverviewWindow::kTransitionMilliseconds),
257         ui::LayerAnimationElement::OPACITY);
258 
259     ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
260     settings.SetPreemptionStrategy(
261         ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
262     settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
263         kFadeInMilliseconds));
264     layer->SetOpacity(1);
265   } else {
266     label_bounds.set_height(window_label_->
267                                 GetContentsView()->GetPreferredSize().height());
268     label_bounds.set_y(label_bounds.y() - window_label_->
269                            GetContentsView()->GetPreferredSize().height());
270     if (animate) {
271       ui::ScopedLayerAnimationSettings settings(
272           window_label_->GetNativeWindow()->layer()->GetAnimator());
273       settings.SetPreemptionStrategy(
274           ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
275       settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
276           ScopedTransformOverviewWindow::kTransitionMilliseconds));
277       window_label_->GetNativeWindow()->SetBounds(label_bounds);
278     } else {
279       window_label_->GetNativeWindow()->SetBounds(label_bounds);
280     }
281   }
282 }
283 
CreateWindowLabel(const base::string16 & title)284 void WindowSelectorItem::CreateWindowLabel(const base::string16& title) {
285   window_label_.reset(new views::Widget);
286   views::Widget::InitParams params;
287   params.type = views::Widget::InitParams::TYPE_POPUP;
288   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
289   params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
290   params.parent =
291       Shell::GetContainer(root_window_, ash::kShellWindowId_OverlayContainer);
292   params.accept_events = false;
293   params.visible_on_all_workspaces = true;
294   window_label_->set_focus_on_creation(false);
295   window_label_->Init(params);
296   window_label_view_ = new views::Label;
297   window_label_view_->SetEnabledColor(kLabelColor);
298   window_label_view_->SetBackgroundColor(kLabelBackground);
299   window_label_view_->SetShadows(gfx::ShadowValues(
300       1,
301       gfx::ShadowValue(
302           gfx::Point(0, kVerticalShadowOffset), kShadowBlur, kLabelShadow)));
303   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
304   window_label_view_->SetFontList(
305       bundle.GetFontList(ui::ResourceBundle::BoldFont));
306   window_label_view_->SetText(title);
307   views::BoxLayout* layout = new views::BoxLayout(views::BoxLayout::kVertical,
308                                                   0,
309                                                   kVerticalLabelPadding,
310                                                   0);
311   window_label_view_->SetLayoutManager(layout);
312   window_label_->SetContentsView(window_label_view_);
313   window_label_->Show();
314 }
315 
316 }  // namespace ash
317