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

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: Starting again from design doc 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/window_selector.h"
11 #include "ash/wm/overview/window_selector_item.h"
12 #include "base/memory/scoped_vector.h"
13 #include "third_party/skia/include/core/SkColor.h"
14 #include "ui/aura/window.h"
15 #include "ui/compositor/layer_animation_observer.h"
16 #include "ui/compositor/scoped_layer_animation_settings.h"
17 #include "ui/gfx/vector2d.h"
18 #include "ui/views/background.h"
19 #include "ui/views/view.h"
20 #include "ui/views/widget/widget.h"
21
22 namespace ash {
23 namespace {
24
25 // An observer which holds onto the passed widget until the animation is
26 // complete.
27 class CleanupWidgetAfterAnimationObserver : public ui::LayerAnimationObserver {
28 public:
29 explicit CleanupWidgetAfterAnimationObserver(
30 scoped_ptr<views::Widget> widget);
31
32 // ui::LayerAnimationObserver:
33 virtual void OnLayerAnimationEnded(
34 ui::LayerAnimationSequence* sequence) OVERRIDE;
35 virtual void OnLayerAnimationAborted(
36 ui::LayerAnimationSequence* sequence) OVERRIDE;
37 virtual void OnLayerAnimationScheduled(
38 ui::LayerAnimationSequence* sequence) OVERRIDE;
39
40 private:
41 virtual ~CleanupWidgetAfterAnimationObserver();
42
43 scoped_ptr<views::Widget> widget_;
44
45 DISALLOW_COPY_AND_ASSIGN(CleanupWidgetAfterAnimationObserver);
46 };
47
48 CleanupWidgetAfterAnimationObserver::CleanupWidgetAfterAnimationObserver(
49 scoped_ptr<views::Widget> widget)
50 : widget_(widget.Pass()) {
51 widget_->GetNativeWindow()->layer()->GetAnimator()->AddObserver(this);
52 }
53
54 CleanupWidgetAfterAnimationObserver::~CleanupWidgetAfterAnimationObserver() {
55 widget_->GetNativeWindow()->layer()->GetAnimator()->RemoveObserver(this);
56 }
57
58 void CleanupWidgetAfterAnimationObserver::OnLayerAnimationEnded(
59 ui::LayerAnimationSequence* sequence) {
60 delete this;
61 }
62
63 void CleanupWidgetAfterAnimationObserver::OnLayerAnimationAborted(
64 ui::LayerAnimationSequence* sequence) {
65 delete this;
66 }
67
68 void CleanupWidgetAfterAnimationObserver::OnLayerAnimationScheduled(
69 ui::LayerAnimationSequence* sequence) {
70 }
71
72 // A comparator for locating a given target window.
73 struct WindowSelectorItemComparator
74 : public std::unary_function<WindowSelectorItem*, bool> {
75 explicit WindowSelectorItemComparator(const aura::Window* target_window)
76 : target(target_window) {
77 }
78
79 bool operator()(WindowSelectorItem* window) const {
80 return window->HasSelectableWindow(target);
81 }
82
83 const aura::Window* target;
84 };
85
86 // Conceptually the window overview is a table or grid of cells having this
87 // fixed aspect ratio. The number of columns is determined by maximizing the
88 // area of them based on the number of window_list.
89 const float kCardAspectRatio = 4.0f / 3.0f;
90
91 // The minimum number of cards along the major axis (i.e. horizontally on a
92 // landscape orientation).
93 const int kMinCardsMajor = 3;
94
95 // In the conceptual overview table, the window margin is the space reserved
96 // around the window within the cell. This margin does not overlap so the
97 // closest distance between adjacent window_list will be twice this amount.
98 const int kWindowMargin = 30;
99
100 const int kOverviewSelectorTransitionMilliseconds = 500;
101
102 // The color and opacity of the overview selector.
103 const SkColor kWindowOverviewSelectionColor = SK_ColorBLACK;
104 const unsigned char kWindowOverviewSelectorOpacity = 128;
105
106 // Initializes the selection widget.
107 views::Widget* CreateSelectionWidget(aura::Window* root_window) {
108 views::Widget* selection_widget_ = new views::Widget;
109 views::Widget::InitParams params;
110 params.type = views::Widget::InitParams::TYPE_POPUP;
111 params.keep_on_top = false;
112 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
113 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
114 params.parent = Shell::GetContainer(root_window,
115 kShellWindowId_DefaultContainer);
116 params.accept_events = false;
117 selection_widget_->set_focus_on_creation(false);
118 selection_widget_->Init(params);
119 views::View* content_view = new views::View;
120 content_view->set_background(
121 views::Background::CreateSolidBackground(kWindowOverviewSelectionColor));
122 selection_widget_->SetContentsView(content_view);
123 selection_widget_->GetNativeWindow()->parent()->StackChildAtBottom(
124 selection_widget_->GetNativeWindow());
125
126 return selection_widget_;
127 }
128
129 // Returns the vector for the fade in animation.
130 gfx::Vector2d GetVectorFor(WindowSelector::Direction direction,
131 gfx::Rect bounds) {
132 gfx::Vector2d vector;
133 switch (direction) {
134 case WindowSelector::DOWN:
135 vector.set_y(bounds.width());
136 break;
137 case WindowSelector::RIGHT:
138 vector.set_x(bounds.height());
139 break;
140 case WindowSelector::UP:
141 vector.set_y(-bounds.width());
142 break;
143 case WindowSelector::LEFT:
144 vector.set_x(-bounds.height());
145 break;
146 }
147 return vector;
148 }
149
150 } // namespace
151
152 WindowGrid::WindowGrid(aura::Window* root_window,
153 std::vector<WindowSelectorItem*> window_list)
154 : root_window_(root_window),
155 window_list_(window_list),
156 selected_index_(0) {
157 PositionWindows(true);
158 }
159
160 WindowGrid::~WindowGrid() {
161 }
162
163 WindowSelectorItem* WindowGrid::RemoveWindow(aura::Window* window) {
164 ScopedVector<WindowSelectorItem>::iterator iter =
165 std::find_if(window_list_.begin(), window_list_.end(),
166 WindowSelectorItemComparator(window));
167 if (iter == window_list_.end())
168 return NULL;
169
170 WindowSelectorItem* removed_item = *iter;
171
172 removed_item->RemoveWindow(window);
173
174 // If there are still windows in this selector entry then the overview is
175 // still active and the active selection remains the same.
176 if (!removed_item->empty())
177 return NULL;
178
179 // We need to get this value before we erase the window to prevent unspecified
180 // behavior.
181 size_t removed_index = iter - window_list_.begin();
182
183 window_list_.erase(iter);
184
185 if (!empty()) {
186 PositionWindows(true);
187
188 // If existing, reposition the selection widget.
189 if (selection_widget_) {
190 if (selected_index_ >= removed_index) {
191 if (selected_index_ != 0) {
192 selected_index_--;
193 }
194 }
195 selection_widget_->SetBounds(GetSelectionBounds());
196 }
197 }
198
199 return removed_item;
200 }
201
202 void WindowGrid::PositionWindows(bool animate) {
203 CHECK(!window_list_.empty());
204
205 gfx::Size window_size;
206 gfx::Rect total_bounds = ScreenUtil::ConvertRectToScreen(
207 root_window_,
208 ScreenUtil::GetDisplayWorkAreaBoundsInParent(
209 Shell::GetContainer(root_window_, kShellWindowId_DefaultContainer)));
210
211 // Find the minimum number of window_list per row that will fit all of the
212 // window_list on screen.
213 columns_ = std::max(
214 total_bounds.width() > total_bounds.height() ? kMinCardsMajor : 1,
215 static_cast<int>(ceil(sqrt(total_bounds.width() * window_list_.size() /
216 (kCardAspectRatio * total_bounds.height())))));
217 rows_ = ((window_list_.size() + columns_ - 1) / columns_);
218 window_size.set_width(std::min(
219 static_cast<int>(total_bounds.width() / columns_),
220 static_cast<int>(total_bounds.height() * kCardAspectRatio / rows_)));
221 window_size.set_height(window_size.width() / kCardAspectRatio);
222
223 // Calculate the X and Y offsets necessary to center the grid.
224 int x_offset = total_bounds.x() + ((window_list_.size() >= columns_ ? 0 :
225 (columns_ - window_list_.size()) * window_size.width()) +
226 (total_bounds.width() - columns_ * window_size.width())) / 2;
227 int y_offset = total_bounds.y() + (total_bounds.height() -
228 rows_ * window_size.height()) / 2;
229 for (size_t i = 0; i < window_list_.size(); ++i) {
230 gfx::Transform transform;
231 int column = i % columns_;
232 int row = i / columns_;
233 gfx::Rect target_bounds(window_size.width() * column + x_offset,
234 window_size.height() * row + y_offset,
235 window_size.width(),
236 window_size.height());
237 target_bounds.Inset(kWindowMargin, kWindowMargin);
238 window_list_[i]->SetBounds(root_window_, target_bounds, animate);
239 }
240
241 // If we have less than |kMinCardsMajor| windows, adjust the column_ value to
242 // reflect how many "real" columns we have.
243 if (columns_ > window_list_.size())
244 columns_ = window_list_.size();
245 }
246
247 bool WindowGrid::Move(WindowSelector::Direction direction) {
248 bool out_of_bounds = MoveSelectedIndex(direction);
249
250 if (selection_widget_) {
251 gfx::Vector2d fade_out_direction =
252 GetVectorFor(direction, selection_widget_->GetNativeWindow()->bounds());
253 // If the selection widget is already active, fade it out in the selection
254 // direction.
255 views::Widget* old_selection = selection_widget_.get();
256
257 // CleanupWidgetAfterAnimationObserver will delete itself (and the
258 // widget) when the animation is complete.
259 {
260 new CleanupWidgetAfterAnimationObserver(selection_widget_.Pass());
261 ui::ScopedLayerAnimationSettings animation_settings(
262 old_selection->GetNativeWindow()->layer()->GetAnimator());
263 animation_settings.SetTransitionDuration(
264 base::TimeDelta::FromMilliseconds(
265 kOverviewSelectorTransitionMilliseconds));
266 animation_settings.SetPreemptionStrategy(
267 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
268 old_selection->SetOpacity(0);
269 old_selection->GetNativeWindow()->SetBounds(
270 old_selection->GetNativeWindow()->bounds() + fade_out_direction);
271 old_selection->Hide();
272 }
273 }
274 if (!out_of_bounds) {
275 // The selected window is in this grid, animate a new selection widget.
276 const gfx::Rect target_bounds = GetSelectionBounds();
277 gfx::Vector2d fade_out_direction =
278 GetVectorFor(direction, target_bounds);
279 gfx::Display dst_display = gfx::Screen::GetScreenFor(
280 root_window_)->GetDisplayMatching(target_bounds);
281
282 selection_widget_.reset(CreateSelectionWidget(root_window_));
283 selection_widget_->Show();
284 // New selection widget starts with 0 opacity and fades in.
285 selection_widget_->GetNativeWindow()->layer()->SetOpacity(0);
286 selection_widget_->GetNativeWindow()->SetBoundsInScreen(
287 target_bounds - fade_out_direction, dst_display);
288 ui::ScopedLayerAnimationSettings animation_settings(
289 selection_widget_->GetNativeWindow()->layer()->GetAnimator());
290 animation_settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
291 kOverviewSelectorTransitionMilliseconds));
292 animation_settings.SetPreemptionStrategy(
293 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
294 selection_widget_->SetBounds(target_bounds);
295 selection_widget_->SetOpacity(kWindowOverviewSelectorOpacity);
296 SelectedWindow()->window_label()->GetContentsView()->
297 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true);
298 } else {
299 selection_widget_.reset();
300 selected_index_ = 0;
301 }
302 return out_of_bounds;
303 }
304
305 WindowSelectorItem* WindowGrid::SelectedWindow() const {
306 CHECK(selected_index_ < window_list_.size());
307 return window_list_[selected_index_];
308 }
309
310 bool WindowGrid::MoveSelectedIndex(WindowSelector::Direction direction) {
311 bool out_of_bounds = false;
312 // If the selection widget is active, update the index.
313 if (selection_widget_) {
314 switch (direction) {
315 case WindowSelector::RIGHT:
316 if (selected_index_ >= window_list_.size() - 1)
317 out_of_bounds = true;
318 else
319 selected_index_++;
320 break;
321 case WindowSelector::LEFT:
322 if (selected_index_ == 0)
323 out_of_bounds = true;
324 else
325 selected_index_--;
326 break;
327 case WindowSelector::DOWN:
328 if (selected_index_ + columns_ >= window_list_.size())
329 selected_index_ = (selected_index_ + 1) % columns_;
330 else
331 selected_index_ += columns_;
332 if (selected_index_ == 0 || selected_index_ >= window_list_.size())
333 out_of_bounds = true;
334 break;
335 case WindowSelector::UP:
336 if (selected_index_ == 0) {
337 out_of_bounds = true;
338 } else {
339 if (selected_index_ < columns_) {
340 selected_index_--;
341 if (!(selected_index_ + columns_ >= window_list_.size())) {
342 selected_index_ += columns_ * (rows_ - 1);
343 if (selected_index_ >= window_list_.size())
344 selected_index_ -= columns_;
345 }
346 } else {
347 selected_index_ -= columns_;
348 }
349 }
350 break;
351 }
352 } else {
353 // If the selection widget is not active, position the index in the "first"
354 // window relative to the chosen direction.
355 if (direction == WindowSelector::LEFT)
356 selected_index_ = window_list_.size() - 1;
357 if (direction == WindowSelector::UP) {
358 selected_index_ = rows_ * columns_ - 1;
359 if (selected_index_ >= window_list_.size())
360 selected_index_ -= columns_;
361 }
362 }
363 return out_of_bounds;
364 }
365
366 const gfx::Rect WindowGrid::GetSelectionBounds() const {
367 gfx::Rect bounds = SelectedWindow()->target_bounds();
368 bounds.Inset(-kWindowMargin, -kWindowMargin);
369 return bounds;
370 }
371
372 } // namespace ash
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698