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

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

Issue 2734653002: chromeos: Move files in //ash/common to //ash (Closed)
Patch Set: fix a11y tests, fix docs Created 3 years, 9 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
« no previous file with comments | « ash/common/wm/overview/window_grid.h ('k') | ash/common/wm/overview/window_selector.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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/common/wm/overview/window_grid.h"
6
7 #include <algorithm>
8 #include <functional>
9 #include <set>
10 #include <utility>
11 #include <vector>
12
13 #include "ash/common/ash_switches.h"
14 #include "ash/common/shelf/wm_shelf.h"
15 #include "ash/common/wm/overview/cleanup_animation_observer.h"
16 #include "ash/common/wm/overview/scoped_overview_animation_settings.h"
17 #include "ash/common/wm/overview/scoped_overview_animation_settings_factory.h"
18 #include "ash/common/wm/overview/window_selector.h"
19 #include "ash/common/wm/overview/window_selector_delegate.h"
20 #include "ash/common/wm/overview/window_selector_item.h"
21 #include "ash/common/wm/window_state.h"
22 #include "ash/common/wm/wm_screen_util.h"
23 #include "ash/common/wm_window.h"
24 #include "ash/public/cpp/shelf_types.h"
25 #include "ash/public/cpp/shell_window_ids.h"
26 #include "ash/root_window_controller.h"
27 #include "ash/wm/window_state_aura.h"
28 #include "base/command_line.h"
29 #include "base/i18n/string_search.h"
30 #include "base/memory/ptr_util.h"
31 #include "base/strings/string_number_conversions.h"
32 #include "third_party/skia/include/core/SkColor.h"
33 #include "third_party/skia/include/pathops/SkPathOps.h"
34 #include "ui/compositor/layer_animation_observer.h"
35 #include "ui/compositor/scoped_layer_animation_settings.h"
36 #include "ui/gfx/animation/tween.h"
37 #include "ui/gfx/canvas.h"
38 #include "ui/gfx/geometry/safe_integer_conversions.h"
39 #include "ui/gfx/geometry/vector2d.h"
40 #include "ui/gfx/scoped_canvas.h"
41 #include "ui/views/background.h"
42 #include "ui/views/border.h"
43 #include "ui/views/painter.h"
44 #include "ui/views/view.h"
45 #include "ui/views/widget/widget.h"
46 #include "ui/wm/core/shadow.h"
47 #include "ui/wm/core/shadow_types.h"
48 #include "ui/wm/core/window_animations.h"
49
50 namespace ash {
51 namespace {
52
53 using Windows = std::vector<WmWindow*>;
54
55 // A comparator for locating a given target window.
56 struct WindowSelectorItemComparator {
57 explicit WindowSelectorItemComparator(const WmWindow* target_window)
58 : target(target_window) {}
59
60 bool operator()(std::unique_ptr<WindowSelectorItem>& window) const {
61 return window->GetWindow() == target;
62 }
63
64 const WmWindow* target;
65 };
66
67 // Time it takes for the selector widget to move to the next target. The same
68 // time is used for fading out shield widget when the overview mode is opened
69 // or closed.
70 const int kOverviewSelectorTransitionMilliseconds = 250;
71
72 // The color and opacity of the screen shield in overview.
73 const SkColor kShieldColor = SkColorSetARGB(255, 0, 0, 0);
74 const float kShieldOpacity = 0.7f;
75
76 // The color and opacity of the overview selector.
77 const SkColor kWindowSelectionColor = SkColorSetARGB(51, 255, 255, 255);
78 const SkColor kWindowSelectionBorderColor = SkColorSetARGB(76, 255, 255, 255);
79
80 // Border thickness of overview selector.
81 const int kWindowSelectionBorderThickness = 1;
82
83 // Corner radius of the overview selector border.
84 const int kWindowSelectionRadius = 4;
85
86 // In the conceptual overview table, the window margin is the space reserved
87 // around the window within the cell. This margin does not overlap so the
88 // closest distance between adjacent windows will be twice this amount.
89 const int kWindowMargin = 5;
90
91 // Windows are not allowed to get taller than this.
92 const int kMaxHeight = 512;
93
94 // Margins reserved in the overview mode.
95 const float kOverviewInsetRatio = 0.05f;
96
97 // Additional vertical inset reserved for windows in overview mode.
98 const float kOverviewVerticalInset = 0.1f;
99
100 // A View having rounded corners and a specified background color which is
101 // only painted within the bounds defined by the rounded corners.
102 // TODO(varkha): This duplicates code from RoundedImageView. Refactor these
103 // classes and move into ui/views.
104 class RoundedRectView : public views::View {
105 public:
106 RoundedRectView(int corner_radius, SkColor background)
107 : corner_radius_(corner_radius), background_(background) {}
108
109 ~RoundedRectView() override {}
110
111 void OnPaint(gfx::Canvas* canvas) override {
112 views::View::OnPaint(canvas);
113
114 SkScalar radius = SkIntToScalar(corner_radius_);
115 const SkScalar kRadius[8] = {radius, radius, radius, radius,
116 radius, radius, radius, radius};
117 SkPath path;
118 gfx::Rect bounds(size());
119 bounds.set_height(bounds.height() + radius);
120 path.addRoundRect(gfx::RectToSkRect(bounds), kRadius);
121
122 canvas->ClipPath(path, true);
123 canvas->DrawColor(background_);
124 }
125
126 private:
127 int corner_radius_;
128 SkColor background_;
129
130 DISALLOW_COPY_AND_ASSIGN(RoundedRectView);
131 };
132
133 // BackgroundWith1PxBorder renders a solid background color, with a one pixel
134 // border with rounded corners. This accounts for the scaling of the canvas, so
135 // that the border is 1 pixel thick regardless of display scaling.
136 class BackgroundWith1PxBorder : public views::Background {
137 public:
138 BackgroundWith1PxBorder(SkColor background,
139 SkColor border_color,
140 int border_thickness,
141 int corner_radius);
142
143 void Paint(gfx::Canvas* canvas, views::View* view) const override;
144
145 private:
146 // Color for the one pixel border.
147 SkColor border_color_;
148
149 // Thickness of border inset.
150 int border_thickness_;
151
152 // Corner radius of the inside edge of the roundrect border stroke.
153 int corner_radius_;
154
155 DISALLOW_COPY_AND_ASSIGN(BackgroundWith1PxBorder);
156 };
157
158 BackgroundWith1PxBorder::BackgroundWith1PxBorder(SkColor background,
159 SkColor border_color,
160 int border_thickness,
161 int corner_radius)
162 : border_color_(border_color),
163 border_thickness_(border_thickness),
164 corner_radius_(corner_radius) {
165 SetNativeControlColor(background);
166 }
167
168 void BackgroundWith1PxBorder::Paint(gfx::Canvas* canvas,
169 views::View* view) const {
170 gfx::RectF border_rect_f(view->GetContentsBounds());
171
172 gfx::ScopedCanvas scoped_canvas(canvas);
173 const float scale = canvas->UndoDeviceScaleFactor();
174 border_rect_f.Scale(scale);
175 const float inset = border_thickness_ * scale - 0.5f;
176 border_rect_f.Inset(inset, inset);
177
178 SkPath path;
179 const SkScalar scaled_corner_radius =
180 SkFloatToScalar(corner_radius_ * scale + 0.5f);
181 path.addRoundRect(gfx::RectFToSkRect(border_rect_f), scaled_corner_radius,
182 scaled_corner_radius);
183
184 cc::PaintFlags flags;
185 flags.setStyle(cc::PaintFlags::kStroke_Style);
186 flags.setStrokeWidth(1);
187 flags.setAntiAlias(true);
188
189 SkPath stroke_path;
190 flags.getFillPath(path, &stroke_path);
191
192 SkPath fill_path;
193 Op(path, stroke_path, kDifference_SkPathOp, &fill_path);
194 flags.setStyle(cc::PaintFlags::kFill_Style);
195 flags.setColor(get_color());
196 canvas->sk_canvas()->drawPath(fill_path, flags);
197
198 if (border_thickness_ > 0) {
199 flags.setColor(border_color_);
200 canvas->sk_canvas()->drawPath(stroke_path, flags);
201 }
202 }
203
204 // Returns the vector for the fade in animation.
205 gfx::Vector2d GetSlideVectorForFadeIn(WindowSelector::Direction direction,
206 const gfx::Rect& bounds) {
207 gfx::Vector2d vector;
208 switch (direction) {
209 case WindowSelector::UP:
210 case WindowSelector::LEFT:
211 vector.set_x(-bounds.width());
212 break;
213 case WindowSelector::DOWN:
214 case WindowSelector::RIGHT:
215 vector.set_x(bounds.width());
216 break;
217 }
218 return vector;
219 }
220
221 // Creates and returns a background translucent widget parented in
222 // |root_window|'s default container and having |background_color|.
223 // When |border_thickness| is non-zero, a border is created having
224 // |border_color|, otherwise |border_color| parameter is ignored.
225 // The new background widget starts with |initial_opacity| and then fades in.
226 views::Widget* CreateBackgroundWidget(WmWindow* root_window,
227 ui::LayerType layer_type,
228 SkColor background_color,
229 int border_thickness,
230 int border_radius,
231 SkColor border_color,
232 float initial_opacity) {
233 views::Widget* widget = new views::Widget;
234 views::Widget::InitParams params;
235 params.type = views::Widget::InitParams::TYPE_POPUP;
236 params.keep_on_top = false;
237 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
238 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
239 params.layer_type = layer_type;
240 params.accept_events = false;
241 widget->set_focus_on_creation(false);
242 // Parenting in kShellWindowId_WallpaperContainer allows proper layering of
243 // the shield and selection widgets. Since that container is created with
244 // USE_LOCAL_COORDINATES BoundsInScreenBehavior local bounds in |root_window_|
245 // need to be provided.
246 root_window->GetRootWindowController()->ConfigureWidgetInitParamsForContainer(
247 widget, kShellWindowId_WallpaperContainer, &params);
248 widget->Init(params);
249 WmWindow* widget_window = WmWindow::Get(widget->GetNativeWindow());
250 // Disable the "bounce in" animation when showing the window.
251 widget_window->SetVisibilityAnimationTransition(::wm::ANIMATE_NONE);
252 // The background widget should not activate the shelf when passing under it.
253 widget_window->GetWindowState()->set_ignored_by_shelf(true);
254 if (params.layer_type == ui::LAYER_SOLID_COLOR) {
255 widget_window->GetLayer()->SetColor(background_color);
256 } else {
257 views::View* content_view =
258 new RoundedRectView(border_radius, SK_ColorTRANSPARENT);
259 content_view->set_background(new BackgroundWith1PxBorder(
260 background_color, border_color, border_thickness, border_radius));
261 widget->SetContentsView(content_view);
262 }
263 widget_window->GetParent()->StackChildAtTop(widget_window);
264 widget->Show();
265 widget_window->SetOpacity(initial_opacity);
266 return widget;
267 }
268
269 bool IsMinimizedStateType(wm::WindowStateType type) {
270 return type == wm::WINDOW_STATE_TYPE_DOCKED_MINIMIZED ||
271 type == wm::WINDOW_STATE_TYPE_MINIMIZED;
272 }
273
274 } // namespace
275
276 WindowGrid::WindowGrid(WmWindow* root_window,
277 const std::vector<WmWindow*>& windows,
278 WindowSelector* window_selector)
279 : root_window_(root_window),
280 window_selector_(window_selector),
281 window_observer_(this),
282 window_state_observer_(this),
283 selected_index_(0),
284 num_columns_(0),
285 prepared_for_overview_(false) {
286 std::vector<WmWindow*> windows_in_root;
287 for (auto* window : windows) {
288 if (window->GetRootWindow() == root_window)
289 windows_in_root.push_back(window);
290 }
291
292 for (auto* window : windows_in_root) {
293 window_observer_.Add(window->aura_window());
294 window_state_observer_.Add(window->GetWindowState());
295 window_list_.push_back(
296 base::MakeUnique<WindowSelectorItem>(window, window_selector_));
297 }
298 }
299
300 WindowGrid::~WindowGrid() {}
301
302 void WindowGrid::Shutdown() {
303 for (const auto& window : window_list_)
304 window->Shutdown();
305
306 if (shield_widget_) {
307 // Fade out the shield widget. This animation continues past the lifetime
308 // of |this|.
309 WmWindow* widget_window = WmWindow::Get(shield_widget_->GetNativeWindow());
310 ui::ScopedLayerAnimationSettings animation_settings(
311 widget_window->GetLayer()->GetAnimator());
312 animation_settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
313 kOverviewSelectorTransitionMilliseconds));
314 animation_settings.SetTweenType(gfx::Tween::EASE_OUT);
315 animation_settings.SetPreemptionStrategy(
316 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
317 // CleanupAnimationObserver will delete itself (and the shield widget) when
318 // the opacity animation is complete.
319 // Ownership over the observer is passed to the window_selector_->delegate()
320 // which has longer lifetime so that animations can continue even after the
321 // overview mode is shut down.
322 views::Widget* shield_widget = shield_widget_.get();
323 std::unique_ptr<CleanupAnimationObserver> observer(
324 new CleanupAnimationObserver(std::move(shield_widget_)));
325 animation_settings.AddObserver(observer.get());
326 window_selector_->delegate()->AddDelayedAnimationObserver(
327 std::move(observer));
328 shield_widget->SetOpacity(0.f);
329 }
330 }
331
332 void WindowGrid::PrepareForOverview() {
333 InitShieldWidget();
334 for (const auto& window : window_list_)
335 window->PrepareForOverview();
336 prepared_for_overview_ = true;
337 }
338
339 void WindowGrid::PositionWindows(bool animate) {
340 if (window_selector_->is_shut_down() || window_list_.empty())
341 return;
342 DCHECK(shield_widget_.get());
343 // Keep the background shield widget covering the whole screen.
344 WmWindow* widget_window = WmWindow::Get(shield_widget_->GetNativeWindow());
345 const gfx::Rect bounds = widget_window->GetParent()->GetBounds();
346 widget_window->SetBounds(bounds);
347 gfx::Rect total_bounds =
348 root_window_->ConvertRectToScreen(wm::GetDisplayWorkAreaBoundsInParent(
349 root_window_->GetChildByShellWindowId(
350 kShellWindowId_DefaultContainer)));
351 // Windows occupy vertically centered area with additional vertical insets.
352 int horizontal_inset =
353 gfx::ToFlooredInt(std::min(kOverviewInsetRatio * total_bounds.width(),
354 kOverviewInsetRatio * total_bounds.height()));
355 int vertical_inset =
356 horizontal_inset +
357 kOverviewVerticalInset * (total_bounds.height() - 2 * horizontal_inset);
358 total_bounds.Inset(std::max(0, horizontal_inset - kWindowMargin),
359 std::max(0, vertical_inset - kWindowMargin));
360 std::vector<gfx::Rect> rects;
361
362 // Keep track of the lowest coordinate.
363 int max_bottom = total_bounds.y();
364
365 // Right bound of the narrowest row.
366 int min_right = total_bounds.right();
367 // Right bound of the widest row.
368 int max_right = total_bounds.x();
369
370 // Keep track of the difference between the narrowest and the widest row.
371 // Initially this is set to the worst it can ever be assuming the windows fit.
372 int width_diff = total_bounds.width();
373
374 // Initially allow the windows to occupy all available width. Shrink this
375 // available space horizontally to find the breakdown into rows that achieves
376 // the minimal |width_diff|.
377 int right_bound = total_bounds.right();
378
379 // Determine the optimal height bisecting between |low_height| and
380 // |high_height|. Once this optimal height is known, |height_fixed| is set to
381 // true and the rows are balanced by repeatedly squeezing the widest row to
382 // cause windows to overflow to the subsequent rows.
383 int low_height = 2 * kWindowMargin;
384 int high_height =
385 std::max(low_height, static_cast<int>(total_bounds.height() + 1));
386 int height = 0.5 * (low_height + high_height);
387 bool height_fixed = false;
388
389 // Repeatedly try to fit the windows |rects| within |right_bound|.
390 // If a maximum |height| is found such that all window |rects| fit, this
391 // fitting continues while shrinking the |right_bound| in order to balance the
392 // rows. If the windows fit the |right_bound| would have been decremented at
393 // least once so it needs to be incremented once before getting out of this
394 // loop and one additional pass made to actually fit the |rects|.
395 // If the |rects| cannot fit (e.g. there are too many windows) the bisection
396 // will still finish and we might increment the |right_bound| once pixel extra
397 // which is acceptable since there is an unused margin on the right.
398 bool make_last_adjustment = false;
399 while (true) {
400 gfx::Rect overview_bounds(total_bounds);
401 overview_bounds.set_width(right_bound - total_bounds.x());
402 bool windows_fit = FitWindowRectsInBounds(
403 overview_bounds, std::min(kMaxHeight + 2 * kWindowMargin, height),
404 &rects, &max_bottom, &min_right, &max_right);
405
406 if (height_fixed) {
407 if (!windows_fit) {
408 // Revert the previous change to |right_bound| and do one last pass.
409 right_bound++;
410 make_last_adjustment = true;
411 break;
412 }
413 // Break if all the windows are zero-width at the current scale.
414 if (max_right <= total_bounds.x())
415 break;
416 } else {
417 // Find the optimal row height bisecting between |low_height| and
418 // |high_height|.
419 if (windows_fit)
420 low_height = height;
421 else
422 high_height = height;
423 height = 0.5 * (low_height + high_height);
424 // When height can no longer be improved, start balancing the rows.
425 if (height == low_height)
426 height_fixed = true;
427 }
428
429 if (windows_fit && height_fixed) {
430 if (max_right - min_right <= width_diff) {
431 // Row alignment is getting better. Try to shrink the |right_bound| in
432 // order to squeeze the widest row.
433 right_bound = max_right - 1;
434 width_diff = max_right - min_right;
435 } else {
436 // Row alignment is getting worse.
437 // Revert the previous change to |right_bound| and do one last pass.
438 right_bound++;
439 make_last_adjustment = true;
440 break;
441 }
442 }
443 }
444 // Once the windows in |window_list_| no longer fit, the change to
445 // |right_bound| was reverted. Perform one last pass to position the |rects|.
446 if (make_last_adjustment) {
447 gfx::Rect overview_bounds(total_bounds);
448 overview_bounds.set_width(right_bound - total_bounds.x());
449 FitWindowRectsInBounds(overview_bounds,
450 std::min(kMaxHeight + 2 * kWindowMargin, height),
451 &rects, &max_bottom, &min_right, &max_right);
452 }
453 // Position the windows centering the left-aligned rows vertically.
454 gfx::Vector2d offset(0, (total_bounds.bottom() - max_bottom) / 2);
455 for (size_t i = 0; i < window_list_.size(); ++i) {
456 window_list_[i]->SetBounds(
457 rects[i] + offset,
458 animate
459 ? OverviewAnimationType::OVERVIEW_ANIMATION_LAY_OUT_SELECTOR_ITEMS
460 : OverviewAnimationType::OVERVIEW_ANIMATION_NONE);
461 }
462
463 // If the selection widget is active, reposition it without any animation.
464 if (selection_widget_)
465 MoveSelectionWidgetToTarget(animate);
466 }
467
468 bool WindowGrid::Move(WindowSelector::Direction direction, bool animate) {
469 bool recreate_selection_widget = false;
470 bool out_of_bounds = false;
471 bool changed_selection_index = false;
472 gfx::Rect old_bounds;
473 if (SelectedWindow()) {
474 old_bounds = SelectedWindow()->target_bounds();
475 // Make the old selected window header non-transparent first.
476 SelectedWindow()->SetSelected(false);
477 }
478
479 // [up] key is equivalent to [left] key and [down] key is equivalent to
480 // [right] key.
481 if (!selection_widget_) {
482 switch (direction) {
483 case WindowSelector::UP:
484 case WindowSelector::LEFT:
485 selected_index_ = window_list_.size() - 1;
486 break;
487 case WindowSelector::DOWN:
488 case WindowSelector::RIGHT:
489 selected_index_ = 0;
490 break;
491 }
492 changed_selection_index = true;
493 }
494 while (!changed_selection_index ||
495 (!out_of_bounds && window_list_[selected_index_]->dimmed())) {
496 switch (direction) {
497 case WindowSelector::UP:
498 case WindowSelector::LEFT:
499 if (selected_index_ == 0)
500 out_of_bounds = true;
501 selected_index_--;
502 break;
503 case WindowSelector::DOWN:
504 case WindowSelector::RIGHT:
505 if (selected_index_ >= window_list_.size() - 1)
506 out_of_bounds = true;
507 selected_index_++;
508 break;
509 }
510 if (!out_of_bounds && SelectedWindow()) {
511 if (SelectedWindow()->target_bounds().y() != old_bounds.y())
512 recreate_selection_widget = true;
513 }
514 changed_selection_index = true;
515 }
516 MoveSelectionWidget(direction, recreate_selection_widget, out_of_bounds,
517 animate);
518
519 // Make the new selected window header fully transparent.
520 if (SelectedWindow())
521 SelectedWindow()->SetSelected(true);
522 return out_of_bounds;
523 }
524
525 WindowSelectorItem* WindowGrid::SelectedWindow() const {
526 if (!selection_widget_)
527 return nullptr;
528 CHECK(selected_index_ < window_list_.size());
529 return window_list_[selected_index_].get();
530 }
531
532 bool WindowGrid::Contains(const WmWindow* window) const {
533 for (const auto& window_item : window_list_) {
534 if (window_item->Contains(window))
535 return true;
536 }
537 return false;
538 }
539
540 void WindowGrid::FilterItems(const base::string16& pattern) {
541 base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents finder(pattern);
542 for (const auto& window : window_list_) {
543 if (finder.Search(window->GetWindow()->GetTitle(), nullptr, nullptr)) {
544 window->SetDimmed(false);
545 } else {
546 window->SetDimmed(true);
547 if (selection_widget_ && SelectedWindow() == window.get()) {
548 SelectedWindow()->SetSelected(false);
549 selection_widget_.reset();
550 selector_shadow_.reset();
551 }
552 }
553 }
554 }
555
556 void WindowGrid::WindowClosing(WindowSelectorItem* window) {
557 if (!selection_widget_ || SelectedWindow() != window)
558 return;
559 WmWindow* selection_widget_window =
560 WmWindow::Get(selection_widget_->GetNativeWindow());
561 std::unique_ptr<ScopedOverviewAnimationSettings> animation_settings_label =
562 ScopedOverviewAnimationSettingsFactory::Get()
563 ->CreateOverviewAnimationSettings(
564 OverviewAnimationType::OVERVIEW_ANIMATION_CLOSING_SELECTOR_ITEM,
565 selection_widget_window);
566 selection_widget_->SetOpacity(0.f);
567 }
568
569 void WindowGrid::OnWindowDestroying(aura::Window* window) {
570 window_observer_.Remove(window);
571 window_state_observer_.Remove(wm::GetWindowState(window));
572 auto iter = std::find_if(window_list_.begin(), window_list_.end(),
573 WindowSelectorItemComparator(WmWindow::Get(window)));
574
575 DCHECK(iter != window_list_.end());
576
577 size_t removed_index = iter - window_list_.begin();
578 window_list_.erase(iter);
579
580 if (empty()) {
581 // If the grid is now empty, notify the window selector so that it erases us
582 // from its grid list.
583 window_selector_->OnGridEmpty(this);
584 return;
585 }
586
587 // If selecting, update the selection index.
588 if (selection_widget_) {
589 bool send_focus_alert = selected_index_ == removed_index;
590 if (selected_index_ >= removed_index && selected_index_ != 0)
591 selected_index_--;
592 SelectedWindow()->SetSelected(true);
593 if (send_focus_alert)
594 SelectedWindow()->SendAccessibleSelectionEvent();
595 }
596
597 PositionWindows(true);
598 }
599
600 void WindowGrid::OnWindowBoundsChanged(aura::Window* window,
601 const gfx::Rect& old_bounds,
602 const gfx::Rect& new_bounds) {
603 // During preparation, window bounds can change. Ignore bounds
604 // change notifications in this case; we'll reposition soon.
605 if (!prepared_for_overview_)
606 return;
607
608 auto iter = std::find_if(window_list_.begin(), window_list_.end(),
609 WindowSelectorItemComparator(WmWindow::Get(window)));
610 DCHECK(iter != window_list_.end());
611
612 // Immediately finish any active bounds animation.
613 window->layer()->GetAnimator()->StopAnimatingProperty(
614 ui::LayerAnimationElement::BOUNDS);
615 PositionWindows(false);
616 }
617
618 void WindowGrid::OnPostWindowStateTypeChange(wm::WindowState* window_state,
619 wm::WindowStateType old_type) {
620 // During preparation, window state can change, e.g. updating shelf
621 // visibility may show the temporarily hidden (minimized) panels.
622 if (!prepared_for_overview_)
623 return;
624
625 wm::WindowStateType new_type = window_state->GetStateType();
626 if (IsMinimizedStateType(old_type) == IsMinimizedStateType(new_type))
627 return;
628
629 auto iter =
630 std::find_if(window_list_.begin(), window_list_.end(),
631 [window_state](std::unique_ptr<WindowSelectorItem>& item) {
632 return item->Contains(window_state->window());
633 });
634 if (iter != window_list_.end()) {
635 (*iter)->OnMinimizedStateChanged();
636 PositionWindows(false);
637 }
638 }
639
640 void WindowGrid::InitShieldWidget() {
641 // TODO(varkha): The code assumes that SHELF_BACKGROUND_MAXIMIZED is
642 // synonymous with a black shelf background. Update this code if that
643 // assumption is no longer valid.
644 const float initial_opacity =
645 (WmShelf::ForWindow(root_window_)->GetBackgroundType() ==
646 SHELF_BACKGROUND_MAXIMIZED)
647 ? 1.f
648 : 0.f;
649 shield_widget_.reset(
650 CreateBackgroundWidget(root_window_, ui::LAYER_SOLID_COLOR, kShieldColor,
651 0, 0, SK_ColorTRANSPARENT, initial_opacity));
652 WmWindow* widget_window = WmWindow::Get(shield_widget_->GetNativeWindow());
653 const gfx::Rect bounds = widget_window->GetParent()->GetBounds();
654 widget_window->SetBounds(bounds);
655 widget_window->SetName("OverviewModeShield");
656
657 ui::ScopedLayerAnimationSettings animation_settings(
658 widget_window->GetLayer()->GetAnimator());
659 animation_settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
660 kOverviewSelectorTransitionMilliseconds));
661 animation_settings.SetTweenType(gfx::Tween::EASE_OUT);
662 animation_settings.SetPreemptionStrategy(
663 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
664 shield_widget_->SetOpacity(kShieldOpacity);
665 }
666
667 void WindowGrid::InitSelectionWidget(WindowSelector::Direction direction) {
668 selection_widget_.reset(CreateBackgroundWidget(
669 root_window_, ui::LAYER_TEXTURED, kWindowSelectionColor,
670 kWindowSelectionBorderThickness, kWindowSelectionRadius,
671 kWindowSelectionBorderColor, 0.f));
672 WmWindow* widget_window = WmWindow::Get(selection_widget_->GetNativeWindow());
673 const gfx::Rect target_bounds =
674 root_window_->ConvertRectFromScreen(SelectedWindow()->target_bounds());
675 gfx::Vector2d fade_out_direction =
676 GetSlideVectorForFadeIn(direction, target_bounds);
677 widget_window->SetBounds(target_bounds - fade_out_direction);
678 widget_window->SetName("OverviewModeSelector");
679
680 selector_shadow_.reset(new ::wm::Shadow());
681 selector_shadow_->Init(::wm::ShadowElevation::LARGE);
682 selector_shadow_->layer()->SetVisible(true);
683 selection_widget_->GetLayer()->SetMasksToBounds(false);
684 selection_widget_->GetLayer()->Add(selector_shadow_->layer());
685 selector_shadow_->SetContentBounds(gfx::Rect(target_bounds.size()));
686 }
687
688 void WindowGrid::MoveSelectionWidget(WindowSelector::Direction direction,
689 bool recreate_selection_widget,
690 bool out_of_bounds,
691 bool animate) {
692 // If the selection widget is already active, fade it out in the selection
693 // direction.
694 if (selection_widget_ && (recreate_selection_widget || out_of_bounds)) {
695 // Animate the old selection widget and then destroy it.
696 views::Widget* old_selection = selection_widget_.get();
697 WmWindow* old_selection_window =
698 WmWindow::Get(old_selection->GetNativeWindow());
699 gfx::Vector2d fade_out_direction =
700 GetSlideVectorForFadeIn(direction, old_selection_window->GetBounds());
701
702 ui::ScopedLayerAnimationSettings animation_settings(
703 old_selection_window->GetLayer()->GetAnimator());
704 animation_settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
705 kOverviewSelectorTransitionMilliseconds));
706 animation_settings.SetPreemptionStrategy(
707 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
708 animation_settings.SetTweenType(gfx::Tween::FAST_OUT_LINEAR_IN);
709 // CleanupAnimationObserver will delete itself (and the widget) when the
710 // motion animation is complete.
711 // Ownership over the observer is passed to the window_selector_->delegate()
712 // which has longer lifetime so that animations can continue even after the
713 // overview mode is shut down.
714 std::unique_ptr<CleanupAnimationObserver> observer(
715 new CleanupAnimationObserver(std::move(selection_widget_)));
716 animation_settings.AddObserver(observer.get());
717 window_selector_->delegate()->AddDelayedAnimationObserver(
718 std::move(observer));
719 old_selection->SetOpacity(0.f);
720 old_selection_window->SetBounds(old_selection_window->GetBounds() +
721 fade_out_direction);
722 old_selection->Hide();
723 }
724 if (out_of_bounds)
725 return;
726
727 if (!selection_widget_)
728 InitSelectionWidget(direction);
729 // Send an a11y alert so that if ChromeVox is enabled, the item label is
730 // read.
731 SelectedWindow()->SendAccessibleSelectionEvent();
732 // The selection widget is moved to the newly selected item in the same
733 // grid.
734 MoveSelectionWidgetToTarget(animate);
735 }
736
737 void WindowGrid::MoveSelectionWidgetToTarget(bool animate) {
738 gfx::Rect bounds =
739 root_window_->ConvertRectFromScreen(SelectedWindow()->target_bounds());
740 if (animate) {
741 WmWindow* selection_widget_window =
742 WmWindow::Get(selection_widget_->GetNativeWindow());
743 ui::ScopedLayerAnimationSettings animation_settings(
744 selection_widget_window->GetLayer()->GetAnimator());
745 animation_settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
746 kOverviewSelectorTransitionMilliseconds));
747 animation_settings.SetTweenType(gfx::Tween::EASE_IN_OUT);
748 animation_settings.SetPreemptionStrategy(
749 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
750 selection_widget_->SetBounds(bounds);
751 selection_widget_->SetOpacity(1.f);
752
753 if (selector_shadow_) {
754 ui::ScopedLayerAnimationSettings animation_settings_shadow(
755 selector_shadow_->shadow_layer()->GetAnimator());
756 animation_settings_shadow.SetTransitionDuration(
757 base::TimeDelta::FromMilliseconds(
758 kOverviewSelectorTransitionMilliseconds));
759 animation_settings_shadow.SetTweenType(gfx::Tween::EASE_IN_OUT);
760 animation_settings_shadow.SetPreemptionStrategy(
761 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
762 bounds.Inset(1, 1);
763 selector_shadow_->SetContentBounds(
764 gfx::Rect(gfx::Point(1, 1), bounds.size()));
765 }
766 return;
767 }
768 selection_widget_->SetBounds(bounds);
769 selection_widget_->SetOpacity(1.f);
770 if (selector_shadow_) {
771 bounds.Inset(1, 1);
772 selector_shadow_->SetContentBounds(
773 gfx::Rect(gfx::Point(1, 1), bounds.size()));
774 }
775 }
776
777 bool WindowGrid::FitWindowRectsInBounds(const gfx::Rect& bounds,
778 int height,
779 std::vector<gfx::Rect>* rects,
780 int* max_bottom,
781 int* min_right,
782 int* max_right) {
783 rects->resize(window_list_.size());
784 bool windows_fit = true;
785
786 // Start in the top-left corner of |bounds|.
787 int left = bounds.x();
788 int top = bounds.y();
789
790 // Keep track of the lowest coordinate.
791 *max_bottom = bounds.y();
792
793 // Right bound of the narrowest row.
794 *min_right = bounds.right();
795 // Right bound of the widest row.
796 *max_right = bounds.x();
797
798 // All elements are of same height and only the height is necessary to
799 // determine each item's scale.
800 const gfx::Size item_size(0, height);
801 size_t i = 0;
802 for (const auto& window : window_list_) {
803 const gfx::Rect target_bounds = window->GetTargetBoundsInScreen();
804 const int width =
805 std::max(1, gfx::ToFlooredInt(target_bounds.width() *
806 window->GetItemScale(item_size)) +
807 2 * kWindowMargin);
808 if (left + width > bounds.right()) {
809 // Move to the next row if possible.
810 if (*min_right > left)
811 *min_right = left;
812 if (*max_right < left)
813 *max_right = left;
814 top += height;
815
816 // Check if the new row reaches the bottom or if the first item in the new
817 // row does not fit within the available width.
818 if (top + height > bounds.bottom() ||
819 bounds.x() + width > bounds.right()) {
820 windows_fit = false;
821 break;
822 }
823 left = bounds.x();
824 }
825
826 // Position the current rect.
827 (*rects)[i].SetRect(left, top, width, height);
828
829 // Increment horizontal position using sanitized positive |width()|.
830 left += (*rects)[i].width();
831
832 if (++i == window_list_.size()) {
833 // Update the narrowest and widest row width for the last row.
834 if (*min_right > left)
835 *min_right = left;
836 if (*max_right < left)
837 *max_right = left;
838 }
839 *max_bottom = top + height;
840 }
841 return windows_fit;
842 }
843
844 } // namespace ash
OLDNEW
« no previous file with comments | « ash/common/wm/overview/window_grid.h ('k') | ash/common/wm/overview/window_selector.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698