Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / ash / wm / overview / window_grid.cc
1 // Copyright 2014 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_grid.h"
6
7 #include "ash/ash_switches.h"
8 #include "ash/screen_util.h"
9 #include "ash/shell.h"
10 #include "ash/shell_window_ids.h"
11 #include "ash/wm/overview/scoped_transform_overview_window.h"
12 #include "ash/wm/overview/window_selector.h"
13 #include "ash/wm/overview/window_selector_item.h"
14 #include "ash/wm/overview/window_selector_panels.h"
15 #include "ash/wm/overview/window_selector_window.h"
16 #include "ash/wm/window_state.h"
17 #include "base/command_line.h"
18 #include "base/i18n/string_search.h"
19 #include "base/memory/scoped_vector.h"
20 #include "third_party/skia/include/core/SkColor.h"
21 #include "ui/aura/window.h"
22 #include "ui/compositor/layer_animation_observer.h"
23 #include "ui/compositor/scoped_layer_animation_settings.h"
24 #include "ui/gfx/animation/tween.h"
25 #include "ui/gfx/vector2d.h"
26 #include "ui/views/background.h"
27 #include "ui/views/view.h"
28 #include "ui/views/widget/widget.h"
29 #include "ui/wm/core/window_animations.h"
30
31 namespace ash {
32 namespace {
33
34 // An observer which holds onto the passed widget until the animation is
35 // complete.
36 class CleanupWidgetAfterAnimationObserver
37     : public ui::ImplicitAnimationObserver {
38  public:
39   explicit CleanupWidgetAfterAnimationObserver(
40       scoped_ptr<views::Widget> widget);
41   ~CleanupWidgetAfterAnimationObserver() override;
42
43   // ui::ImplicitAnimationObserver:
44   void OnImplicitAnimationsCompleted() override;
45
46  private:
47   scoped_ptr<views::Widget> widget_;
48
49   DISALLOW_COPY_AND_ASSIGN(CleanupWidgetAfterAnimationObserver);
50 };
51
52 CleanupWidgetAfterAnimationObserver::CleanupWidgetAfterAnimationObserver(
53     scoped_ptr<views::Widget> widget)
54     : widget_(widget.Pass()) {
55 }
56
57 CleanupWidgetAfterAnimationObserver::~CleanupWidgetAfterAnimationObserver() {
58 }
59
60 void CleanupWidgetAfterAnimationObserver::OnImplicitAnimationsCompleted() {
61   delete this;
62 }
63
64 // A comparator for locating a given target window.
65 struct WindowSelectorItemComparator
66     : public std::unary_function<WindowSelectorItem*, bool> {
67   explicit WindowSelectorItemComparator(const aura::Window* target_window)
68       : target(target_window) {
69   }
70
71   bool operator()(WindowSelectorItem* window) const {
72     return window->HasSelectableWindow(target);
73   }
74
75   const aura::Window* target;
76 };
77
78 // A comparator for locating a WindowSelectorItem given a targeted window.
79 struct WindowSelectorItemTargetComparator
80     : public std::unary_function<WindowSelectorItem*, bool> {
81   explicit WindowSelectorItemTargetComparator(const aura::Window* target_window)
82       : target(target_window) {
83   }
84
85   bool operator()(WindowSelectorItem* window) const {
86     return window->Contains(target);
87   }
88
89   const aura::Window* target;
90 };
91
92 // Conceptually the window overview is a table or grid of cells having this
93 // fixed aspect ratio. The number of columns is determined by maximizing the
94 // area of them based on the number of window_list.
95 const float kCardAspectRatio = 4.0f / 3.0f;
96
97 // The minimum number of cards along the major axis (i.e. horizontally on a
98 // landscape orientation).
99 const int kMinCardsMajor = 3;
100
101 const int kOverviewSelectorTransitionMilliseconds = 100;
102
103 // The color and opacity of the overview selector.
104 const SkColor kWindowOverviewSelectionColor = SK_ColorBLACK;
105 const unsigned char kWindowOverviewSelectorOpacity = 128;
106
107 // The minimum amount of spacing between the bottom of the text filtering
108 // text field and the top of the selection widget on the first row of items.
109 const int kTextFilterBottomMargin = 5;
110
111 // Returns the vector for the fade in animation.
112 gfx::Vector2d GetSlideVectorForFadeIn(WindowSelector::Direction direction,
113                                       const gfx::Rect& bounds) {
114   gfx::Vector2d vector;
115   switch (direction) {
116     case WindowSelector::DOWN:
117       vector.set_y(bounds.width());
118       break;
119     case WindowSelector::RIGHT:
120       vector.set_x(bounds.height());
121       break;
122     case WindowSelector::UP:
123       vector.set_y(-bounds.width());
124       break;
125     case WindowSelector::LEFT:
126       vector.set_x(-bounds.height());
127       break;
128   }
129   return vector;
130 }
131
132 }  // namespace
133
134 WindowGrid::WindowGrid(aura::Window* root_window,
135                        const std::vector<aura::Window*>& windows,
136                        WindowSelector* window_selector)
137     : root_window_(root_window),
138       window_selector_(window_selector) {
139   WindowSelectorPanels* panels_item = NULL;
140   for (aura::Window::Windows::const_iterator iter = windows.begin();
141        iter != windows.end(); ++iter) {
142     if ((*iter)->GetRootWindow() != root_window)
143       continue;
144     (*iter)->AddObserver(this);
145     observed_windows_.insert(*iter);
146
147     if ((*iter)->type() == ui::wm::WINDOW_TYPE_PANEL &&
148         wm::GetWindowState(*iter)->panel_attached()) {
149       // Attached panel windows are grouped into a single overview item per
150       // grid.
151       if (!panels_item) {
152         panels_item = new WindowSelectorPanels(root_window_);
153         window_list_.push_back(panels_item);
154       }
155       panels_item->AddWindow(*iter);
156     } else {
157       window_list_.push_back(new WindowSelectorWindow(*iter));
158     }
159   }
160   if (window_list_.empty())
161     return;
162 }
163
164 WindowGrid::~WindowGrid() {
165   for (std::set<aura::Window*>::iterator iter = observed_windows_.begin();
166        iter != observed_windows_.end(); iter++) {
167     (*iter)->RemoveObserver(this);
168   }
169 }
170
171 void WindowGrid::PrepareForOverview() {
172   for (ScopedVector<WindowSelectorItem>::iterator iter = window_list_.begin();
173        iter != window_list_.end(); ++iter) {
174     (*iter)->PrepareForOverview();
175   }
176 }
177
178 void WindowGrid::PositionWindows(bool animate) {
179   CHECK(!window_list_.empty());
180
181   gfx::Size window_size;
182   gfx::Rect total_bounds = ScreenUtil::ConvertRectToScreen(
183       root_window_,
184       ScreenUtil::GetDisplayWorkAreaBoundsInParent(
185           Shell::GetContainer(root_window_, kShellWindowId_DefaultContainer)));
186
187   // If the text filtering feature is enabled, reserve space at the top for the
188   // text filtering textbox to appear.
189   if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
190           switches::kAshDisableTextFilteringInOverviewMode)) {
191     total_bounds.Inset(
192         0,
193         WindowSelector::kTextFilterBottomEdge + kTextFilterBottomMargin,
194         0,
195         0);
196   }
197
198   // Find the minimum number of windows per row that will fit all of the
199   // windows on screen.
200   num_columns_ = std::max(
201       total_bounds.width() > total_bounds.height() ? kMinCardsMajor : 1,
202       static_cast<int>(ceil(sqrt(total_bounds.width() * window_list_.size() /
203                                  (kCardAspectRatio * total_bounds.height())))));
204   int num_rows = ((window_list_.size() + num_columns_ - 1) / num_columns_);
205   window_size.set_width(std::min(
206       static_cast<int>(total_bounds.width() / num_columns_),
207       static_cast<int>(total_bounds.height() * kCardAspectRatio / num_rows)));
208   window_size.set_height(window_size.width() / kCardAspectRatio);
209
210   // Calculate the X and Y offsets necessary to center the grid.
211   int x_offset = total_bounds.x() + ((window_list_.size() >= num_columns_ ? 0 :
212       (num_columns_ - window_list_.size()) * window_size.width()) +
213       (total_bounds.width() - num_columns_ * window_size.width())) / 2;
214   int y_offset = total_bounds.y() + (total_bounds.height() -
215       num_rows * window_size.height()) / 2;
216
217   for (size_t i = 0; i < window_list_.size(); ++i) {
218     gfx::Transform transform;
219     int column = i % num_columns_;
220     int row = i / num_columns_;
221     gfx::Rect target_bounds(window_size.width() * column + x_offset,
222                             window_size.height() * row + y_offset,
223                             window_size.width(),
224                             window_size.height());
225     window_list_[i]->SetBounds(root_window_, target_bounds, animate);
226   }
227
228   // If we have less than |kMinCardsMajor| windows, adjust the column_ value to
229   // reflect how many "real" columns we have.
230   if (num_columns_ > window_list_.size())
231     num_columns_ = window_list_.size();
232
233   // If the selection widget is active, reposition it without any animation.
234   if (selection_widget_)
235     MoveSelectionWidgetToTarget(animate);
236 }
237
238 bool WindowGrid::Move(WindowSelector::Direction direction, bool animate) {
239   bool recreate_selection_widget = false;
240   bool out_of_bounds = false;
241   if (!selection_widget_) {
242     switch (direction) {
243      case WindowSelector::LEFT:
244        selected_index_ = window_list_.size() - 1;
245        break;
246      case WindowSelector::UP:
247        selected_index_ =
248            (window_list_.size() / num_columns_) * num_columns_ - 1;
249        break;
250      case WindowSelector::RIGHT:
251      case WindowSelector::DOWN:
252        selected_index_ = 0;
253        break;
254      }
255   }
256   while (SelectedWindow()->dimmed() || selection_widget_) {
257     switch (direction) {
258       case WindowSelector::RIGHT:
259         if (selected_index_ >= window_list_.size() - 1)
260           out_of_bounds = true;
261         selected_index_++;
262         if (selected_index_ % num_columns_ == 0)
263           recreate_selection_widget = true;
264         break;
265       case WindowSelector::LEFT:
266         if (selected_index_ == 0)
267           out_of_bounds = true;
268         selected_index_--;
269         if ((selected_index_ + 1) % num_columns_ == 0)
270           recreate_selection_widget = true;
271         break;
272       case WindowSelector::DOWN:
273         selected_index_ += num_columns_;
274         if (selected_index_ >= window_list_.size()) {
275           selected_index_ = (selected_index_ + 1) % num_columns_;
276           if (selected_index_ == 0)
277             out_of_bounds = true;
278           recreate_selection_widget = true;
279         }
280         break;
281       case WindowSelector::UP:
282         if (selected_index_ == 0)
283           out_of_bounds = true;
284         if (selected_index_ < num_columns_) {
285           selected_index_ += num_columns_ *
286               ((window_list_.size() - selected_index_) / num_columns_) - 1;
287           recreate_selection_widget = true;
288         } else {
289           selected_index_ -= num_columns_;
290         }
291         break;
292     }
293     // Exit the loop if we broke free from the grid or found an active item.
294     if (out_of_bounds || !SelectedWindow()->dimmed())
295       break;
296   }
297
298   MoveSelectionWidget(direction, recreate_selection_widget,
299                       out_of_bounds, animate);
300   return out_of_bounds;
301 }
302
303 WindowSelectorItem* WindowGrid::SelectedWindow() const {
304   CHECK(selected_index_ < window_list_.size());
305   return window_list_[selected_index_];
306 }
307
308 bool WindowGrid::Contains(const aura::Window* window) const {
309   return std::find_if(window_list_.begin(), window_list_.end(),
310                       WindowSelectorItemTargetComparator(window)) !=
311                           window_list_.end();
312 }
313
314 void WindowGrid::FilterItems(const base::string16& pattern) {
315   base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents finder(pattern);
316   for (ScopedVector<WindowSelectorItem>::iterator iter = window_list_.begin();
317        iter != window_list_.end(); iter++) {
318     if (finder.Search((*iter)->SelectionWindow()->title(), NULL, NULL)) {
319       (*iter)->SetDimmed(false);
320     } else {
321       (*iter)->SetDimmed(true);
322       if (selection_widget_ && SelectedWindow() == *iter)
323         selection_widget_.reset();
324     }
325   }
326 }
327
328 void WindowGrid::OnWindowDestroying(aura::Window* window) {
329   window->RemoveObserver(this);
330   observed_windows_.erase(window);
331   ScopedVector<WindowSelectorItem>::iterator iter =
332       std::find_if(window_list_.begin(), window_list_.end(),
333                    WindowSelectorItemComparator(window));
334
335   DCHECK(iter != window_list_.end());
336
337   (*iter)->RemoveWindow(window);
338
339   // If there are still windows in this selector entry then the overview is
340   // still active and the active selection remains the same.
341   if (!(*iter)->empty())
342     return;
343
344   size_t removed_index = iter - window_list_.begin();
345   window_list_.erase(iter);
346
347   if (empty()) {
348     // If the grid is now empty, notify the window selector so that it erases us
349     // from its grid list.
350     window_selector_->OnGridEmpty(this);
351     return;
352   }
353
354   // If selecting, update the selection index.
355   if (selection_widget_) {
356     bool send_focus_alert = selected_index_ == removed_index;
357     if (selected_index_ >= removed_index && selected_index_ != 0)
358       selected_index_--;
359     if (send_focus_alert)
360       SelectedWindow()->SendFocusAlert();
361   }
362
363   PositionWindows(true);
364 }
365
366 void WindowGrid::OnWindowBoundsChanged(aura::Window* window,
367                                        const gfx::Rect& old_bounds,
368                                        const gfx::Rect& new_bounds) {
369   ScopedVector<WindowSelectorItem>::const_iterator iter =
370       std::find_if(window_list_.begin(), window_list_.end(),
371                    WindowSelectorItemTargetComparator(window));
372   DCHECK(iter != window_list_.end());
373
374   // Immediately finish any active bounds animation.
375   window->layer()->GetAnimator()->StopAnimatingProperty(
376       ui::LayerAnimationElement::BOUNDS);
377
378   // Recompute the transform for the window.
379   (*iter)->RecomputeWindowTransforms();
380 }
381
382 void WindowGrid::InitSelectionWidget(WindowSelector::Direction direction) {
383   selection_widget_.reset(new views::Widget);
384   views::Widget::InitParams params;
385   params.type = views::Widget::InitParams::TYPE_POPUP;
386   params.keep_on_top = false;
387   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
388   params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
389   params.parent = Shell::GetContainer(root_window_,
390                                       kShellWindowId_DefaultContainer);
391   params.accept_events = false;
392   selection_widget_->set_focus_on_creation(false);
393   selection_widget_->Init(params);
394   // Disable the "bounce in" animation when showing the window.
395   ::wm::SetWindowVisibilityAnimationTransition(
396       selection_widget_->GetNativeWindow(), ::wm::ANIMATE_NONE);
397   // The selection widget should not activate the shelf when passing under it.
398   ash::wm::GetWindowState(selection_widget_->GetNativeWindow())->
399       set_ignored_by_shelf(true);
400
401   views::View* content_view = new views::View;
402   content_view->set_background(
403       views::Background::CreateSolidBackground(kWindowOverviewSelectionColor));
404   selection_widget_->SetContentsView(content_view);
405   selection_widget_->GetNativeWindow()->parent()->StackChildAtBottom(
406       selection_widget_->GetNativeWindow());
407   selection_widget_->Show();
408   // New selection widget starts with 0 opacity and then fades in.
409   selection_widget_->GetNativeWindow()->layer()->SetOpacity(0);
410
411   const gfx::Rect target_bounds = SelectedWindow()->target_bounds();
412   gfx::Vector2d fade_out_direction =
413           GetSlideVectorForFadeIn(direction, target_bounds);
414   gfx::Display dst_display = gfx::Screen::GetScreenFor(root_window_)->
415       GetDisplayMatching(target_bounds);
416   selection_widget_->GetNativeWindow()->SetBoundsInScreen(
417       target_bounds - fade_out_direction, dst_display);
418 }
419
420 void WindowGrid::MoveSelectionWidget(WindowSelector::Direction direction,
421                                      bool recreate_selection_widget,
422                                      bool out_of_bounds,
423                                      bool animate) {
424   // If the selection widget is already active, fade it out in the selection
425   // direction.
426   if (selection_widget_ && (recreate_selection_widget || out_of_bounds)) {
427     // Animate the old selection widget and then destroy it.
428     views::Widget* old_selection = selection_widget_.get();
429     gfx::Vector2d fade_out_direction =
430         GetSlideVectorForFadeIn(
431             direction, old_selection->GetNativeWindow()->bounds());
432
433     ui::ScopedLayerAnimationSettings animation_settings(
434         old_selection->GetNativeWindow()->layer()->GetAnimator());
435     animation_settings.SetTransitionDuration(
436         base::TimeDelta::FromMilliseconds(
437             kOverviewSelectorTransitionMilliseconds));
438     animation_settings.SetPreemptionStrategy(
439         ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
440     animation_settings.SetTweenType(gfx::Tween::FAST_OUT_LINEAR_IN);
441     // CleanupWidgetAfterAnimationObserver will delete itself (and the
442     // widget) when the movement animation is complete.
443     animation_settings.AddObserver(
444         new CleanupWidgetAfterAnimationObserver(selection_widget_.Pass()));
445     old_selection->SetOpacity(0);
446     old_selection->GetNativeWindow()->SetBounds(
447         old_selection->GetNativeWindow()->bounds() + fade_out_direction);
448     old_selection->Hide();
449   }
450   if (out_of_bounds)
451     return;
452
453   if (!selection_widget_)
454     InitSelectionWidget(direction);
455   // Send an a11y alert so that if ChromeVox is enabled, the item label is
456   // read.
457   SelectedWindow()->SendFocusAlert();
458   // The selection widget is moved to the newly selected item in the same
459   // grid.
460   MoveSelectionWidgetToTarget(animate);
461 }
462
463 void WindowGrid::MoveSelectionWidgetToTarget(bool animate) {
464   if (animate) {
465     ui::ScopedLayerAnimationSettings animation_settings(
466         selection_widget_->GetNativeWindow()->layer()->GetAnimator());
467     animation_settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
468         kOverviewSelectorTransitionMilliseconds));
469     animation_settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
470     animation_settings.SetPreemptionStrategy(
471         ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
472     selection_widget_->SetBounds(SelectedWindow()->target_bounds());
473     selection_widget_->SetOpacity(kWindowOverviewSelectorOpacity);
474     return;
475   }
476   selection_widget_->SetBounds(SelectedWindow()->target_bounds());
477   selection_widget_->SetOpacity(kWindowOverviewSelectorOpacity);
478 }
479
480 }  // namespace ash