Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(588)

Side by Side Diff: ash/wm/overview/window_grid.cc

Issue 251103005: Added arrow key navigation to Overview Mode (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Simplified RemoveWindow() Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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/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/window_selector.h"
12 #include "ash/wm/overview/window_selector_item.h"
13 #include "base/memory/scoped_vector.h"
14 #include "third_party/skia/include/core/SkColor.h"
15 #include "ui/aura/window.h"
16 #include "ui/compositor/layer_animation_observer.h"
17 #include "ui/compositor/scoped_layer_animation_settings.h"
18 #include "ui/gfx/vector2d.h"
19 #include "ui/views/background.h"
20 #include "ui/views/view.h"
21 #include "ui/views/widget/widget.h"
22 #include "ui/wm/core/window_animations.h"
23
24 namespace ash {
25 namespace {
26
27 // An observer which holds onto the passed widget until the animation is
28 // complete.
29 class CleanupWidgetAfterAnimationObserver : public ui::LayerAnimationObserver {
30 public:
31 explicit CleanupWidgetAfterAnimationObserver(
32 scoped_ptr<views::Widget> widget);
33
34 // ui::LayerAnimationObserver:
35 virtual void OnLayerAnimationEnded(
36 ui::LayerAnimationSequence* sequence) OVERRIDE;
37 virtual void OnLayerAnimationAborted(
38 ui::LayerAnimationSequence* sequence) OVERRIDE;
39 virtual void OnLayerAnimationScheduled(
40 ui::LayerAnimationSequence* sequence) OVERRIDE;
41
42 private:
43 virtual ~CleanupWidgetAfterAnimationObserver();
44
45 scoped_ptr<views::Widget> widget_;
46
47 DISALLOW_COPY_AND_ASSIGN(CleanupWidgetAfterAnimationObserver);
48 };
49
50 CleanupWidgetAfterAnimationObserver::CleanupWidgetAfterAnimationObserver(
51 scoped_ptr<views::Widget> widget)
52 : widget_(widget.Pass()) {
53 widget_->GetNativeWindow()->layer()->GetAnimator()->AddObserver(this);
54 }
55
56 CleanupWidgetAfterAnimationObserver::~CleanupWidgetAfterAnimationObserver() {
57 widget_->GetNativeWindow()->layer()->GetAnimator()->RemoveObserver(this);
58 }
59
60 void CleanupWidgetAfterAnimationObserver::OnLayerAnimationEnded(
61 ui::LayerAnimationSequence* sequence) {
62 delete this;
63 }
64
65 void CleanupWidgetAfterAnimationObserver::OnLayerAnimationAborted(
66 ui::LayerAnimationSequence* sequence) {
67 delete this;
68 }
69
70 void CleanupWidgetAfterAnimationObserver::OnLayerAnimationScheduled(
71 ui::LayerAnimationSequence* sequence) {
72 }
73
74 // A comparator for locating a given target window.
75 struct WindowSelectorItemComparator
76 : public std::unary_function<WindowSelectorItem*, bool> {
77 explicit WindowSelectorItemComparator(const aura::Window* target_window)
78 : target(target_window) {
79 }
80
81 bool operator()(WindowSelectorItem* window) const {
82 return window->HasSelectableWindow(target);
83 }
84
85 const aura::Window* target;
86 };
87
88 // Conceptually the window overview is a table or grid of cells having this
89 // fixed aspect ratio. The number of columns is determined by maximizing the
90 // area of them based on the number of window_list.
91 const float kCardAspectRatio = 4.0f / 3.0f;
92
93 // The minimum number of cards along the major axis (i.e. horizontally on a
94 // landscape orientation).
95 const int kMinCardsMajor = 3;
96
97 const int kOverviewSelectorTransitionMilliseconds = 100;
98
99 // The color and opacity of the overview selector.
100 const SkColor kWindowOverviewSelectionColor = SK_ColorBLACK;
101 const unsigned char kWindowOverviewSelectorOpacity = 128;
102
103 // Returns the vector for the fade in animation.
104 gfx::Vector2d GetSlideVectorForFadeIn(WindowSelector::Direction direction,
105 const gfx::Rect& bounds) {
flackr 2014/06/03 17:12:07 nit: align.
Nina 2014/06/04 20:21:13 Done.
106 gfx::Vector2d vector;
107 switch (direction) {
108 case WindowSelector::DOWN:
109 vector.set_y(bounds.width());
110 break;
111 case WindowSelector::RIGHT:
112 vector.set_x(bounds.height());
113 break;
114 case WindowSelector::UP:
115 vector.set_y(-bounds.width());
116 break;
117 case WindowSelector::LEFT:
118 vector.set_x(-bounds.height());
119 break;
120 }
121 return vector;
122 }
123
124 } // namespace
125
126 WindowGrid::WindowGrid(aura::Window* root_window,
127 const std::vector<WindowSelectorItem*>& windows)
128 : root_window_(root_window) {
flackr 2014/06/03 17:12:07 Sorry, I mean this should have only 4 spaces in fr
Nina 2014/06/04 20:21:13 Done.
129 for (std::vector<WindowSelectorItem*>::const_iterator iter = windows.begin();
130 iter != windows.end(); ++iter) {
131 if ((*iter)->GetRootWindow() == root_window)
132 window_list_.push_back(*iter);
133 }
134 DCHECK(!window_list_.empty());
135 PositionWindows(true);
136 }
137
138 WindowGrid::~WindowGrid() {
139 }
140
141 void WindowGrid::RemoveWindow(aura::Window* window) {
142 ScopedVector<WindowSelectorItem>::iterator iter =
143 std::find_if(window_list_.begin(), window_list_.end(),
144 WindowSelectorItemComparator(window));
145 if (iter == window_list_.end())
146 return;
147
148 WindowSelectorItem* removed_item = *iter;
149 removed_item->RemoveWindow(window);
150
151 // If there are still windows in this selector entry then the overview is
152 // still active and the active selection remains the same.
153 if (!removed_item->empty())
154 return;
155
156 // We need to get this value before we erase the window to prevent unspecified
157 // behavior.
158 size_t removed_index = iter - window_list_.begin();
159 window_list_.erase(iter);
160 if (!empty()) {
flackr 2014/06/03 17:12:07 How about early return here: if (empty()) return
Nina 2014/06/04 20:21:13 Done.
161 PositionWindows(true);
162
163 // If existing, reposition the selection widget.
164 if (selection_widget_) {
165 if (selected_index_ >= removed_index && selected_index_ != 0)
166 selected_index_--;
167 ui::ScopedLayerAnimationSettings animation_settings(
168 selection_widget_->GetNativeWindow()->layer()->GetAnimator());
169 animation_settings.SetTransitionDuration(
170 base::TimeDelta::FromMilliseconds(
171 ScopedTransformOverviewWindow::kTransitionMilliseconds));
172 animation_settings.SetPreemptionStrategy(
173 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
174 selection_widget_->SetBounds(SelectedWindow()->target_bounds());
175 }
176 }
177 }
178
179 void WindowGrid::PositionWindows(bool animate) {
180 CHECK(!window_list_.empty());
181
182 gfx::Size window_size;
183 gfx::Rect total_bounds = ScreenUtil::ConvertRectToScreen(
184 root_window_,
185 ScreenUtil::GetDisplayWorkAreaBoundsInParent(
186 Shell::GetContainer(root_window_, kShellWindowId_DefaultContainer)));
187
188 // Find the minimum number of window_list per row that will fit all of the
189 // window_list on screen.
190 num_columns_ = std::max(
191 total_bounds.width() > total_bounds.height() ? kMinCardsMajor : 1,
192 static_cast<int>(ceil(sqrt(total_bounds.width() * window_list_.size() /
193 (kCardAspectRatio * total_bounds.height())))));
194 int num_rows = ((window_list_.size() + num_columns_ - 1) / num_columns_);
195 window_size.set_width(std::min(
196 static_cast<int>(total_bounds.width() / num_columns_),
197 static_cast<int>(total_bounds.height() * kCardAspectRatio / num_rows)));
198 window_size.set_height(window_size.width() / kCardAspectRatio);
199
200 // Calculate the X and Y offsets necessary to center the grid.
201 int x_offset = total_bounds.x() + ((window_list_.size() >= num_columns_ ? 0 :
202 (num_columns_ - window_list_.size()) * window_size.width()) +
203 (total_bounds.width() - num_columns_ * window_size.width())) / 2;
204 int y_offset = total_bounds.y() + (total_bounds.height() -
205 num_rows * window_size.height()) / 2;
206 for (size_t i = 0; i < window_list_.size(); ++i) {
207 gfx::Transform transform;
208 int column = i % num_columns_;
209 int row = i / num_columns_;
210 gfx::Rect target_bounds(window_size.width() * column + x_offset,
211 window_size.height() * row + y_offset,
212 window_size.width(),
213 window_size.height());
214 window_list_[i]->SetBounds(root_window_, target_bounds, animate);
215 }
216
217 // If we have less than |kMinCardsMajor| windows, adjust the column_ value to
218 // reflect how many "real" columns we have.
219 if (num_columns_ > window_list_.size())
220 num_columns_ = window_list_.size();
221 }
222
223 bool WindowGrid::Move(WindowSelector::Direction direction) {
224 bool out_of_bounds = MoveSelectedIndex(direction);
225 // If the selection widget is already active, fade it out in the selection
226 // direction.
227 if (selection_widget_) {
228 views::Widget* old_selection = selection_widget_.get();
229 gfx::Vector2d fade_out_direction =
230 GetSlideVectorForFadeIn(
231 direction, selection_widget_->GetNativeWindow()->bounds());
232
233 // CleanupWidgetAfterAnimationObserver will delete itself (and the
234 // widget) when the animation is complete.
235 new CleanupWidgetAfterAnimationObserver(selection_widget_.Pass());
236 ui::ScopedLayerAnimationSettings animation_settings(
237 old_selection->GetNativeWindow()->layer()->GetAnimator());
238 animation_settings.SetTransitionDuration(
239 base::TimeDelta::FromMilliseconds(
240 kOverviewSelectorTransitionMilliseconds));
241 animation_settings.SetPreemptionStrategy(
242 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
243 old_selection->SetOpacity(0);
244 old_selection->GetNativeWindow()->SetBounds(
245 old_selection->GetNativeWindow()->bounds() + fade_out_direction);
246 old_selection->Hide();
247 }
248 if (!out_of_bounds) {
249 // The newly selected window is in this grid, animate a new selection
250 // widget.
251 InitSelectionWidget(direction);
252 }
253
254 return out_of_bounds;
255 }
256
257 WindowSelectorItem* WindowGrid::SelectedWindow() const {
258 CHECK(selected_index_ < window_list_.size());
259 return window_list_[selected_index_];
260 }
261
262 void WindowGrid::InitSelectionWidget(WindowSelector::Direction direction) {
263 selection_widget_.reset(new views::Widget);
264 views::Widget::InitParams params;
265 params.type = views::Widget::InitParams::TYPE_POPUP;
266 params.keep_on_top = false;
267 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
268 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
269 params.parent = Shell::GetContainer(root_window_,
270 kShellWindowId_DefaultContainer);
271 params.accept_events = false;
272 selection_widget_->set_focus_on_creation(false);
273 selection_widget_->Init(params);
274 // Disable the "bounce in" animation when showing the window.
275 wm::SetWindowVisibilityAnimationTransition(
276 selection_widget_->GetNativeWindow(), wm::ANIMATE_NONE);
277 views::View* content_view = new views::View;
278 content_view->set_background(
279 views::Background::CreateSolidBackground(kWindowOverviewSelectionColor));
280 selection_widget_->SetContentsView(content_view);
281 selection_widget_->GetNativeWindow()->parent()->StackChildAtBottom(
282 selection_widget_->GetNativeWindow());
283
284 const gfx::Rect target_bounds = SelectedWindow()->target_bounds();
285 gfx::Vector2d fade_out_direction =
286 GetSlideVectorForFadeIn(direction, target_bounds);
287 gfx::Display dst_display = gfx::Screen::GetScreenFor(root_window_)->
288 GetDisplayMatching(target_bounds);
289
290 selection_widget_->Show();
291 // New selection widget starts with 0 opacity and fades in.
292 selection_widget_->GetNativeWindow()->layer()->SetOpacity(0);
293 selection_widget_->GetNativeWindow()->SetBoundsInScreen(
294 target_bounds - fade_out_direction, dst_display);
295 ui::ScopedLayerAnimationSettings animation_settings(
296 selection_widget_->GetNativeWindow()->layer()->GetAnimator());
297 animation_settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
298 kOverviewSelectorTransitionMilliseconds));
299 animation_settings.SetPreemptionStrategy(
300 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
301 selection_widget_->SetBounds(target_bounds);
302 selection_widget_->SetOpacity(kWindowOverviewSelectorOpacity);
303
304 // Send an a11y alert so that if ChromeVox is enabled, the item label is read.
305 SelectedWindow()->SendFocusAlert();
306 }
307
308 bool WindowGrid::MoveSelectedIndex(WindowSelector::Direction direction) {
309 if (selection_widget_) {
310 switch (direction) {
311 case WindowSelector::RIGHT:
312 if (selected_index_ >= window_list_.size() - 1)
313 return true;
314 else
flackr 2014/06/03 17:12:07 No need for else after early return, here and belo
Nina 2014/06/04 20:21:13 Done.
315 selected_index_++;
316 break;
317 case WindowSelector::LEFT:
318 if (selected_index_ == 0)
319 return true;
320 else
321 selected_index_--;
322 break;
323 case WindowSelector::DOWN:
324 if (selected_index_ + num_columns_ >= window_list_.size())
325 selected_index_ = (selected_index_ + 1) % num_columns_;
326 else
327 selected_index_ += num_columns_;
flackr 2014/06/03 17:12:07 how about just always add num_columns, and if >= w
Nina 2014/06/04 20:21:13 Done.
328 if (selected_index_ == 0 || selected_index_ >= window_list_.size())
flackr 2014/06/03 17:12:07 The latter part of this condition isn't possible a
Nina 2014/06/04 20:21:13 Done.
329 return true;
330 break;
331 case WindowSelector::UP:
332 if (selected_index_ == 0) {
333 return true;
334 } else {
335 if (selected_index_ < num_columns_) {
336 selected_index_--;
337 if (!(selected_index_ + num_columns_ >= window_list_.size())) {
flackr 2014/06/03 17:12:07 This looks like it will not work if there's only o
Nina 2014/06/04 20:21:13 But it will. If there is only one row, the only th
flackr 2014/06/04 22:25:06 Ah sorry, I was confused by the decrement being se
Nina 2014/06/05 18:04:34 Oh I didn't realize it would also work that way fo
338 selected_index_ +=
339 num_columns_ * (window_list_.size() / num_columns_);
340 if (selected_index_ >= window_list_.size())
341 selected_index_ -= num_columns_;
342 }
343 } else {
344 selected_index_ -= num_columns_;
345 }
346 }
347 break;
348 }
349 } else {
350 switch (direction) {
351 case WindowSelector::LEFT:
352 selected_index_ = window_list_.size() - 1;
353 break;
354 case WindowSelector::UP:
355 selected_index_ =
356 (window_list_.size() / num_columns_) * num_columns_ - 1;
357 break;
358 case WindowSelector::RIGHT:
359 case WindowSelector::DOWN:
360 selected_index_ = 0;
361 break;
362 }
flackr 2014/06/03 17:12:07 Can you move this to the top of the method and use
Nina 2014/06/04 20:21:13 Done.
363 }
364 return false;
365 }
366
367 } // namespace ash
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698