Commit 2d9a7fc8 authored by Tetsui Ohkubo's avatar Tetsui Ohkubo Committed by Commit Bot

NewMessageListView: Implement slide out animation.

This CL adds sliding out animation to NewMessageListView when
notifications are removed. Animation on removal consists of two stages:
SLIDE_OUT and MOVE_DOWN. Previously, MOVE_DOWN was implemented in
https://crrev.com/c/1280087 .

Video: http://shortn/_HiEcuNpheU

UnifiedMessageListView will replace MessageListView. It's behind a flag:
--enable-features=NewMessageListView

Design doc: go/chrome-popup-refactoring

TEST=UnifiedMessageListViewTest
BUG=769219

Change-Id: I8abe9eedeeb7bd1bcef09e5cb5a072a0ab88fee2
Reviewed-on: https://chromium-review.googlesource.com/c/1282628
Commit-Queue: Tetsui Ohkubo <tetsui@chromium.org>
Reviewed-by: default avatarYoshiki Iguchi <yoshiki@chromium.org>
Cr-Commit-Position: refs/heads/master@{#600639}
parent 895a0d76
......@@ -175,9 +175,12 @@ void NewUnifiedMessageCenterView::ButtonPressed(views::Button* sender,
base::RecordAction(
base::UserMetricsAction("StatusArea_Notifications_ClearAll"));
// TODO(tetsui): Support Clear All animation.
message_list_view_->set_enable_animation(false);
message_center::MessageCenter::Get()->RemoveAllNotifications(
true /* by_user */,
message_center::MessageCenter::RemoveType::NON_PINNED);
message_list_view_->set_enable_animation(true);
}
void NewUnifiedMessageCenterView::OnWillChangeFocus(views::View* before,
......
......@@ -154,6 +154,7 @@ TEST_F(NewUnifiedMessageCenterViewTest, AddAndRemoveNotification) {
EXPECT_TRUE(message_center_view()->visible());
MessageCenter::Get()->RemoveNotification(id0, true /* by_user */);
AnimateToEnd();
AnimateToMiddle();
EXPECT_TRUE(message_center_view()->visible());
AnimateToEnd();
......
......@@ -88,6 +88,7 @@ class UnifiedMessageListView::MessageViewContainer
gfx::Rect start_bounds() const { return start_bounds_; }
gfx::Rect ideal_bounds() const { return ideal_bounds_; }
bool is_removed() const { return is_removed_; }
void set_start_bounds(const gfx::Rect& start_bounds) {
start_bounds_ = start_bounds;
......@@ -97,6 +98,8 @@ class UnifiedMessageListView::MessageViewContainer
ideal_bounds_ = ideal_bounds;
}
void set_is_removed() { is_removed_ = true; }
private:
// The bounds that the container starts animating from. If not animating, it's
// ignored.
......@@ -106,6 +109,10 @@ class UnifiedMessageListView::MessageViewContainer
// actual bounds().
gfx::Rect ideal_bounds_;
// True when the notification is removed and during SLIDE_OUT animation.
// Unused if |state_| is not SLIDE_OUT.
bool is_removed_ = false;
MessageView* const message_view_;
NotificationSwipeControlView* const control_view_;
......@@ -198,16 +205,25 @@ void UnifiedMessageListView::OnNotificationAdded(const std::string& id) {
void UnifiedMessageListView::OnNotificationRemoved(const std::string& id,
bool by_user) {
ResetBounds();
for (int i = 0; i < child_count(); ++i) {
auto* view = GetContainer(i);
if (view->GetNotificationId() == id) {
delete view;
view->set_is_removed();
break;
}
}
if (!enable_animation_) {
ResetBounds();
return;
}
UpdateBorders();
UpdateBounds();
state_ = State::SLIDE_OUT;
animation_->Start();
}
......@@ -243,6 +259,17 @@ void UnifiedMessageListView::AnimationEnded(const gfx::Animation* animation) {
// This is also called from AnimationCanceled().
animation_->SetCurrentValue(1.0);
PreferredSizeChanged();
if (state_ == State::SLIDE_OUT) {
DeleteRemovedNotifications();
UpdateBorders();
UpdateBounds();
state_ = State::MOVE_DOWN;
animation_->Start();
} else if (state_ == State::MOVE_DOWN) {
state_ = State::IDLE;
}
}
void UnifiedMessageListView::AnimationProgressed(
......@@ -295,7 +322,10 @@ void UnifiedMessageListView::UpdateBounds() {
auto* view = GetContainer(i);
const int height = view->GetHeightForWidth(kTrayMenuWidth);
view->set_start_bounds(view->ideal_bounds());
view->set_ideal_bounds(gfx::Rect(0, y, kTrayMenuWidth, height));
view->set_ideal_bounds(
view->is_removed()
? gfx::Rect(kTrayMenuWidth, y, kTrayMenuWidth, height)
: gfx::Rect(0, y, kTrayMenuWidth, height));
y += height;
}
......@@ -304,13 +334,25 @@ void UnifiedMessageListView::UpdateBounds() {
}
void UnifiedMessageListView::ResetBounds() {
DeleteRemovedNotifications();
UpdateBorders();
UpdateBounds();
state_ = State::IDLE;
if (animation_->is_animating())
animation_->End();
else
PreferredSizeChanged();
}
void UnifiedMessageListView::DeleteRemovedNotifications() {
for (int i = 0; i < child_count(); ++i) {
auto* view = GetContainer(i);
if (view->is_removed())
delete view;
}
}
double UnifiedMessageListView::GetCurrentValue() const {
return gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT,
animation_->GetCurrentValue());
......
......@@ -68,6 +68,10 @@ class ASH_EXPORT UnifiedMessageListView
void AnimationProgressed(const gfx::Animation* animation) override;
void AnimationCanceled(const gfx::Animation* animation) override;
void set_enable_animation(bool enable_animation) {
enable_animation_ = enable_animation;
}
protected:
// Virtual for testing.
virtual message_center::MessageView* CreateMessageView(
......@@ -78,6 +82,20 @@ class ASH_EXPORT UnifiedMessageListView
friend class UnifiedMessageListViewTest;
class MessageViewContainer;
// UnifiedMessageListView always runs single animation at one time. When
// |state_| is IDLE, animation_->is_animating() is always false and vice
// versa.
enum class State {
// No animation is running.
IDLE,
// Sliding out a removed notification. It will transition to MOVE_DOWN.
SLIDE_OUT,
// Moving down notifications.
MOVE_DOWN
};
MessageViewContainer* GetContainer(int index);
const MessageViewContainer* GetContainer(int index) const;
......@@ -101,6 +119,9 @@ class ASH_EXPORT UnifiedMessageListView
// |final_bounds|.
void ResetBounds();
// Deletes all the MessageViewContainer marked as |is_removed|.
void DeleteRemovedNotifications();
NewUnifiedMessageCenterView* const message_center_view_;
// If true, ChildPreferredSizeChanged() will be ignored. This is used in
......@@ -112,6 +133,8 @@ class ASH_EXPORT UnifiedMessageListView
// implicit animation.
const std::unique_ptr<gfx::LinearAnimation> animation_;
State state_ = State::IDLE;
// The height the UnifiedMessageListView starts animating from. If not
// animating, it's ignored.
int start_height_ = 0;
......@@ -120,6 +143,9 @@ class ASH_EXPORT UnifiedMessageListView
// as height().
int ideal_height_ = 0;
// If false, disables animation on notification removal.
bool enable_animation_ = true;
DISALLOW_COPY_AND_ASSIGN(UnifiedMessageListView);
};
......
......@@ -112,18 +112,26 @@ class UnifiedMessageListViewTest : public AshTestBase,
}
void AnimateToMiddle() {
EXPECT_TRUE(IsAnimating());
message_list_view()->animation_->SetCurrentValue(0.5);
message_list_view()->AnimationProgressed(
message_list_view()->animation_.get());
}
void AnimateToEnd() { message_list_view()->animation_->End(); }
void AnimateToEnd() {
EXPECT_TRUE(IsAnimating());
message_list_view()->animation_->End();
}
void AnimateUntilIdle() {
while (message_list_view()->animation_->is_animating())
message_list_view()->animation_->End();
}
bool IsAnimating() { return message_list_view()->animation_->is_animating(); }
int GetMessageViewCount() { return message_list_view()->child_count(); }
UnifiedMessageListView* message_list_view() const {
return message_list_view_.get();
}
......@@ -216,7 +224,7 @@ TEST_F(UnifiedMessageListViewTest, RemoveNotification) {
gfx::Rect previous_bounds = GetMessageViewBounds(0);
MessageCenter::Get()->RemoveNotification(id0, true /* by_user */);
AnimateUntilIdle();
EXPECT_EQ(1, size_changed_count());
EXPECT_EQ(3, size_changed_count());
EXPECT_EQ(previous_bounds.y(), GetMessageViewBounds(0).y());
EXPECT_LT(0, message_list_view()->GetPreferredSize().height());
EXPECT_GT(previous_height, message_list_view()->GetPreferredSize().height());
......@@ -226,7 +234,7 @@ TEST_F(UnifiedMessageListViewTest, RemoveNotification) {
MessageCenter::Get()->RemoveNotification(id1, true /* by_user */);
AnimateUntilIdle();
EXPECT_EQ(2, size_changed_count());
EXPECT_EQ(6, size_changed_count());
EXPECT_EQ(0, message_list_view()->GetPreferredSize().height());
}
......@@ -264,6 +272,11 @@ TEST_F(UnifiedMessageListViewTest, RemovingNotificationAnimation) {
gfx::Rect bounds1 = GetMessageViewBounds(1);
MessageCenter::Get()->RemoveNotification(id1, true /* by_user */);
AnimateToMiddle();
gfx::Rect slided_bounds = GetMessageViewBounds(1);
EXPECT_LT(bounds1.x(), slided_bounds.x());
AnimateToEnd();
AnimateToMiddle();
EXPECT_GT(previous_height, message_list_view()->GetPreferredSize().height());
previous_height = message_list_view()->GetPreferredSize().height();
......@@ -274,6 +287,7 @@ TEST_F(UnifiedMessageListViewTest, RemovingNotificationAnimation) {
EXPECT_EQ(bounds1, GetMessageViewBounds(1));
MessageCenter::Get()->RemoveNotification(id2, true /* by_user */);
AnimateToEnd();
AnimateToMiddle();
EXPECT_GT(previous_height, message_list_view()->GetPreferredSize().height());
previous_height = message_list_view()->GetPreferredSize().height();
......@@ -283,6 +297,7 @@ TEST_F(UnifiedMessageListViewTest, RemovingNotificationAnimation) {
EXPECT_EQ(bounds0, GetMessageViewBounds(0));
MessageCenter::Get()->RemoveNotification(id0, true /* by_user */);
AnimateToEnd();
AnimateToMiddle();
EXPECT_GT(previous_height, message_list_view()->GetPreferredSize().height());
previous_height = message_list_view()->GetPreferredSize().height();
......@@ -291,4 +306,32 @@ TEST_F(UnifiedMessageListViewTest, RemovingNotificationAnimation) {
EXPECT_EQ(0, message_list_view()->GetPreferredSize().height());
}
TEST_F(UnifiedMessageListViewTest, DisableAnimation) {
auto id0 = AddNotification();
auto id1 = AddNotification();
CreateMessageListView();
message_list_view()->set_enable_animation(false);
MessageCenter::Get()->RemoveNotification(id0, true /* by_user */);
EXPECT_FALSE(IsAnimating());
}
TEST_F(UnifiedMessageListViewTest, ResetAnimation) {
auto id0 = AddNotification();
auto id1 = AddNotification();
CreateMessageListView();
MessageCenter::Get()->RemoveNotification(id0, true /* by_user */);
EXPECT_TRUE(IsAnimating());
AnimateToMiddle();
// New event resets the animation.
auto id2 = AddNotification();
EXPECT_FALSE(IsAnimating());
EXPECT_EQ(2, GetMessageViewCount());
EXPECT_EQ(id1, GetMessageViewAt(0)->notification_id());
EXPECT_EQ(id2, GetMessageViewAt(1)->notification_id());
}
} // namespace ash
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment