#include "chrome/browser/ui/views/payments/view_stack.h"
#include <memory>
#include <utility>
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/compositor/layer.h"
#include "ui/views/layout/fill_layout.h"
ViewStack::ViewStack()
: slide_in_animator_(std::make_unique<views::BoundsAnimator>(this)),
slide_out_animator_(std::make_unique<views::BoundsAnimator>(this)) {
SetLayoutManager(std::make_unique<views::FillLayout>());
slide_out_animator_->AddObserver(this);
slide_in_animator_->AddObserver(this);
SetPaintToLayer();
layer()->SetMasksToBounds(true);
slide_in_animator_->set_tween_type(gfx::Tween::FAST_OUT_SLOW_IN);
slide_out_animator_->set_tween_type(gfx::Tween::FAST_OUT_SLOW_IN);
}
ViewStack::~ViewStack() = default;
void ViewStack::Push(std::unique_ptr<views::View> view, bool animate) {
gfx::Rect destination = bounds();
destination.set_origin(gfx::Point(0, 0));
if (animate) {
view->SetBounds(width(), 0, width(), height());
} else {
view->SetBoundsRect(destination);
}
view->DeprecatedLayoutImmediately();
stack_.push_back(view.get());
AddChildView(std::move(view));
if (animate) {
slide_in_animator_->AnimateViewTo(stack_.back(), destination);
} else {
HideCoveredViews();
RequestFocus();
}
}
void ViewStack::Pop(bool animate) {
DCHECK_LT(1u,
GetSize());
stack_[GetSize() - 2]->SetVisible(true);
if (animate) {
gfx::Rect destination = bounds();
destination.set_origin(gfx::Point(width(), 0));
slide_out_animator_->AnimateViewTo(stack_.back(), destination);
} else {
RemoveChildViewT(stack_.back());
stack_.pop_back();
}
}
void ViewStack::PopMany(int n, bool animate) {
DCHECK_LT(static_cast<size_t>(n),
GetSize());
size_t pre_size = stack_.size();
for (unsigned i = pre_size - n; i < (pre_size - 1); i++) {
RemoveChildViewT(stack_.at(i));
}
stack_.erase(stack_.end() - n, stack_.end() - 1);
DCHECK_EQ(pre_size - n + 1, stack_.size());
Pop(animate);
}
size_t ViewStack::GetSize() const {
return stack_.size();
}
bool ViewStack::GetCanProcessEventsWithinSubtree() const {
return !slide_in_animator_->IsAnimating() &&
!slide_out_animator_->IsAnimating();
}
void ViewStack::RequestFocus() {
if (top()->GetWidget()) {
top()->RequestFocus();
}
}
void ViewStack::OnBoundsChanged(const gfx::Rect& previous_bounds) {
UpdateAnimatorBounds(slide_in_animator_.get(), GetLocalBounds());
UpdateAnimatorBounds(slide_out_animator_.get(), {{width(), 0}, size()});
}
void ViewStack::HideCoveredViews() {
for (size_t i = 0; i + 1 < GetSize(); i++) {
stack_[i]->SetVisible(false);
}
}
void ViewStack::UpdateAnimatorBounds(views::BoundsAnimator* animator,
const gfx::Rect& target) {
if (animator->IsAnimating()) {
for (views::View* view : stack_) {
if (animator->IsAnimating(view)) {
animator->SetTargetBounds(view, target);
}
}
}
}
void ViewStack::OnBoundsAnimatorDone(views::BoundsAnimator* animator) {
if (animator == slide_out_animator_.get()) {
RemoveChildViewT(stack_.back());
stack_.pop_back();
DCHECK(!stack_.empty()) << "State stack should never be empty";
} else {
CHECK_EQ(animator, slide_in_animator_.get());
HideCoveredViews();
}
RequestFocus();
}
BEGIN_METADATA(ViewStack)
ADD_READONLY_PROPERTY_METADATA(size_t, Size)
END_METADATA