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

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: Fixed a bug with panels and changing windows bounds 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 "ash/wm/overview/window_selector_panels.h"
14 #include "ash/wm/overview/window_selector_window.h"
15 #include "ash/wm/window_state.h"
16 #include "base/memory/scoped_vector.h"
17 #include "third_party/skia/include/core/SkColor.h"
18 #include "ui/aura/window.h"
19 #include "ui/compositor/layer_animation_observer.h"
20 #include "ui/compositor/scoped_layer_animation_settings.h"
21 #include "ui/gfx/vector2d.h"
22 #include "ui/views/background.h"
23 #include "ui/views/view.h"
24 #include "ui/views/widget/widget.h"
25 #include "ui/wm/core/window_animations.h"
26
27 namespace ash {
28 namespace {
29
30 // An observer which holds onto the passed widget until the animation is
31 // complete.
32 class CleanupWidgetAfterAnimationObserver
33 : public ui::ImplicitAnimationObserver {
34 public:
35 explicit CleanupWidgetAfterAnimationObserver(
36 scoped_ptr<views::Widget> widget);
37 virtual ~CleanupWidgetAfterAnimationObserver();
38
39 // ui::ImplicitAnimationObserver:
40 virtual void OnImplicitAnimationsCompleted() OVERRIDE;
41
42 // ui::LayerAnimationObserver:
43 virtual void OnLayerAnimationEnded(
44 ui::LayerAnimationSequence* sequence) OVERRIDE;
45 virtual void OnLayerAnimationAborted(
46 ui::LayerAnimationSequence* sequence) OVERRIDE;
47 virtual void OnLayerAnimationScheduled(
48 ui::LayerAnimationSequence* sequence) OVERRIDE;
flackr 2014/06/04 22:25:06 The ui::LayerAnimationObserver methods should not
Nina 2014/06/05 18:04:35 Done.
49
50 private:
51 scoped_ptr<views::Widget> widget_;
52
53 DISALLOW_COPY_AND_ASSIGN(CleanupWidgetAfterAnimationObserver);
54 };
55
56 CleanupWidgetAfterAnimationObserver::CleanupWidgetAfterAnimationObserver(
57 scoped_ptr<views::Widget> widget)
58 : widget_(widget.Pass()) {
59 }
60
61 CleanupWidgetAfterAnimationObserver::~CleanupWidgetAfterAnimationObserver() {
62 }
63
64 void CleanupWidgetAfterAnimationObserver::OnImplicitAnimationsCompleted() {
65 delete this;
66 }
67
68 void CleanupWidgetAfterAnimationObserver::OnLayerAnimationEnded(
69 ui::LayerAnimationSequence* sequence) {
70 }
71 void CleanupWidgetAfterAnimationObserver::OnLayerAnimationAborted(
72 ui::LayerAnimationSequence* sequence) {
73 }
74 void CleanupWidgetAfterAnimationObserver::OnLayerAnimationScheduled(
75 ui::LayerAnimationSequence* sequence) {
76 }
77
78 // A comparator for locating a given target window.
79 struct WindowSelectorItemComparator
80 : public std::unary_function<WindowSelectorItem*, bool> {
81 explicit WindowSelectorItemComparator(const aura::Window* target_window)
82 : target(target_window) {
83 }
84
85 bool operator()(WindowSelectorItem* window) const {
86 return window->HasSelectableWindow(target);
87 }
88
89 const aura::Window* target;
90 };
91
92 // A comparator for locating a selectable window given a targeted window.
flackr 2014/06/04 22:25:06 nit: For locating a WindowSelectorItem
Nina 2014/06/05 18:04:35 Done.
93 struct WindowSelectorItemTargetComparator
94 : public std::unary_function<WindowSelectorItem*, bool> {
95 explicit WindowSelectorItemTargetComparator(const aura::Window* target_window)
96 : target(target_window) {
97 }
98
99 bool operator()(WindowSelectorItem* window) const {
100 return window->Contains(target);
101 }
102
103 const aura::Window* target;
104 };
105
106 // Conceptually the window overview is a table or grid of cells having this
107 // fixed aspect ratio. The number of columns is determined by maximizing the
108 // area of them based on the number of window_list.
109 const float kCardAspectRatio = 4.0f / 3.0f;
110
111 // The minimum number of cards along the major axis (i.e. horizontally on a
112 // landscape orientation).
113 const int kMinCardsMajor = 3;
114
115 const int kOverviewSelectorTransitionMilliseconds = 100;
116
117 // The color and opacity of the overview selector.
118 const SkColor kWindowOverviewSelectionColor = SK_ColorBLACK;
119 const unsigned char kWindowOverviewSelectorOpacity = 128;
120
121 // Returns the vector for the fade in animation.
122 gfx::Vector2d GetSlideVectorForFadeIn(WindowSelector::Direction direction,
123 const gfx::Rect& bounds) {
124 gfx::Vector2d vector;
125 switch (direction) {
126 case WindowSelector::DOWN:
127 vector.set_y(bounds.width());
128 break;
129 case WindowSelector::RIGHT:
130 vector.set_x(bounds.height());
131 break;
132 case WindowSelector::UP:
133 vector.set_y(-bounds.width());
134 break;
135 case WindowSelector::LEFT:
136 vector.set_x(-bounds.height());
137 break;
138 }
139 return vector;
140 }
141
142 } // namespace
143
144 WindowGrid::WindowGrid(aura::Window* root_window,
145 const std::vector<aura::Window*>& windows,
146 WindowSelector* window_selector)
147 : root_window_(root_window),
148 window_selector_(window_selector) {
149 WindowSelectorPanels* panels_item = NULL;
150 for (aura::Window::Windows::const_iterator iter = windows.begin();
151 iter != windows.end(); ++iter) {
152 if ((*iter)->GetRootWindow() != root_window)
153 continue;
154 (*iter)->AddObserver(this);
155 observed_windows_.push_back(*iter);
156 WindowSelectorItem* item = NULL;
157
158 if ((*iter)->type() == ui::wm::WINDOW_TYPE_PANEL &&
159 wm::GetWindowState(*iter)->panel_attached()) {
160 // Attached panel windows are grouped into a single overview item per
161 // grid.
162 if (!panels_item) {
163 panels_item = new WindowSelectorPanels();
164 window_list_.push_back(panels_item);
165 }
166 panels_item->AddWindow(*iter);
167 item = panels_item;
168 } else {
169 item = new WindowSelectorWindow(*iter);
170 window_list_.push_back(item);
171 }
172 // Verify that the window has been added to an item in overview.
173 CHECK(item->Contains(*iter));
flackr 2014/06/04 22:25:06 I think this check is no longer necessary.
Nina 2014/06/05 18:04:35 Done.
174 }
175 if (window_list_.empty())
176 return;
177
178 for (ScopedVector<WindowSelectorItem>::iterator iter = window_list_.begin();
179 iter != window_list_.end(); iter++) {
180 (*iter)->PrepareForOverview();
flackr 2014/06/04 22:25:06 I forget, but is there any reason we can't do this
Nina 2014/06/05 18:04:35 Well I did it for the panels, but now that we only
181 }
182
183 PositionWindows(true);
184 }
185
186 WindowGrid::~WindowGrid() {
187 for (std::vector<aura::Window*>::iterator iter = observed_windows_.begin();
188 iter != observed_windows_.end(); iter++) {
189 (*iter)->RemoveObserver(this);
190 }
191 }
192
193 void WindowGrid::PositionWindows(bool animate) {
194 CHECK(!window_list_.empty());
195
196 gfx::Size window_size;
197 gfx::Rect total_bounds = ScreenUtil::ConvertRectToScreen(
198 root_window_,
199 ScreenUtil::GetDisplayWorkAreaBoundsInParent(
200 Shell::GetContainer(root_window_, kShellWindowId_DefaultContainer)));
201
202 // Find the minimum number of window_list per row that will fit all of the
203 // window_list on screen.
204 num_columns_ = std::max(
205 total_bounds.width() > total_bounds.height() ? kMinCardsMajor : 1,
206 static_cast<int>(ceil(sqrt(total_bounds.width() * window_list_.size() /
207 (kCardAspectRatio * total_bounds.height())))));
208 int num_rows = ((window_list_.size() + num_columns_ - 1) / num_columns_);
209 window_size.set_width(std::min(
210 static_cast<int>(total_bounds.width() / num_columns_),
211 static_cast<int>(total_bounds.height() * kCardAspectRatio / num_rows)));
212 window_size.set_height(window_size.width() / kCardAspectRatio);
213
214 // Calculate the X and Y offsets necessary to center the grid.
215 int x_offset = total_bounds.x() + ((window_list_.size() >= num_columns_ ? 0 :
216 (num_columns_ - window_list_.size()) * window_size.width()) +
217 (total_bounds.width() - num_columns_ * window_size.width())) / 2;
218 int y_offset = total_bounds.y() + (total_bounds.height() -
219 num_rows * window_size.height()) / 2;
220 for (size_t i = 0; i < window_list_.size(); ++i) {
221 gfx::Transform transform;
222 int column = i % num_columns_;
223 int row = i / num_columns_;
224 gfx::Rect target_bounds(window_size.width() * column + x_offset,
225 window_size.height() * row + y_offset,
226 window_size.width(),
227 window_size.height());
228 window_list_[i]->SetBounds(root_window_, target_bounds, animate);
229 }
230
231 // If we have less than |kMinCardsMajor| windows, adjust the column_ value to
232 // reflect how many "real" columns we have.
233 if (num_columns_ > window_list_.size())
234 num_columns_ = window_list_.size();
235 }
236
237 bool WindowGrid::Move(WindowSelector::Direction direction) {
238 bool recreate_selection_widget = false;
239 bool out_of_bounds = MoveSelectedIndex(direction, &recreate_selection_widget);
flackr 2014/06/04 22:25:06 Rather than passing recreate_selection_widget in t
Nina 2014/06/05 18:04:35 While I preferred the other approach, this allows
240 // If the selection widget is already active, fade it out in the selection
241 // direction.
242 if (selection_widget_) {
243 if (recreate_selection_widget || out_of_bounds) {
244 // Animate the old selection widget and then destroy it.
245 views::Widget* old_selection = selection_widget_.get();
246 gfx::Vector2d fade_out_direction =
247 GetSlideVectorForFadeIn(
248 direction, old_selection->GetNativeWindow()->bounds());
249
250 ui::ScopedLayerAnimationSettings animation_settings(
251 old_selection->GetNativeWindow()->layer()->GetAnimator());
252 animation_settings.SetTransitionDuration(
253 base::TimeDelta::FromMilliseconds(
254 kOverviewSelectorTransitionMilliseconds));
255 animation_settings.SetPreemptionStrategy(
256 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
257 // CleanupWidgetAfterAnimationObserver will delete itself (and the
258 // widget) when the movement animation is complete.
259 animation_settings.AddObserver(
260 new CleanupWidgetAfterAnimationObserver(selection_widget_.Pass()));
261 old_selection->SetOpacity(0);
262 old_selection->GetNativeWindow()->SetBounds(
263 old_selection->GetNativeWindow()->bounds() + fade_out_direction);
264 old_selection->Hide();
265 if (!out_of_bounds)
266 InitSelectionWidget(direction);
267 }
268 } else {
269 InitSelectionWidget(direction);
270 }
271 if (!out_of_bounds) {
272 // Send an a11y alert so that if ChromeVox is enabled, the item label is
273 // read.
274 SelectedWindow()->SendFocusAlert();
275
276 // The selection widget is moved to the newly selected item in the same
277 // grid.
278 ui::ScopedLayerAnimationSettings animation_settings(
279 selection_widget_->GetNativeWindow()->layer()->GetAnimator());
280 animation_settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
281 kOverviewSelectorTransitionMilliseconds));
282 animation_settings.SetPreemptionStrategy(
283 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
284 selection_widget_->SetBounds(SelectedWindow()->target_bounds());
285 selection_widget_->SetOpacity(kWindowOverviewSelectorOpacity);
286 }
287
288 return out_of_bounds;
289 }
290
291 WindowSelectorItem* WindowGrid::SelectedWindow() const {
292 CHECK(selected_index_ < window_list_.size());
293 return window_list_[selected_index_];
294 }
295
296 void WindowGrid::OnWindowDestroying(aura::Window* window) {
297 std::vector<aura::Window*>::iterator window_iter =
298 std::find(observed_windows_.begin(), observed_windows_.end(), window);
299 DCHECK(window_iter != observed_windows_.end());
300 window->RemoveObserver(this);
301 observed_windows_.erase(window_iter);
302 ScopedVector<WindowSelectorItem>::iterator iter =
303 std::find_if(window_list_.begin(), window_list_.end(),
304 WindowSelectorItemComparator(window));
305
306 DCHECK(iter != window_list_.end());
307
308 (*iter)->RemoveWindow(window);
309
310 // If there are still windows in this selector entry then the overview is
311 // still active and the active selection remains the same.
312 if (!(*iter)->empty())
313 return;
314
315 // We need to get this value before we erase the window to prevent unspecified
316 // behavior.
flackr 2014/06/04 22:25:06 I don't think this comment is necessary.
Nina 2014/06/05 18:04:35 Removed.
317 size_t removed_index = iter - window_list_.begin();
318 window_list_.erase(iter);
319
320 if (empty()) {
321 // If the grid is now empty, notify the window selector so that it erases us
322 // from its grid list.
323 window_selector_->OnGridEmpty(this);
324 return;
325 }
326
327 PositionWindows(true);
328
329 // If existing, reposition the selection widget.
330 if (selection_widget_) {
331 if (selected_index_ >= removed_index && selected_index_ != 0)
flackr 2014/06/04 22:25:06 If the selection changes it should cause another a
Nina 2014/06/05 18:04:35 Good point. Done.
332 selected_index_--;
333 ui::ScopedLayerAnimationSettings animation_settings(
334 selection_widget_->GetNativeWindow()->layer()->GetAnimator());
335 animation_settings.SetTransitionDuration(
336 base::TimeDelta::FromMilliseconds(
337 ScopedTransformOverviewWindow::kTransitionMilliseconds));
338 animation_settings.SetPreemptionStrategy(
339 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
340 selection_widget_->SetBounds(SelectedWindow()->target_bounds());
flackr 2014/06/04 22:25:06 Seems like setting up an animated move to the sele
Nina 2014/06/05 18:04:35 Done.
341 }
342 }
343
344 void WindowGrid::OnWindowBoundsChanged(aura::Window* window,
345 const gfx::Rect& old_bounds,
346 const gfx::Rect& new_bounds) {
347 ScopedVector<WindowSelectorItem>::const_iterator iter =
348 std::find_if(window_list_.begin(), window_list_.end(),
349 WindowSelectorItemTargetComparator(window));
350 DCHECK(iter != window_list_.end());
351
352 // Immediately finish any active bounds animation.
353 window->layer()->GetAnimator()->StopAnimatingProperty(
354 ui::LayerAnimationElement::BOUNDS);
355
356 // Recompute the transform for the window.
357 (*iter)->RecomputeWindowTransforms();
358 }
359
360 void WindowGrid::InitSelectionWidget(WindowSelector::Direction direction) {
361 selection_widget_.reset(new views::Widget);
362 views::Widget::InitParams params;
363 params.type = views::Widget::InitParams::TYPE_POPUP;
364 params.keep_on_top = false;
365 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
366 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
367 params.parent = Shell::GetContainer(root_window_,
368 kShellWindowId_DefaultContainer);
369 params.accept_events = false;
370 selection_widget_->set_focus_on_creation(false);
371 selection_widget_->Init(params);
372 // Disable the "bounce in" animation when showing the window.
373 ::wm::SetWindowVisibilityAnimationTransition(
flackr 2014/06/04 22:25:06 Why ::wm?
Nina 2014/06/05 18:04:35 It's not in namespace ash. I think it wouldn't com
374 selection_widget_->GetNativeWindow(), ::wm::ANIMATE_NONE);
375 views::View* content_view = new views::View;
376 content_view->set_background(
377 views::Background::CreateSolidBackground(kWindowOverviewSelectionColor));
378 selection_widget_->SetContentsView(content_view);
379 selection_widget_->GetNativeWindow()->parent()->StackChildAtBottom(
380 selection_widget_->GetNativeWindow());
381 // New selection widget starts with 0 opacity and then fades in.
382 selection_widget_->GetNativeWindow()->layer()->SetOpacity(0);
383 selection_widget_->Show();
flackr 2014/06/04 22:25:06 We actually don't allow calling show on a window w
Nina 2014/06/05 18:04:35 If by this you mean calling Show before setting th
384
385 const gfx::Rect target_bounds = SelectedWindow()->target_bounds();
386 gfx::Vector2d fade_out_direction =
387 GetSlideVectorForFadeIn(direction, target_bounds);
388 gfx::Display dst_display = gfx::Screen::GetScreenFor(root_window_)->
389 GetDisplayMatching(target_bounds);
390 selection_widget_->GetNativeWindow()->SetBoundsInScreen(
391 target_bounds - fade_out_direction, dst_display);
392 }
393
394 bool WindowGrid::MoveSelectedIndex(WindowSelector::Direction direction,
395 bool* recreate_selection_widget) {
396 if (!selection_widget_) {
397 switch (direction) {
398 case WindowSelector::LEFT:
399 selected_index_ = window_list_.size() - 1;
400 break;
401 case WindowSelector::UP:
402 selected_index_ =
403 (window_list_.size() / num_columns_) * num_columns_ - 1;
404 break;
405 case WindowSelector::RIGHT:
406 case WindowSelector::DOWN:
407 selected_index_ = 0;
408 break;
409 }
410 return false;
411 }
flackr 2014/06/04 22:25:06 nit: newline before main switch.
Nina 2014/06/05 18:04:35 Now that we have an else there, it might not be ne
412 switch (direction) {
413 case WindowSelector::RIGHT:
414 if (selected_index_ >= window_list_.size() - 1)
415 return true;
416 selected_index_++;
417 if (selected_index_ % num_columns_ == 0)
418 (*recreate_selection_widget) = true;
419 break;
420 case WindowSelector::LEFT:
421 if (selected_index_ == 0)
422 return true;
423 selected_index_--;
424 if ((selected_index_ + 1) % num_columns_ == 0)
425 (*recreate_selection_widget) = true;
426 break;
427 case WindowSelector::DOWN:
428 selected_index_ += num_columns_;
429 if (selected_index_ >= window_list_.size()) {
430 selected_index_ = (selected_index_ + 1) % num_columns_;
431 if (selected_index_ == 0)
432 return true;
433 (*recreate_selection_widget) = true;
434 }
435 break;
436 case WindowSelector::UP:
437 if (selected_index_ == 0)
438 return true;
439 if (selected_index_ < num_columns_) {
440 selected_index_--;
441 if (!(selected_index_ + num_columns_ >= window_list_.size())) {
442 selected_index_ +=
443 num_columns_ * (window_list_.size() / num_columns_);
flackr 2014/06/04 22:25:06 This should probably be num_columns_ * ((window_li
Nina 2014/06/05 18:04:35 I combined both this comment and the previous one
444 if (selected_index_ >= window_list_.size())
445 selected_index_ -= num_columns_;
446 }
447 (*recreate_selection_widget) = true;
448 } else {
449 selected_index_ -= num_columns_;
450 }
451 break;
452 }
453 return false;
454 }
455
456 } // namespace ash
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698