1 // Copyright (c) 2012 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/window_animations.h"
6
7 #include <math.h>
8
9 #include <algorithm>
10 #include <vector>
11
12 #include "ash/launcher/launcher.h"
13 #include "ash/screen_ash.h"
14 #include "ash/shelf/shelf_layout_manager.h"
15 #include "ash/shelf/shelf_widget.h"
16 #include "ash/shell.h"
17 #include "ash/wm/window_util.h"
18 #include "ash/wm/workspace_controller.h"
19 #include "base/command_line.h"
20 #include "base/compiler_specific.h"
21 #include "base/logging.h"
22 #include "base/message_loop/message_loop.h"
23 #include "base/stl_util.h"
24 #include "base/time/time.h"
25 #include "ui/aura/client/aura_constants.h"
26 #include "ui/aura/window.h"
27 #include "ui/aura/window_observer.h"
28 #include "ui/aura/window_property.h"
29 #include "ui/compositor/compositor_observer.h"
30 #include "ui/compositor/layer.h"
31 #include "ui/compositor/layer_animation_observer.h"
32 #include "ui/compositor/layer_animation_sequence.h"
33 #include "ui/compositor/layer_animator.h"
34 #include "ui/compositor/scoped_layer_animation_settings.h"
35 #include "ui/gfx/interpolated_transform.h"
36 #include "ui/gfx/screen.h"
37 #include "ui/gfx/vector3d_f.h"
38 #include "ui/views/corewm/window_util.h"
39 #include "ui/views/view.h"
40 #include "ui/views/widget/widget.h"
41
42 namespace ash {
43 namespace {
44 const int kLayerAnimationsForMinimizeDurationMS = 200;
45
46 // Durations for the cross-fade animation, in milliseconds.
47 const float kCrossFadeDurationMinMs = 200.f;
48 const float kCrossFadeDurationMaxMs = 400.f;
49
50 // Durations for the brightness/grayscale fade animation, in milliseconds.
51 const int kBrightnessGrayscaleFadeDurationMs = 1000;
52
53 // Brightness/grayscale values for hide/show window animations.
54 const float kWindowAnimation_HideBrightnessGrayscale = 1.f;
55 const float kWindowAnimation_ShowBrightnessGrayscale = 0.f;
56
57 const float kWindowAnimation_HideOpacity = 0.f;
58 const float kWindowAnimation_ShowOpacity = 1.f;
59 // TODO(sky): if we end up sticking with 0, nuke the code doing the rotation.
60 const float kWindowAnimation_MinimizeRotate = 0.f;
61
62 // Scales for AshWindow above/below current workspace.
63 const float kLayerScaleAboveSize = 1.1f;
64 const float kLayerScaleBelowSize = .9f;
65
Round64(float f)66 int64 Round64(float f) {
67 return static_cast<int64>(f + 0.5f);
68 }
69
70 } // namespace
71
72 const int kCrossFadeDurationMS = 200;
73
AddLayerAnimationsForMinimize(aura::Window * window,bool show)74 void AddLayerAnimationsForMinimize(aura::Window* window, bool show) {
75 // Recalculate the transform at restore time since the launcher item may have
76 // moved while the window was minimized.
77 gfx::Rect bounds = window->bounds();
78 gfx::Rect target_bounds = GetMinimizeAnimationTargetBoundsInScreen(window);
79 target_bounds =
80 ScreenAsh::ConvertRectFromScreen(window->parent(), target_bounds);
81
82 float scale_x = static_cast<float>(target_bounds.width()) / bounds.width();
83 float scale_y = static_cast<float>(target_bounds.height()) / bounds.height();
84
85 scoped_ptr<ui::InterpolatedTransform> scale(
86 new ui::InterpolatedScale(gfx::Point3F(1, 1, 1),
87 gfx::Point3F(scale_x, scale_y, 1)));
88
89 scoped_ptr<ui::InterpolatedTransform> translation(
90 new ui::InterpolatedTranslation(
91 gfx::Point(),
92 gfx::Point(target_bounds.x() - bounds.x(),
93 target_bounds.y() - bounds.y())));
94
95 scoped_ptr<ui::InterpolatedTransform> rotation(
96 new ui::InterpolatedRotation(0, kWindowAnimation_MinimizeRotate));
97
98 scoped_ptr<ui::InterpolatedTransform> rotation_about_pivot(
99 new ui::InterpolatedTransformAboutPivot(
100 gfx::Point(bounds.width() * 0.5, bounds.height() * 0.5),
101 rotation.release()));
102
103 scale->SetChild(translation.release());
104 rotation_about_pivot->SetChild(scale.release());
105
106 rotation_about_pivot->SetReversed(show);
107
108 base::TimeDelta duration = window->layer()->GetAnimator()->
109 GetTransitionDuration();
110
111 scoped_ptr<ui::LayerAnimationElement> transition(
112 ui::LayerAnimationElement::CreateInterpolatedTransformElement(
113 rotation_about_pivot.release(), duration));
114
115 transition->set_tween_type(
116 show ? gfx::Tween::EASE_IN : gfx::Tween::EASE_IN_OUT);
117
118 window->layer()->GetAnimator()->ScheduleAnimation(
119 new ui::LayerAnimationSequence(transition.release()));
120
121 // When hiding a window, turn off blending until the animation is 3 / 4 done
122 // to save bandwidth and reduce jank.
123 if (!show) {
124 window->layer()->GetAnimator()->SchedulePauseForProperties(
125 (duration * 3) / 4, ui::LayerAnimationElement::OPACITY, -1);
126 }
127
128 // Fade in and out quickly when the window is small to reduce jank.
129 float opacity = show ? 1.0f : 0.0f;
130 window->layer()->GetAnimator()->ScheduleAnimation(
131 new ui::LayerAnimationSequence(
132 ui::LayerAnimationElement::CreateOpacityElement(
133 opacity, duration / 4)));
134 }
135
AnimateShowWindow_Minimize(aura::Window * window)136 void AnimateShowWindow_Minimize(aura::Window* window) {
137 window->layer()->set_delegate(window);
138 window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
139 ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
140 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(
141 kLayerAnimationsForMinimizeDurationMS);
142 settings.SetTransitionDuration(duration);
143 AddLayerAnimationsForMinimize(window, true);
144
145 // Now that the window has been restored, we need to clear its animation style
146 // to default so that normal animation applies.
147 views::corewm::SetWindowVisibilityAnimationType(
148 window, views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT);
149 }
150
AnimateHideWindow_Minimize(aura::Window * window)151 void AnimateHideWindow_Minimize(aura::Window* window) {
152 window->layer()->set_delegate(NULL);
153
154 // Property sets within this scope will be implicitly animated.
155 ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
156 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(
157 kLayerAnimationsForMinimizeDurationMS);
158 settings.SetTransitionDuration(duration);
159 settings.AddObserver(
160 views::corewm::CreateHidingWindowAnimationObserver(window));
161 window->layer()->SetVisible(false);
162
163 AddLayerAnimationsForMinimize(window, false);
164 }
165
AnimateShowHideWindowCommon_BrightnessGrayscale(aura::Window * window,bool show)166 void AnimateShowHideWindowCommon_BrightnessGrayscale(aura::Window* window,
167 bool show) {
168 window->layer()->set_delegate(window);
169
170 float start_value, end_value;
171 if (show) {
172 start_value = kWindowAnimation_HideBrightnessGrayscale;
173 end_value = kWindowAnimation_ShowBrightnessGrayscale;
174 } else {
175 start_value = kWindowAnimation_ShowBrightnessGrayscale;
176 end_value = kWindowAnimation_HideBrightnessGrayscale;
177 }
178
179 window->layer()->SetLayerBrightness(start_value);
180 window->layer()->SetLayerGrayscale(start_value);
181 if (show) {
182 window->layer()->SetOpacity(kWindowAnimation_ShowOpacity);
183 window->layer()->SetVisible(true);
184 }
185
186 base::TimeDelta duration =
187 base::TimeDelta::FromMilliseconds(kBrightnessGrayscaleFadeDurationMs);
188
189 ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
190 settings.SetTransitionDuration(duration);
191 if (!show) {
192 settings.AddObserver(
193 views::corewm::CreateHidingWindowAnimationObserver(window));
194 }
195
196 window->layer()->GetAnimator()->
197 ScheduleTogether(
198 CreateBrightnessGrayscaleAnimationSequence(end_value, duration));
199 if (!show) {
200 window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
201 window->layer()->SetVisible(false);
202 }
203 }
204
AnimateShowWindow_BrightnessGrayscale(aura::Window * window)205 void AnimateShowWindow_BrightnessGrayscale(aura::Window* window) {
206 AnimateShowHideWindowCommon_BrightnessGrayscale(window, true);
207 }
208
AnimateHideWindow_BrightnessGrayscale(aura::Window * window)209 void AnimateHideWindow_BrightnessGrayscale(aura::Window* window) {
210 AnimateShowHideWindowCommon_BrightnessGrayscale(window, false);
211 }
212
AnimateShowWindow(aura::Window * window)213 bool AnimateShowWindow(aura::Window* window) {
214 if (!views::corewm::HasWindowVisibilityAnimationTransition(
215 window, views::corewm::ANIMATE_SHOW)) {
216 return false;
217 }
218
219 switch (views::corewm::GetWindowVisibilityAnimationType(window)) {
220 case WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE:
221 AnimateShowWindow_Minimize(window);
222 return true;
223 case WINDOW_VISIBILITY_ANIMATION_TYPE_BRIGHTNESS_GRAYSCALE:
224 AnimateShowWindow_BrightnessGrayscale(window);
225 return true;
226 default:
227 NOTREACHED();
228 return false;
229 }
230 }
231
AnimateHideWindow(aura::Window * window)232 bool AnimateHideWindow(aura::Window* window) {
233 if (!views::corewm::HasWindowVisibilityAnimationTransition(
234 window, views::corewm::ANIMATE_HIDE)) {
235 return false;
236 }
237
238 switch (views::corewm::GetWindowVisibilityAnimationType(window)) {
239 case WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE:
240 AnimateHideWindow_Minimize(window);
241 return true;
242 case WINDOW_VISIBILITY_ANIMATION_TYPE_BRIGHTNESS_GRAYSCALE:
243 AnimateHideWindow_BrightnessGrayscale(window);
244 return true;
245 default:
246 NOTREACHED();
247 return false;
248 }
249 }
250
251 // Observer for a window cross-fade animation. If either the window closes or
252 // the layer's animation completes or compositing is aborted due to GPU crash,
253 // it deletes the layer and removes itself as an observer.
254 class CrossFadeObserver : public ui::CompositorObserver,
255 public aura::WindowObserver,
256 public ui::ImplicitAnimationObserver {
257 public:
258 // Observes |window| for destruction, but does not take ownership.
259 // Takes ownership of |layer| and its child layers.
CrossFadeObserver(aura::Window * window,ui::Layer * layer)260 CrossFadeObserver(aura::Window* window, ui::Layer* layer)
261 : window_(window),
262 layer_(layer) {
263 window_->AddObserver(this);
264 layer_->GetCompositor()->AddObserver(this);
265 }
~CrossFadeObserver()266 virtual ~CrossFadeObserver() {
267 window_->RemoveObserver(this);
268 window_ = NULL;
269 layer_->GetCompositor()->RemoveObserver(this);
270 views::corewm::DeepDeleteLayers(layer_);
271 layer_ = NULL;
272 }
273
274 // ui::CompositorObserver overrides:
OnCompositingDidCommit(ui::Compositor * compositor)275 virtual void OnCompositingDidCommit(ui::Compositor* compositor) OVERRIDE {
276 }
OnCompositingStarted(ui::Compositor * compositor,base::TimeTicks start_time)277 virtual void OnCompositingStarted(ui::Compositor* compositor,
278 base::TimeTicks start_time) OVERRIDE {
279 }
OnCompositingEnded(ui::Compositor * compositor)280 virtual void OnCompositingEnded(ui::Compositor* compositor) OVERRIDE {
281 }
OnCompositingAborted(ui::Compositor * compositor)282 virtual void OnCompositingAborted(ui::Compositor* compositor) OVERRIDE {
283 // Triggers OnImplicitAnimationsCompleted() to be called and deletes us.
284 layer_->GetAnimator()->StopAnimating();
285 }
OnCompositingLockStateChanged(ui::Compositor * compositor)286 virtual void OnCompositingLockStateChanged(
287 ui::Compositor* compositor) OVERRIDE {
288 }
OnUpdateVSyncParameters(ui::Compositor * compositor,base::TimeTicks timebase,base::TimeDelta interval)289 virtual void OnUpdateVSyncParameters(ui::Compositor* compositor,
290 base::TimeTicks timebase,
291 base::TimeDelta interval) OVERRIDE {
292 }
293
294 // aura::WindowObserver overrides:
OnWindowDestroying(aura::Window * window)295 virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
296 // Triggers OnImplicitAnimationsCompleted() to be called and deletes us.
297 layer_->GetAnimator()->StopAnimating();
298 }
OnWindowRemovingFromRootWindow(aura::Window * window)299 virtual void OnWindowRemovingFromRootWindow(aura::Window* window) OVERRIDE {
300 layer_->GetAnimator()->StopAnimating();
301 }
302
303 // ui::ImplicitAnimationObserver overrides:
OnImplicitAnimationsCompleted()304 virtual void OnImplicitAnimationsCompleted() OVERRIDE {
305 delete this;
306 }
307
308 private:
309 aura::Window* window_; // not owned
310 ui::Layer* layer_; // owned
311
312 DISALLOW_COPY_AND_ASSIGN(CrossFadeObserver);
313 };
314
315 // Implementation of cross fading. Window is the window being cross faded. It
316 // should be at the target bounds. |old_layer| the previous layer from |window|.
317 // This takes ownership of |old_layer| and deletes when the animation is done.
318 // |pause_duration| is the duration to pause at the current bounds before
319 // animating. Returns the duration of the fade.
CrossFadeImpl(aura::Window * window,ui::Layer * old_layer,gfx::Tween::Type tween_type)320 base::TimeDelta CrossFadeImpl(aura::Window* window,
321 ui::Layer* old_layer,
322 gfx::Tween::Type tween_type) {
323 const gfx::Rect old_bounds(old_layer->bounds());
324 const gfx::Rect new_bounds(window->bounds());
325 const bool old_on_top = (old_bounds.width() > new_bounds.width());
326
327 // Shorten the animation if there's not much visual movement.
328 const base::TimeDelta duration = GetCrossFadeDuration(window,
329 old_bounds, new_bounds);
330
331 // Scale up the old layer while translating to new position.
332 {
333 old_layer->GetAnimator()->StopAnimating();
334 ui::ScopedLayerAnimationSettings settings(old_layer->GetAnimator());
335
336 // Animation observer owns the old layer and deletes itself.
337 settings.AddObserver(new CrossFadeObserver(window, old_layer));
338 settings.SetTransitionDuration(duration);
339 settings.SetTweenType(tween_type);
340 gfx::Transform out_transform;
341 float scale_x = static_cast<float>(new_bounds.width()) /
342 static_cast<float>(old_bounds.width());
343 float scale_y = static_cast<float>(new_bounds.height()) /
344 static_cast<float>(old_bounds.height());
345 out_transform.Translate(new_bounds.x() - old_bounds.x(),
346 new_bounds.y() - old_bounds.y());
347 out_transform.Scale(scale_x, scale_y);
348 old_layer->SetTransform(out_transform);
349 if (old_on_top) {
350 // The old layer is on top, and should fade out. The new layer below will
351 // stay opaque to block the desktop.
352 old_layer->SetOpacity(kWindowAnimation_HideOpacity);
353 }
354 // In tests |old_layer| is deleted here, as animations have zero duration.
355 old_layer = NULL;
356 }
357
358 // Set the new layer's current transform, such that the user sees a scaled
359 // version of the window with the original bounds at the original position.
360 gfx::Transform in_transform;
361 const float scale_x = static_cast<float>(old_bounds.width()) /
362 static_cast<float>(new_bounds.width());
363 const float scale_y = static_cast<float>(old_bounds.height()) /
364 static_cast<float>(new_bounds.height());
365 in_transform.Translate(old_bounds.x() - new_bounds.x(),
366 old_bounds.y() - new_bounds.y());
367 in_transform.Scale(scale_x, scale_y);
368 window->layer()->SetTransform(in_transform);
369 if (!old_on_top) {
370 // The new layer is on top and should fade in. The old layer below will
371 // stay opaque and block the desktop.
372 window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
373 }
374 {
375 // Animate the new layer to the identity transform, so the window goes to
376 // its newly set bounds.
377 ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
378 settings.SetTransitionDuration(duration);
379 settings.SetTweenType(tween_type);
380 window->layer()->SetTransform(gfx::Transform());
381 if (!old_on_top) {
382 // New layer is on top, fade it in.
383 window->layer()->SetOpacity(kWindowAnimation_ShowOpacity);
384 }
385 }
386 return duration;
387 }
388
CrossFadeToBounds(aura::Window * window,const gfx::Rect & new_bounds)389 void CrossFadeToBounds(aura::Window* window, const gfx::Rect& new_bounds) {
390 // Some test results in invoking CrossFadeToBounds when window is not visible.
391 // No animation is necessary in that case, thus just change the bounds and
392 // quit.
393 if (!window->TargetVisibility()) {
394 window->SetBounds(new_bounds);
395 return;
396 }
397
398 const gfx::Rect old_bounds = window->bounds();
399
400 // Create fresh layers for the window and all its children to paint into.
401 // Takes ownership of the old layer and all its children, which will be
402 // cleaned up after the animation completes.
403 // Specify |set_bounds| to true here to keep the old bounds in the child
404 // windows of |window|.
405 ui::Layer* old_layer = views::corewm::RecreateWindowLayers(window, true);
406 ui::Layer* new_layer = window->layer();
407
408 // Resize the window to the new size, which will force a layout and paint.
409 window->SetBounds(new_bounds);
410
411 // Ensure the higher-resolution layer is on top.
412 bool old_on_top = (old_bounds.width() > new_bounds.width());
413 if (old_on_top)
414 old_layer->parent()->StackBelow(new_layer, old_layer);
415 else
416 old_layer->parent()->StackAbove(new_layer, old_layer);
417
418 CrossFadeImpl(window, old_layer, gfx::Tween::EASE_OUT);
419 }
420
GetCrossFadeDuration(aura::Window * window,const gfx::Rect & old_bounds,const gfx::Rect & new_bounds)421 base::TimeDelta GetCrossFadeDuration(aura::Window* window,
422 const gfx::Rect& old_bounds,
423 const gfx::Rect& new_bounds) {
424 if (views::corewm::WindowAnimationsDisabled(window))
425 return base::TimeDelta();
426
427 int old_area = old_bounds.width() * old_bounds.height();
428 int new_area = new_bounds.width() * new_bounds.height();
429 int max_area = std::max(old_area, new_area);
430 // Avoid divide by zero.
431 if (max_area == 0)
432 return base::TimeDelta::FromMilliseconds(kCrossFadeDurationMS);
433
434 int delta_area = std::abs(old_area - new_area);
435 // If the area didn't change, the animation is instantaneous.
436 if (delta_area == 0)
437 return base::TimeDelta::FromMilliseconds(kCrossFadeDurationMS);
438
439 float factor =
440 static_cast<float>(delta_area) / static_cast<float>(max_area);
441 const float kRange = kCrossFadeDurationMaxMs - kCrossFadeDurationMinMs;
442 return base::TimeDelta::FromMilliseconds(
443 Round64(kCrossFadeDurationMinMs + (factor * kRange)));
444 }
445
AnimateOnChildWindowVisibilityChanged(aura::Window * window,bool visible)446 bool AnimateOnChildWindowVisibilityChanged(aura::Window* window, bool visible) {
447 if (views::corewm::WindowAnimationsDisabled(window))
448 return false;
449
450 // Attempt to run CoreWm supplied animation types.
451 if (views::corewm::AnimateOnChildWindowVisibilityChanged(window, visible))
452 return true;
453
454 // Otherwise try to run an Ash-specific animation.
455 if (visible)
456 return AnimateShowWindow(window);
457 // Don't start hiding the window again if it's already being hidden.
458 return window->layer()->GetTargetOpacity() != 0.0f &&
459 AnimateHideWindow(window);
460 }
461
462 std::vector<ui::LayerAnimationSequence*>
CreateBrightnessGrayscaleAnimationSequence(float target_value,base::TimeDelta duration)463 CreateBrightnessGrayscaleAnimationSequence(float target_value,
464 base::TimeDelta duration) {
465 gfx::Tween::Type animation_type = gfx::Tween::EASE_OUT;
466 scoped_ptr<ui::LayerAnimationSequence> brightness_sequence(
467 new ui::LayerAnimationSequence());
468 scoped_ptr<ui::LayerAnimationSequence> grayscale_sequence(
469 new ui::LayerAnimationSequence());
470
471 scoped_ptr<ui::LayerAnimationElement> brightness_element(
472 ui::LayerAnimationElement::CreateBrightnessElement(
473 target_value, duration));
474 brightness_element->set_tween_type(animation_type);
475 brightness_sequence->AddElement(brightness_element.release());
476
477 scoped_ptr<ui::LayerAnimationElement> grayscale_element(
478 ui::LayerAnimationElement::CreateGrayscaleElement(
479 target_value, duration));
480 grayscale_element->set_tween_type(animation_type);
481 grayscale_sequence->AddElement(grayscale_element.release());
482
483 std::vector<ui::LayerAnimationSequence*> animations;
484 animations.push_back(brightness_sequence.release());
485 animations.push_back(grayscale_sequence.release());
486
487 return animations;
488 }
489
490 // Returns scale related to the specified AshWindowScaleType.
SetTransformForScaleAnimation(ui::Layer * layer,LayerScaleAnimationDirection type)491 void SetTransformForScaleAnimation(ui::Layer* layer,
492 LayerScaleAnimationDirection type) {
493 const float scale =
494 type == LAYER_SCALE_ANIMATION_ABOVE ? kLayerScaleAboveSize :
495 kLayerScaleBelowSize;
496 gfx::Transform transform;
497 transform.Translate(-layer->bounds().width() * (scale - 1.0f) / 2,
498 -layer->bounds().height() * (scale - 1.0f) / 2);
499 transform.Scale(scale, scale);
500 layer->SetTransform(transform);
501 }
502
GetMinimizeAnimationTargetBoundsInScreen(aura::Window * window)503 gfx::Rect GetMinimizeAnimationTargetBoundsInScreen(aura::Window* window) {
504 Launcher* launcher = Launcher::ForWindow(window);
505 // Shelf is created lazily and can be NULL.
506 if (!launcher)
507 return gfx::Rect();
508 gfx::Rect item_rect = launcher->GetScreenBoundsOfItemIconForWindow(window);
509
510 // The launcher item is visible and has an icon.
511 if (!item_rect.IsEmpty())
512 return item_rect;
513
514 // If both the icon width and height are 0, then there is no icon in the
515 // launcher for |window| or the icon is hidden in the overflow menu. If the
516 // launcher is auto hidden, one of the height or width will be 0 but the
517 // position in the launcher and the major dimension are still reported
518 // correctly and the window can be animated to the launcher item's light
519 // bar.
520 if (item_rect.width() != 0 || item_rect.height() != 0) {
521 internal::ShelfLayoutManager* layout_manager =
522 internal::ShelfLayoutManager::ForLauncher(window);
523 if (layout_manager->visibility_state() == SHELF_AUTO_HIDE) {
524 gfx::Rect shelf_bounds =
525 launcher->shelf_widget()->GetWindowBoundsInScreen();
526 switch (layout_manager->GetAlignment()) {
527 case SHELF_ALIGNMENT_BOTTOM:
528 item_rect.set_y(shelf_bounds.y());
529 break;
530 case SHELF_ALIGNMENT_LEFT:
531 item_rect.set_x(shelf_bounds.right());
532 break;
533 case SHELF_ALIGNMENT_RIGHT:
534 item_rect.set_x(shelf_bounds.x());
535 break;
536 case SHELF_ALIGNMENT_TOP:
537 item_rect.set_y(shelf_bounds.bottom());
538 break;
539 }
540 return item_rect;
541 }
542 }
543
544 // Assume the launcher is overflowed, zoom off to the bottom right of the
545 // work area.
546 gfx::Rect work_area =
547 Shell::GetScreen()->GetDisplayNearestWindow(window).work_area();
548 return gfx::Rect(work_area.right(), work_area.bottom(), 0, 0);
549 }
550
551 } // namespace ash
552