1 // Copyright 2019 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 "ui/views/layout/animating_layout_manager.h"
6 
7 #include <algorithm>
8 #include <map>
9 #include <set>
10 #include <utility>
11 #include <vector>
12 
13 #include "base/auto_reset.h"
14 #include "base/stl_util.h"
15 #include "ui/gfx/animation/animation_container.h"
16 #include "ui/gfx/animation/slide_animation.h"
17 #include "ui/views/animation/animation_delegate_views.h"
18 #include "ui/views/layout/normalized_geometry.h"
19 #include "ui/views/view.h"
20 
21 namespace views {
22 
23 namespace {
24 
25 // Returns the ChildLayout data for the child view in the proposed layout, or
26 // nullptr if not found.
FindChildViewInLayout(const ProposedLayout & layout,const View * view)27 const ChildLayout* FindChildViewInLayout(const ProposedLayout& layout,
28                                          const View* view) {
29   if (!view)
30     return nullptr;
31 
32   // The number of children should be small enough that this is more efficient
33   // than caching a lookup set.
34   for (auto& child_layout : layout.child_layouts) {
35     if (child_layout.child_view == view)
36       return &child_layout;
37   }
38   return nullptr;
39 }
40 
FindChildViewInLayout(ProposedLayout * layout,const View * view)41 ChildLayout* FindChildViewInLayout(ProposedLayout* layout, const View* view) {
42   return const_cast<ChildLayout*>(FindChildViewInLayout(*layout, view));
43 }
44 
45 // Describes the type of fade, used by LayoutFadeInfo (see below).
46 enum class LayoutFadeType {
47   // This view is fading in as part of the current animation.
48   kFadingIn,
49   // This view is fading out as part of the current animation.
50   kFadingOut,
51   // This view was fading as part of a previous animation that was interrupted
52   // and redirected. No child views in the current animation should base their
53   // position off of it.
54   kContinuingFade
55 };
56 
57 }  // namespace
58 
59 // Holds data about a view that is fading in or out as part of an animation.
60 struct AnimatingLayoutManager::LayoutFadeInfo {
61   // How the child view is fading.
62   LayoutFadeType fade_type;
63   // The child view which is fading.
64   View* child_view = nullptr;
65   // The view previous (leading side) to the fading view which is in both the
66   // starting and target layout, or null if none.
67   View* prev_view = nullptr;
68   // The view next (trailing side) to the fading view which is in both the
69   // starting and target layout, or null if none.
70   View* next_view = nullptr;
71   // The full-size bounds, normalized to the orientation of the layout manaer,
72   // that |child_view| starts with, if fading out, or ends with, if fading in.
73   NormalizedRect reference_bounds;
74   // The offset from the end of |prev_view| and the start of |next_view|. Insets
75   // may be negative if the views overlap.
76   Inset1D offsets;
77 };
78 
79 // Manages the animation and various callbacks from the animation system that
80 // are required to update the layout during animations.
81 class AnimatingLayoutManager::AnimationDelegate
82     : public AnimationDelegateViews {
83  public:
84   explicit AnimationDelegate(AnimatingLayoutManager* layout_manager);
85   ~AnimationDelegate() override = default;
86 
87   // Returns true after the host view is added to a widget or animation has been
88   // enabled by a unit test.
89   //
90   // Before that, animation is not possible, so all changes to the host view
91   // should result in the host view's layout being snapped directly to the
92   // target layout.
ready_to_animate() const93   bool ready_to_animate() const { return ready_to_animate_; }
94 
95   // Pushes animation configuration (tween type, duration) through to the
96   // animation itself.
97   void UpdateAnimationParameters();
98 
99   // Starts the animation.
100   void Animate();
101 
102   // Cancels and resets the current animation (if any).
103   void Reset();
104 
105   // If the current layout is not yet ready to animate, transitions into the
106   // ready-to-animate state, possibly resetting the current layout and
107   // invalidating the host to make sure the layout is up to date.
108   void MakeReadyForAnimation();
109 
110  private:
111   // Observer used to watch for the host view being parented to a widget.
112   class ViewWidgetObserver : public ViewObserver {
113    public:
ViewWidgetObserver(AnimationDelegate * animation_delegate)114     explicit ViewWidgetObserver(AnimationDelegate* animation_delegate)
115         : animation_delegate_(animation_delegate) {}
116 
OnViewAddedToWidget(View * observed_view)117     void OnViewAddedToWidget(View* observed_view) override {
118       animation_delegate_->MakeReadyForAnimation();
119     }
120 
OnViewIsDeleting(View * observed_view)121     void OnViewIsDeleting(View* observed_view) override {
122       if (animation_delegate_->scoped_observer_.IsObserving(observed_view))
123         animation_delegate_->scoped_observer_.Remove(observed_view);
124     }
125 
126    private:
127     AnimationDelegate* const animation_delegate_;
128   };
129   friend class Observer;
130 
131   // AnimationDelegateViews:
132   void AnimationProgressed(const gfx::Animation* animation) override;
133   void AnimationCanceled(const gfx::Animation* animation) override;
134   void AnimationEnded(const gfx::Animation* animation) override;
135 
136   bool ready_to_animate_ = false;
137   bool resetting_animation_ = false;
138   AnimatingLayoutManager* const target_layout_manager_;
139   std::unique_ptr<gfx::SlideAnimation> animation_;
140   ViewWidgetObserver view_widget_observer_{this};
141   ScopedObserver<View, ViewObserver> scoped_observer_{&view_widget_observer_};
142 };
143 
AnimationDelegate(AnimatingLayoutManager * layout_manager)144 AnimatingLayoutManager::AnimationDelegate::AnimationDelegate(
145     AnimatingLayoutManager* layout_manager)
146     : AnimationDelegateViews(layout_manager->host_view()),
147       target_layout_manager_(layout_manager),
148       animation_(std::make_unique<gfx::SlideAnimation>(this)) {
149   animation_->SetContainer(new gfx::AnimationContainer());
150   View* const host_view = layout_manager->host_view();
151   DCHECK(host_view);
152   if (host_view->GetWidget())
153     MakeReadyForAnimation();
154   else
155     scoped_observer_.Add(host_view);
156   UpdateAnimationParameters();
157 }
158 
UpdateAnimationParameters()159 void AnimatingLayoutManager::AnimationDelegate::UpdateAnimationParameters() {
160   animation_->SetTweenType(target_layout_manager_->tween_type());
161   animation_->SetSlideDuration(target_layout_manager_->animation_duration());
162 }
163 
Animate()164 void AnimatingLayoutManager::AnimationDelegate::Animate() {
165   DCHECK(ready_to_animate_);
166   Reset();
167   animation_->Show();
168 }
169 
Reset()170 void AnimatingLayoutManager::AnimationDelegate::Reset() {
171   if (!ready_to_animate_)
172     return;
173   base::AutoReset<bool> setter(&resetting_animation_, true);
174   animation_->Reset();
175 }
176 
MakeReadyForAnimation()177 void AnimatingLayoutManager::AnimationDelegate::MakeReadyForAnimation() {
178   if (!ready_to_animate_) {
179     target_layout_manager_->ResetLayout();
180     ready_to_animate_ = true;
181     if (scoped_observer_.IsObserving(target_layout_manager_->host_view()))
182       scoped_observer_.Remove(target_layout_manager_->host_view());
183   }
184 }
185 
AnimationProgressed(const gfx::Animation * animation)186 void AnimatingLayoutManager::AnimationDelegate::AnimationProgressed(
187     const gfx::Animation* animation) {
188   DCHECK(animation_.get() == animation);
189   target_layout_manager_->AnimateTo(animation->GetCurrentValue());
190 }
191 
AnimationCanceled(const gfx::Animation * animation)192 void AnimatingLayoutManager::AnimationDelegate::AnimationCanceled(
193     const gfx::Animation* animation) {
194   AnimationEnded(animation);
195 }
196 
AnimationEnded(const gfx::Animation * animation)197 void AnimatingLayoutManager::AnimationDelegate::AnimationEnded(
198     const gfx::Animation* animation) {
199   if (resetting_animation_)
200     return;
201   DCHECK(animation_.get() == animation);
202   target_layout_manager_->AnimateTo(1.0);
203 }
204 
205 // AnimatingLayoutManager:
206 
207 AnimatingLayoutManager::AnimatingLayoutManager() = default;
208 AnimatingLayoutManager::~AnimatingLayoutManager() = default;
209 
SetShouldAnimateBounds(bool should_animate_bounds)210 AnimatingLayoutManager& AnimatingLayoutManager::SetShouldAnimateBounds(
211     bool should_animate_bounds) {
212   if (should_animate_bounds_ != should_animate_bounds) {
213     should_animate_bounds_ = should_animate_bounds;
214     ResetLayout();
215   }
216   return *this;
217 }
218 
SetAnimationDuration(base::TimeDelta animation_duration)219 AnimatingLayoutManager& AnimatingLayoutManager::SetAnimationDuration(
220     base::TimeDelta animation_duration) {
221   DCHECK_GE(animation_duration, base::TimeDelta());
222   animation_duration_ = animation_duration;
223   if (animation_delegate_)
224     animation_delegate_->UpdateAnimationParameters();
225   return *this;
226 }
227 
SetTweenType(gfx::Tween::Type tween_type)228 AnimatingLayoutManager& AnimatingLayoutManager::SetTweenType(
229     gfx::Tween::Type tween_type) {
230   tween_type_ = tween_type;
231   if (animation_delegate_)
232     animation_delegate_->UpdateAnimationParameters();
233   return *this;
234 }
235 
SetOrientation(LayoutOrientation orientation)236 AnimatingLayoutManager& AnimatingLayoutManager::SetOrientation(
237     LayoutOrientation orientation) {
238   if (orientation_ != orientation) {
239     orientation_ = orientation;
240     ResetLayout();
241   }
242   return *this;
243 }
244 
SetDefaultFadeMode(FadeInOutMode default_fade_mode)245 AnimatingLayoutManager& AnimatingLayoutManager::SetDefaultFadeMode(
246     FadeInOutMode default_fade_mode) {
247   default_fade_mode_ = default_fade_mode;
248   return *this;
249 }
250 
ResetLayout()251 void AnimatingLayoutManager::ResetLayout() {
252   if (!target_layout_manager())
253     return;
254   ResetLayoutToTargetSize();
255   InvalidateHost(false);
256 }
257 
FadeOut(View * child_view)258 void AnimatingLayoutManager::FadeOut(View* child_view) {
259   DCHECK(child_view);
260   DCHECK(child_view->parent());
261   DCHECK_EQ(host_view(), child_view->parent());
262 
263   // If the view in question is already incapable of being visible, either:
264   // 1. the view wasn't capable of being visible in the first place
265   // 2. the view is already invisible because the layout has chosen to hide it
266   // In either case, it is generally useful to recalculate the layout just in
267   // case the caller has made other changes that won't directly cause a layout -
268   // for example, the user has changed a layout-affecting class property. Worst
269   // case this ends up being a slightly costly no-op but we don't expect this
270   // method to be called very often.
271   if (!CanBeVisible(child_view)) {
272     InvalidateHost(true);
273     return;
274   }
275 
276   // Indicate that the view should become hidden in the layout without
277   // immediately changing its visibility. Instead, this triggers an animation
278   // which results in the view being hidden.
279   //
280   // This method is typically only called from View and has a private final
281   // implementation in LayoutManagerBase so we have to cast to call it.
282   static_cast<LayoutManager*>(this)->ViewVisibilitySet(
283       host_view(), child_view, child_view->GetVisible(), false);
284 }
285 
FadeIn(View * child_view)286 void AnimatingLayoutManager::FadeIn(View* child_view) {
287   DCHECK(child_view);
288   DCHECK(child_view->parent());
289   DCHECK_EQ(host_view(), child_view->parent());
290 
291   // If the view in question is already capable of being visible, either:
292   // 1. the view is already visible so this is a no-op
293   // 2. the view is not visible because the target layout has chosen to hide it
294   // In either case, it is generally useful to recalculate the layout just in
295   // case the caller has made other changes that won't directly cause a layout -
296   // for example, the user has changed a layout-affecting class property. Worst
297   // case this ends up being a slightly costly no-op but we don't expect this
298   // method to be called very often.
299   if (CanBeVisible(child_view)) {
300     InvalidateHost(true);
301     return;
302   }
303 
304   // Indicate that the view should become visible in the layout without
305   // immediately changing its visibility. Instead, this triggers an animation
306   // which results in the view being shown.
307   //
308   // This method is typically only called from View and has a private final
309   // implementation in LayoutManagerBase so we have to cast to call it.
310   static_cast<LayoutManager*>(this)->ViewVisibilitySet(
311       host_view(), child_view, child_view->GetVisible(), true);
312 }
313 
AddObserver(Observer * observer)314 void AnimatingLayoutManager::AddObserver(Observer* observer) {
315   if (!observers_.HasObserver(observer))
316     observers_.AddObserver(observer);
317 }
318 
RemoveObserver(Observer * observer)319 void AnimatingLayoutManager::RemoveObserver(Observer* observer) {
320   if (observers_.HasObserver(observer))
321     observers_.RemoveObserver(observer);
322 }
323 
HasObserver(Observer * observer) const324 bool AnimatingLayoutManager::HasObserver(Observer* observer) const {
325   return observers_.HasObserver(observer);
326 }
327 
GetPreferredSize(const View * host) const328 gfx::Size AnimatingLayoutManager::GetPreferredSize(const View* host) const {
329   if (!target_layout_manager())
330     return gfx::Size();
331 
332   return should_animate_bounds_
333              ? current_layout_.host_size
334              : target_layout_manager()->GetPreferredSize(host);
335 }
336 
GetMinimumSize(const View * host) const337 gfx::Size AnimatingLayoutManager::GetMinimumSize(const View* host) const {
338   if (!target_layout_manager())
339     return gfx::Size();
340   // TODO(dfried): consider cases where the minimum size might not be just the
341   // minimum size of the embedded layout.
342   gfx::Size minimum_size = target_layout_manager()->GetMinimumSize(host);
343   if (should_animate_bounds_)
344     minimum_size.SetToMin(current_layout_.host_size);
345   return minimum_size;
346 }
347 
GetPreferredHeightForWidth(const View * host,int width) const348 int AnimatingLayoutManager::GetPreferredHeightForWidth(const View* host,
349                                                        int width) const {
350   if (!target_layout_manager())
351     return 0;
352 
353   // TODO(dfried): revisit this computation.
354   return should_animate_bounds_
355              ? current_layout_.host_size.height()
356              : target_layout_manager()->GetPreferredHeightForWidth(host, width);
357 }
358 
GetChildViewsInPaintOrder(const View * host) const359 std::vector<View*> AnimatingLayoutManager::GetChildViewsInPaintOrder(
360     const View* host) const {
361   DCHECK_EQ(host_view(), host);
362 
363   if (!is_animating())
364     return LayoutManagerBase::GetChildViewsInPaintOrder(host);
365 
366   std::vector<View*> result;
367   std::set<View*> fading;
368 
369   // Put all fading views to the front of the list (back of the Z-order).
370   for (const LayoutFadeInfo& fade_info : fade_infos_) {
371     result.push_back(fade_info.child_view);
372     fading.insert(fade_info.child_view);
373   }
374 
375   // Add the result of the views.
376   for (View* child : host->children()) {
377     if (!base::Contains(fading, child))
378       result.push_back(child);
379   }
380 
381   return result;
382 }
383 
OnViewRemoved(View * host,View * view)384 bool AnimatingLayoutManager::OnViewRemoved(View* host, View* view) {
385   // Remove any fade infos corresponding to the removed view.
386   base::EraseIf(fade_infos_, [view](const LayoutFadeInfo& fade_info) {
387     return fade_info.child_view == view;
388   });
389 
390   // Remove any elements in the current layout corresponding to the removed
391   // view.
392   base::EraseIf(current_layout_.child_layouts,
393                 [view](const ChildLayout& child_layout) {
394                   return child_layout.child_view == view;
395                 });
396 
397   return LayoutManagerBase::OnViewRemoved(host, view);
398 }
399 
PostOrQueueAction(base::OnceClosure action)400 void AnimatingLayoutManager::PostOrQueueAction(base::OnceClosure action) {
401   queued_actions_.push_back(std::move(action));
402   if (!is_animating())
403     PostQueuedActions();
404 }
405 
GetDefaultFlexRule() const406 FlexRule AnimatingLayoutManager::GetDefaultFlexRule() const {
407   return base::BindRepeating(&AnimatingLayoutManager::DefaultFlexRuleImpl,
408                              base::Unretained(this));
409 }
410 
411 gfx::AnimationContainer*
GetAnimationContainerForTesting()412 AnimatingLayoutManager::GetAnimationContainerForTesting() {
413   DCHECK(animation_delegate_);
414   animation_delegate_->MakeReadyForAnimation();
415   DCHECK(animation_delegate_->ready_to_animate());
416   return animation_delegate_->container();
417 }
418 
EnableAnimationForTesting()419 void AnimatingLayoutManager::EnableAnimationForTesting() {
420   DCHECK(animation_delegate_);
421   animation_delegate_->MakeReadyForAnimation();
422   DCHECK(animation_delegate_->ready_to_animate());
423 }
424 
CalculateProposedLayout(const SizeBounds & size_bounds) const425 ProposedLayout AnimatingLayoutManager::CalculateProposedLayout(
426     const SizeBounds& size_bounds) const {
427   // This class directly overrides Layout() so GetProposedLayout() and
428   // CalculateProposedLayout() are not called.
429   NOTREACHED();
430   return ProposedLayout();
431 }
432 
OnInstalled(View * host)433 void AnimatingLayoutManager::OnInstalled(View* host) {
434   DCHECK(!animation_delegate_);
435   animation_delegate_ = std::make_unique<AnimationDelegate>(this);
436 }
437 
OnLayoutChanged()438 void AnimatingLayoutManager::OnLayoutChanged() {
439   // This replaces the normal behavior of clearing cached layouts.
440   RecalculateTarget();
441 }
442 
LayoutImpl()443 void AnimatingLayoutManager::LayoutImpl() {
444   // Changing the size of a view directly will lead to a layout call rather
445   // than an invalidation. This should reset the layout (but see the note in
446   // RecalculateTarget() below).
447   const gfx::Size host_size = host_view()->size();
448   if (should_animate_bounds_) {
449     // Reset the layout immediately if the current or target layout exceeds the
450     // host size or the available space.
451     const SizeBounds available_size = GetAvailableHostSize();
452     const base::Optional<int> bounds_main =
453         GetMainAxis(orientation(), available_size);
454     const int host_main = GetMainAxis(orientation(), host_size);
455     const int current_main =
456         GetMainAxis(orientation(), current_layout_.host_size);
457     if (current_main > host_main ||
458         (bounds_main && current_main > *bounds_main)) {
459       last_available_host_size_ = available_size;
460       ResetLayoutToSize(host_size);
461     } else if (available_size != last_available_host_size_) {
462       // May need to re-trigger animation if our bounds were relaxed; let us
463       // expand into the new available space.
464       RecalculateTarget();
465     }
466 
467     // Verify that the last available size has been updated.
468     DCHECK_EQ(available_size, last_available_host_size_);
469 
470   } else if (!cached_layout_size() || host_size != *cached_layout_size()) {
471     // Host size changed, so reset the layout.
472     ResetLayoutToTargetSize();
473   }
474 
475   ApplyLayout(current_layout_);
476 
477   // Send animating stopped events on layout so the current layout during the
478   // event represents the final state instead of an intermediate state.
479   if (is_animating_ && current_offset_ == 1.0)
480     OnAnimationEnded();
481 }
482 
OnAnimationEnded()483 void AnimatingLayoutManager::OnAnimationEnded() {
484   DCHECK(is_animating_);
485   is_animating_ = false;
486   fade_infos_.clear();
487   PostQueuedActions();
488   NotifyIsAnimatingChanged();
489 }
490 
ResetLayoutToTargetSize()491 void AnimatingLayoutManager::ResetLayoutToTargetSize() {
492   ResetLayoutToSize(GetAvailableTargetLayoutSize());
493 }
494 
ResetLayoutToSize(const gfx::Size & target_size)495 void AnimatingLayoutManager::ResetLayoutToSize(const gfx::Size& target_size) {
496   if (animation_delegate_)
497     animation_delegate_->Reset();
498 
499   ResolveFades();
500 
501   target_layout_ = target_layout_manager()->GetProposedLayout(target_size);
502   current_layout_ = target_layout_;
503   starting_layout_ = current_layout_;
504   fade_infos_.clear();
505   current_offset_ = 1.0;
506   set_cached_layout_size(target_size);
507 
508   if (is_animating_)
509     OnAnimationEnded();
510 }
511 
RecalculateTarget()512 bool AnimatingLayoutManager::RecalculateTarget() {
513   constexpr double kResetAnimationThreshold = 0.8;
514 
515   if (!target_layout_manager())
516     return false;
517 
518   if (!cached_layout_size() || !animation_delegate_ ||
519       !animation_delegate_->ready_to_animate()) {
520     ResetLayoutToTargetSize();
521     return true;
522   }
523 
524   const gfx::Size target_size = GetAvailableTargetLayoutSize();
525 
526   // For layouts that are confined to available space, changing the available
527   // space causes a fresh layout, not an animation.
528   // TODO(dfried): define a way for views to animate into and out of empty
529   // space as adjacent child views appear/disappear. This will be useful in
530   // animating tab titles, which currently slide over when the favicon
531   // disappears.
532   if (!should_animate_bounds_ && *cached_layout_size() != target_size) {
533     ResetLayoutToSize(target_size);
534     return true;
535   }
536 
537   set_cached_layout_size(target_size);
538 
539   // If there has been no appreciable change in layout, there's no reason to
540   // start or update an animation.
541   const ProposedLayout proposed_layout =
542       target_layout_manager()->GetProposedLayout(target_size);
543   if (target_layout_ == proposed_layout)
544     return false;
545 
546   target_layout_ = proposed_layout;
547   if (current_offset_ > kResetAnimationThreshold) {
548     starting_layout_ = current_layout_;
549     starting_offset_ = 0.0;
550     current_offset_ = 0.0;
551     animation_delegate_->Animate();
552     if (!is_animating_) {
553       is_animating_ = true;
554       NotifyIsAnimatingChanged();
555     }
556   } else if (current_offset_ > starting_offset_) {
557     // Only update the starting layout if the animation has progressed. This has
558     // the effect of "batching up" changes that all happen on the same frame,
559     // keeping the same starting point. (A common example of this is multiple
560     // child views' visibility changing.)
561     starting_layout_ = current_layout_;
562     starting_offset_ = current_offset_;
563   }
564   CalculateFadeInfos();
565   UpdateCurrentLayout(0.0);
566 
567   return true;
568 }
569 
AnimateTo(double value)570 void AnimatingLayoutManager::AnimateTo(double value) {
571   DCHECK_GE(value, 0.0);
572   DCHECK_LE(value, 1.0);
573   DCHECK_GE(value, starting_offset_);
574   DCHECK_GE(value, current_offset_);
575   if (current_offset_ == value)
576     return;
577   current_offset_ = value;
578   const double percent =
579       (current_offset_ - starting_offset_) / (1.0 - starting_offset_);
580   UpdateCurrentLayout(percent);
581   InvalidateHost(false);
582 }
583 
NotifyIsAnimatingChanged()584 void AnimatingLayoutManager::NotifyIsAnimatingChanged() {
585   for (auto& observer : observers_)
586     observer.OnLayoutIsAnimatingChanged(this, is_animating());
587 }
588 
RunQueuedActions()589 void AnimatingLayoutManager::RunQueuedActions() {
590   run_queued_actions_is_pending_ = false;
591   std::vector<base::OnceClosure> actions = std::move(queued_actions_to_run_);
592   for (auto& action : actions)
593     std::move(action).Run();
594 }
595 
PostQueuedActions()596 void AnimatingLayoutManager::PostQueuedActions() {
597   // Move queued actions over to actions that should run during the next
598   // PostTask(). This prevents a race between old PostTask() calls and new
599   // delayed actions. See the header for more detail.
600   for (auto& action : queued_actions_)
601     queued_actions_to_run_.push_back(std::move(action));
602   queued_actions_.clear();
603 
604   // Early return to prevent multiple RunQueuedAction() tasks.
605   if (run_queued_actions_is_pending_)
606     return;
607 
608   // Post to self (instead of posting the queued actions directly) which lets
609   // us:
610   // * Keep "AnimatingLayoutManager::RunDelayedActions" in the stack frame.
611   // * Tie the task lifetimes to AnimatingLayoutManager.
612   run_queued_actions_is_pending_ =
613       base::ThreadTaskRunnerHandle::Get()->PostTask(
614           FROM_HERE, base::BindOnce(&AnimatingLayoutManager::RunQueuedActions,
615                                     weak_ptr_factory_.GetWeakPtr()));
616 }
617 
UpdateCurrentLayout(double percent)618 void AnimatingLayoutManager::UpdateCurrentLayout(double percent) {
619   // This drops out any child view elements that don't exist in the target
620   // layout. We'll add them back in later.
621   current_layout_ =
622       ProposedLayoutBetween(percent, starting_layout_, target_layout_);
623 
624   for (const LayoutFadeInfo& fade_info : fade_infos_) {
625     // This shouldn't happen but we should ensure that with a check.
626     DCHECK_NE(-1, host_view()->GetIndexOf(fade_info.child_view));
627 
628     // Views that were previously fading are animated as normal, so nothing to
629     // do here.
630     if (fade_info.fade_type == LayoutFadeType::kContinuingFade)
631       continue;
632 
633     ChildLayout child_layout;
634 
635     if (percent == 1.0) {
636       // At the end of the animation snap to the final state of the child view.
637       child_layout.child_view = fade_info.child_view;
638       switch (fade_info.fade_type) {
639         case LayoutFadeType::kFadingIn:
640           child_layout.visible = true;
641           child_layout.bounds =
642               Denormalize(orientation(), fade_info.reference_bounds);
643           break;
644         case LayoutFadeType::kFadingOut:
645           child_layout.visible = false;
646           break;
647         case LayoutFadeType::kContinuingFade:
648           NOTREACHED();
649           continue;
650       }
651     } else if (default_fade_mode_ == FadeInOutMode::kHide) {
652       child_layout.child_view = fade_info.child_view;
653       child_layout.visible = false;
654     } else {
655       const double scale_percent =
656           fade_info.fade_type == LayoutFadeType::kFadingIn ? percent
657                                                            : 1.0 - percent;
658 
659       switch (default_fade_mode_) {
660         case FadeInOutMode::kHide:
661           NOTREACHED();
662           break;
663         case FadeInOutMode::kScaleFromMinimum:
664           child_layout = CalculateScaleFade(fade_info, scale_percent,
665                                             /* scale_from_zero */ false);
666           break;
667         case FadeInOutMode::kScaleFromZero:
668           child_layout = CalculateScaleFade(fade_info, scale_percent,
669                                             /* scale_from_zero */ true);
670           break;
671         case FadeInOutMode::kSlideFromLeadingEdge:
672           child_layout = CalculateSlideFade(fade_info, scale_percent,
673                                             /* slide_from_leading */ true);
674           break;
675         case FadeInOutMode::kSlideFromTrailingEdge:
676           child_layout = CalculateSlideFade(fade_info, scale_percent,
677                                             /* slide_from_leading */ false);
678           break;
679       }
680     }
681 
682     ChildLayout* const to_overwrite =
683         FindChildViewInLayout(&current_layout_, fade_info.child_view);
684     if (to_overwrite)
685       *to_overwrite = child_layout;
686     else
687       current_layout_.child_layouts.push_back(child_layout);
688   }
689 }
690 
CalculateFadeInfos()691 void AnimatingLayoutManager::CalculateFadeInfos() {
692   // Save any views that were previously fading so we don't try to key off of
693   // them when calculating leading/trailing edge.
694   std::set<const View*> previously_fading;
695   for (const auto& fade_info : fade_infos_)
696     previously_fading.insert(fade_info.child_view);
697 
698   fade_infos_.clear();
699 
700   struct ChildInfo {
701     base::Optional<size_t> start;
702     NormalizedRect start_bounds;
703     bool start_visible = false;
704     base::Optional<size_t> target;
705     NormalizedRect target_bounds;
706     bool target_visible = false;
707   };
708 
709   std::map<View*, ChildInfo> child_to_info;
710   std::map<int, View*> start_leading_edges;
711   std::map<int, View*> target_leading_edges;
712 
713   // Collect some bookkeping info to prevent linear searches later.
714 
715   for (View* child : host_view()->children()) {
716     if (IsChildIncludedInLayout(child, /* include hidden */ true))
717       child_to_info.emplace(child, ChildInfo());
718   }
719 
720   for (size_t i = 0; i < starting_layout_.child_layouts.size(); ++i) {
721     const auto& child_layout = starting_layout_.child_layouts[i];
722     auto it = child_to_info.find(child_layout.child_view);
723     if (it != child_to_info.end()) {
724       it->second.start = i;
725       it->second.start_bounds = Normalize(orientation(), child_layout.bounds);
726       it->second.start_visible = child_layout.visible;
727     }
728   }
729 
730   for (size_t i = 0; i < target_layout_.child_layouts.size(); ++i) {
731     const auto& child_layout = target_layout_.child_layouts[i];
732     auto it = child_to_info.find(child_layout.child_view);
733     if (it != child_to_info.end()) {
734       it->second.target = i;
735       it->second.target_bounds = Normalize(orientation(), child_layout.bounds);
736       it->second.target_visible = child_layout.visible;
737     }
738   }
739 
740   for (View* child : host_view()->children()) {
741     const auto& index = child_to_info[child];
742     if (index.start_visible && index.target_visible &&
743         !base::Contains(previously_fading, child)) {
744       start_leading_edges.emplace(index.start_bounds.origin_main(), child);
745       target_leading_edges.emplace(index.target_bounds.origin_main(), child);
746     }
747   }
748 
749   // Build the LayoutFadeInfo data.
750 
751   const NormalizedSize start_host_size =
752       Normalize(orientation(), starting_layout_.host_size);
753   const NormalizedSize target_host_size =
754       Normalize(orientation(), target_layout_.host_size);
755 
756   for (View* child : host_view()->children()) {
757     const auto& current = child_to_info[child];
758     if (current.start_visible && !current.target_visible) {
759       LayoutFadeInfo fade_info;
760       fade_info.fade_type = LayoutFadeType::kFadingOut;
761       fade_info.child_view = child;
762       fade_info.reference_bounds = current.start_bounds;
763       auto next =
764           start_leading_edges.upper_bound(current.start_bounds.origin_main());
765       if (next == start_leading_edges.end()) {
766         fade_info.next_view = nullptr;
767         fade_info.offsets.set_trailing(start_host_size.main() -
768                                        current.start_bounds.max_main());
769       } else {
770         fade_info.next_view = next->second;
771         fade_info.offsets.set_trailing(next->first -
772                                        current.start_bounds.max_main());
773       }
774       if (next == start_leading_edges.begin()) {
775         fade_info.prev_view = nullptr;
776         fade_info.offsets.set_leading(current.start_bounds.origin_main());
777       } else {
778         auto prev = next;
779         --prev;
780         const auto& prev_info = child_to_info[prev->second];
781         fade_info.prev_view = prev->second;
782         fade_info.offsets.set_leading(current.start_bounds.origin_main() -
783                                       prev_info.start_bounds.max_main());
784       }
785       fade_infos_.push_back(fade_info);
786     } else if (!current.start_visible && current.target_visible) {
787       LayoutFadeInfo fade_info;
788       fade_info.fade_type = LayoutFadeType::kFadingIn;
789       fade_info.child_view = child;
790       fade_info.reference_bounds = current.target_bounds;
791       auto next =
792           target_leading_edges.upper_bound(current.target_bounds.origin_main());
793       if (next == target_leading_edges.end()) {
794         fade_info.next_view = nullptr;
795         fade_info.offsets.set_trailing(target_host_size.main() -
796                                        current.target_bounds.max_main());
797       } else {
798         fade_info.next_view = next->second;
799         fade_info.offsets.set_trailing(next->first -
800                                        current.target_bounds.max_main());
801       }
802       if (next == target_leading_edges.begin()) {
803         fade_info.prev_view = nullptr;
804         fade_info.offsets.set_leading(current.target_bounds.origin_main());
805       } else {
806         auto prev = next;
807         --prev;
808         const auto& prev_info = child_to_info[prev->second];
809         fade_info.prev_view = prev->second;
810         fade_info.offsets.set_leading(current.target_bounds.origin_main() -
811                                       prev_info.target_bounds.max_main());
812       }
813       fade_infos_.push_back(fade_info);
814     } else if (base::Contains(previously_fading, child)) {
815       // Capture the fact that this view was fading as part of an animation that
816       // was interrupted. (It is therefore technically still fading.) This
817       // status goes away when the animation ends.
818       LayoutFadeInfo fade_info;
819       fade_info.fade_type = LayoutFadeType::kContinuingFade;
820       fade_info.child_view = child;
821       // No reference bounds or offsets since we'll use the normal animation
822       // pathway for this view.
823       fade_infos_.push_back(fade_info);
824     }
825   }
826 }
827 
ResolveFades()828 void AnimatingLayoutManager::ResolveFades() {
829   // Views that need faded out are views which were were fading out previously
830   // because they were set to not be visible, either by calling SetVisible() or
831   // FadeOut(). Those views will not be included in the new layout but may not
832   // have been allowed to become invisible yet because of the fade-out
833   // animation. Even in the case of FadeInOutMode::kHide, if no frames of the
834   // animation have run, the relevant view may still be visible.
835   for (const LayoutFadeInfo& fade_info : fade_infos_) {
836     View* const child = fade_info.child_view;
837     if (fade_info.fade_type == LayoutFadeType::kFadingOut &&
838         host_view()->GetIndexOf(child) >= 0 &&
839         !IsChildViewIgnoredByLayout(child) && !IsChildIncludedInLayout(child)) {
840       SetViewVisibility(child, false);
841     }
842   }
843 }
844 
CalculateScaleFade(const LayoutFadeInfo & fade_info,double scale_percent,bool scale_from_zero) const845 ChildLayout AnimatingLayoutManager::CalculateScaleFade(
846     const LayoutFadeInfo& fade_info,
847     double scale_percent,
848     bool scale_from_zero) const {
849   ChildLayout child_layout;
850 
851   int leading_reference_point = 0;
852   if (fade_info.prev_view) {
853     // Since prev/next view is always a view in the start and target layouts, it
854     // should also be in the current layout. Therefore this should never return
855     // null.
856     const ChildLayout* const prev_layout =
857         FindChildViewInLayout(current_layout_, fade_info.prev_view);
858     leading_reference_point =
859         Normalize(orientation(), prev_layout->bounds).max_main();
860   }
861   leading_reference_point += fade_info.offsets.leading();
862 
863   int trailing_reference_point;
864   if (fade_info.next_view) {
865     // Since prev/next view is always a view in the start and target layouts, it
866     // should also be in the current layout. Therefore this should never return
867     // null.
868     const ChildLayout* const next_layout =
869         FindChildViewInLayout(current_layout_, fade_info.next_view);
870     trailing_reference_point =
871         Normalize(orientation(), next_layout->bounds).origin_main();
872   } else {
873     trailing_reference_point =
874         Normalize(orientation(), current_layout_.host_size).main();
875   }
876   trailing_reference_point -= fade_info.offsets.trailing();
877 
878   const int new_size =
879       std::min(int{scale_percent * fade_info.reference_bounds.size_main()},
880                trailing_reference_point - leading_reference_point);
881 
882   child_layout.child_view = fade_info.child_view;
883   if (new_size > 0 &&
884       (scale_from_zero ||
885        new_size >=
886            Normalize(orientation(), fade_info.child_view->GetMinimumSize())
887                .main())) {
888     child_layout.visible = true;
889     NormalizedRect new_bounds = fade_info.reference_bounds;
890     switch (fade_info.fade_type) {
891       case LayoutFadeType::kFadingIn:
892         new_bounds.set_origin_main(leading_reference_point);
893         break;
894       case LayoutFadeType::kFadingOut:
895         new_bounds.set_origin_main(trailing_reference_point - new_size);
896         break;
897       case LayoutFadeType::kContinuingFade:
898         NOTREACHED();
899         break;
900     }
901     new_bounds.set_size_main(new_size);
902     child_layout.bounds = Denormalize(orientation(), new_bounds);
903   }
904 
905   return child_layout;
906 }
907 
CalculateSlideFade(const LayoutFadeInfo & fade_info,double scale_percent,bool slide_from_leading) const908 ChildLayout AnimatingLayoutManager::CalculateSlideFade(
909     const LayoutFadeInfo& fade_info,
910     double scale_percent,
911     bool slide_from_leading) const {
912   // Fall back to kScaleFromMinimum if there is no edge to slide out from.
913   if (!fade_info.prev_view && !fade_info.next_view)
914     return CalculateScaleFade(fade_info, scale_percent, false);
915 
916   // Slide from the other direction if against the edge of the host view.
917   if (slide_from_leading && !fade_info.prev_view)
918     slide_from_leading = false;
919   else if (!slide_from_leading && !fade_info.next_view)
920     slide_from_leading = true;
921 
922   NormalizedRect new_bounds = fade_info.reference_bounds;
923 
924   // Determine which layout the sliding view will be completely faded in.
925   const ProposedLayout* fully_faded_layout;
926   switch (fade_info.fade_type) {
927     case LayoutFadeType::kFadingIn:
928       fully_faded_layout = &starting_layout_;
929       break;
930     case LayoutFadeType::kFadingOut:
931       fully_faded_layout = &target_layout_;
932       break;
933     case LayoutFadeType::kContinuingFade:
934       NOTREACHED();
935       break;
936   }
937 
938   if (slide_from_leading) {
939     // Get the layout info for the leading child.
940     const ChildLayout* const leading_child =
941         FindChildViewInLayout(*fully_faded_layout, fade_info.prev_view);
942 
943     // This is the right side of the leading control that will eclipse the
944     // sliding view at the start/end of the animation.
945     const int initial_trailing =
946         Normalize(orientation(), leading_child->bounds).max_main();
947 
948     // Interpolate between initial and final trailing edge.
949     const int new_trailing = gfx::Tween::IntValueBetween(
950         scale_percent, initial_trailing, new_bounds.max_main());
951 
952     // Adjust the bounding rectangle of the view.
953     new_bounds.Offset(new_trailing - new_bounds.max_main(), 0);
954 
955   } else {
956     // Get the layout info for the trailing child.
957     const ChildLayout* const trailing_child =
958         FindChildViewInLayout(*fully_faded_layout, fade_info.next_view);
959 
960     // This is the left side of the trailing control that will eclipse the
961     // sliding view at the start/end of the animation.
962     const int initial_leading =
963         Normalize(orientation(), trailing_child->bounds).origin_main();
964 
965     // Interpolate between initial and final leading edge.
966     const int new_leading = gfx::Tween::IntValueBetween(
967         scale_percent, initial_leading, new_bounds.origin_main());
968 
969     // Adjust the bounding rectangle of the view.
970     new_bounds.Offset(new_leading - new_bounds.origin_main(), 0);
971   }
972 
973   // Actual bounds are a linear interpolation between starting and reference
974   // bounds.
975   ChildLayout child_layout;
976   child_layout.child_view = fade_info.child_view;
977   child_layout.visible = true;
978   child_layout.bounds = Denormalize(orientation(), new_bounds);
979 
980   return child_layout;
981 }
982 
983 // Returns the space in which to calculate the target layout.
GetAvailableTargetLayoutSize()984 gfx::Size AnimatingLayoutManager::GetAvailableTargetLayoutSize() {
985   if (!should_animate_bounds_)
986     return host_view()->size();
987 
988   const SizeBounds bounds = GetAvailableHostSize();
989   last_available_host_size_ = bounds;
990   const gfx::Size preferred_size =
991       target_layout_manager()->GetPreferredSize(host_view());
992   if (!bounds.width() || *bounds.width() > preferred_size.width()) {
993     return gfx::Size(preferred_size.width(),
994                      bounds.height()
995                          ? std::min(preferred_size.height(), *bounds.height())
996                          : preferred_size.height());
997   }
998 
999   const int height = target_layout_manager()->GetPreferredHeightForWidth(
1000       host_view(), *bounds.width());
1001   return gfx::Size(*bounds.width(), bounds.height()
1002                                         ? std::min(height, *bounds.height())
1003                                         : height);
1004 }
1005 
1006 // static
DefaultFlexRuleImpl(const AnimatingLayoutManager * animating_layout,const View * view,const SizeBounds & size_bounds)1007 gfx::Size AnimatingLayoutManager::DefaultFlexRuleImpl(
1008     const AnimatingLayoutManager* animating_layout,
1009     const View* view,
1010     const SizeBounds& size_bounds) {
1011   DCHECK_EQ(view->GetLayoutManager(), animating_layout);
1012 
1013   // This is the current preferred size, which takes animation into account.
1014   const gfx::Size preferred_size = animating_layout->GetPreferredSize(view);
1015 
1016   // Does the preferred size fit in the bounds? If so, return the preferred
1017   // size. Note that the *target* size might not fit in the bounds, but we'll
1018   // recalculate that the next time we lay out.
1019   if (CanFitInBounds(preferred_size, size_bounds))
1020     return preferred_size;
1021 
1022   // Special case - if we're being asked for a zero-size layout we'll return the
1023   // minimum size of the layout. This is because we're being probed for how
1024   // small we can get, not being asked for an actual size.
1025   const base::Optional<int> bounds_main =
1026       GetMainAxis(animating_layout->orientation(), size_bounds);
1027   if (bounds_main && *bounds_main <= 0)
1028     return animating_layout->GetMinimumSize(view);
1029 
1030   // We know our current size does not fit into the bounds being given to us.
1031   // This is going to force a snap to a new size, which will be the ideal size
1032   // of the target layout in the provided space.
1033   const LayoutManagerBase* const target_layout =
1034       animating_layout->target_layout_manager();
1035 
1036   // Easiest case is that the target layout's preferred size *does* fit, in
1037   // which case we can use that.
1038   const gfx::Size target_preferred = target_layout->GetPreferredSize(view);
1039   if (CanFitInBounds(target_preferred, size_bounds))
1040     return target_preferred;
1041 
1042   // We know that at least one of the width and height are constrained, so we
1043   // need to ask the target layout how large it wants to be in the space
1044   // provided.
1045   gfx::Size size;
1046   if (size_bounds.width() && size_bounds.height()) {
1047     // Both width and height are specified.  Constraining the width may change
1048     // the desired height, so we can't just blindly return the minimum in both
1049     // dimensions.  Instead, query the target layout in the constrained space
1050     // and return its size.
1051     size = gfx::Size(*size_bounds.width(), *size_bounds.height());
1052   } else if (size_bounds.width()) {
1053     // The width is specified and too small.  Use the height-for-width
1054     // calculation.
1055     // TODO(dfried): This should be rare, but it is also inefficient. See if we
1056     // can't add an alternative to GetPreferredHeightForWidth() that actually
1057     // calculates the layout in this space so we don't have to do it twice.
1058     const int width = *size_bounds.width();
1059     size = gfx::Size(width,
1060                      target_layout->GetPreferredHeightForWidth(view, width));
1061   } else {
1062     DCHECK(size_bounds.height());
1063     // The height is specified and too small.  Fortunately the height of a
1064     // layout can't (shouldn't?) affect its width.
1065     size = gfx::Size(target_preferred.width(), *size_bounds.height());
1066   }
1067 
1068   return target_layout->GetProposedLayout(size).host_size;
1069 }
1070 
1071 }  // namespace views
1072