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, ...@@ -175,9 +175,12 @@ void NewUnifiedMessageCenterView::ButtonPressed(views::Button* sender,
base::RecordAction( base::RecordAction(
base::UserMetricsAction("StatusArea_Notifications_ClearAll")); base::UserMetricsAction("StatusArea_Notifications_ClearAll"));
// TODO(tetsui): Support Clear All animation.
message_list_view_->set_enable_animation(false);
message_center::MessageCenter::Get()->RemoveAllNotifications( message_center::MessageCenter::Get()->RemoveAllNotifications(
true /* by_user */, true /* by_user */,
message_center::MessageCenter::RemoveType::NON_PINNED); message_center::MessageCenter::RemoveType::NON_PINNED);
message_list_view_->set_enable_animation(true);
} }
void NewUnifiedMessageCenterView::OnWillChangeFocus(views::View* before, void NewUnifiedMessageCenterView::OnWillChangeFocus(views::View* before,
......
...@@ -154,6 +154,7 @@ TEST_F(NewUnifiedMessageCenterViewTest, AddAndRemoveNotification) { ...@@ -154,6 +154,7 @@ TEST_F(NewUnifiedMessageCenterViewTest, AddAndRemoveNotification) {
EXPECT_TRUE(message_center_view()->visible()); EXPECT_TRUE(message_center_view()->visible());
MessageCenter::Get()->RemoveNotification(id0, true /* by_user */); MessageCenter::Get()->RemoveNotification(id0, true /* by_user */);
AnimateToEnd();
AnimateToMiddle(); AnimateToMiddle();
EXPECT_TRUE(message_center_view()->visible()); EXPECT_TRUE(message_center_view()->visible());
AnimateToEnd(); AnimateToEnd();
......
...@@ -88,6 +88,7 @@ class UnifiedMessageListView::MessageViewContainer ...@@ -88,6 +88,7 @@ class UnifiedMessageListView::MessageViewContainer
gfx::Rect start_bounds() const { return start_bounds_; } gfx::Rect start_bounds() const { return start_bounds_; }
gfx::Rect ideal_bounds() const { return ideal_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) { void set_start_bounds(const gfx::Rect& start_bounds) {
start_bounds_ = start_bounds; start_bounds_ = start_bounds;
...@@ -97,6 +98,8 @@ class UnifiedMessageListView::MessageViewContainer ...@@ -97,6 +98,8 @@ class UnifiedMessageListView::MessageViewContainer
ideal_bounds_ = ideal_bounds; ideal_bounds_ = ideal_bounds;
} }
void set_is_removed() { is_removed_ = true; }
private: private:
// The bounds that the container starts animating from. If not animating, it's // The bounds that the container starts animating from. If not animating, it's
// ignored. // ignored.
...@@ -106,6 +109,10 @@ class UnifiedMessageListView::MessageViewContainer ...@@ -106,6 +109,10 @@ class UnifiedMessageListView::MessageViewContainer
// actual bounds(). // actual bounds().
gfx::Rect ideal_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_; MessageView* const message_view_;
NotificationSwipeControlView* const control_view_; NotificationSwipeControlView* const control_view_;
...@@ -198,16 +205,25 @@ void UnifiedMessageListView::OnNotificationAdded(const std::string& id) { ...@@ -198,16 +205,25 @@ void UnifiedMessageListView::OnNotificationAdded(const std::string& id) {
void UnifiedMessageListView::OnNotificationRemoved(const std::string& id, void UnifiedMessageListView::OnNotificationRemoved(const std::string& id,
bool by_user) { bool by_user) {
ResetBounds();
for (int i = 0; i < child_count(); ++i) { for (int i = 0; i < child_count(); ++i) {
auto* view = GetContainer(i); auto* view = GetContainer(i);
if (view->GetNotificationId() == id) { if (view->GetNotificationId() == id) {
delete view; view->set_is_removed();
break; break;
} }
} }
if (!enable_animation_) {
ResetBounds();
return;
}
UpdateBorders(); UpdateBorders();
UpdateBounds(); UpdateBounds();
state_ = State::SLIDE_OUT;
animation_->Start(); animation_->Start();
} }
...@@ -243,6 +259,17 @@ void UnifiedMessageListView::AnimationEnded(const gfx::Animation* animation) { ...@@ -243,6 +259,17 @@ void UnifiedMessageListView::AnimationEnded(const gfx::Animation* animation) {
// This is also called from AnimationCanceled(). // This is also called from AnimationCanceled().
animation_->SetCurrentValue(1.0); animation_->SetCurrentValue(1.0);
PreferredSizeChanged(); 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( void UnifiedMessageListView::AnimationProgressed(
...@@ -295,7 +322,10 @@ void UnifiedMessageListView::UpdateBounds() { ...@@ -295,7 +322,10 @@ void UnifiedMessageListView::UpdateBounds() {
auto* view = GetContainer(i); auto* view = GetContainer(i);
const int height = view->GetHeightForWidth(kTrayMenuWidth); const int height = view->GetHeightForWidth(kTrayMenuWidth);
view->set_start_bounds(view->ideal_bounds()); 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; y += height;
} }
...@@ -304,13 +334,25 @@ void UnifiedMessageListView::UpdateBounds() { ...@@ -304,13 +334,25 @@ void UnifiedMessageListView::UpdateBounds() {
} }
void UnifiedMessageListView::ResetBounds() { void UnifiedMessageListView::ResetBounds() {
DeleteRemovedNotifications();
UpdateBorders();
UpdateBounds(); UpdateBounds();
state_ = State::IDLE;
if (animation_->is_animating()) if (animation_->is_animating())
animation_->End(); animation_->End();
else else
PreferredSizeChanged(); 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 { double UnifiedMessageListView::GetCurrentValue() const {
return gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT, return gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT,
animation_->GetCurrentValue()); animation_->GetCurrentValue());
......
...@@ -68,6 +68,10 @@ class ASH_EXPORT UnifiedMessageListView ...@@ -68,6 +68,10 @@ class ASH_EXPORT UnifiedMessageListView
void AnimationProgressed(const gfx::Animation* animation) override; void AnimationProgressed(const gfx::Animation* animation) override;
void AnimationCanceled(const gfx::Animation* animation) override; void AnimationCanceled(const gfx::Animation* animation) override;
void set_enable_animation(bool enable_animation) {
enable_animation_ = enable_animation;
}
protected: protected:
// Virtual for testing. // Virtual for testing.
virtual message_center::MessageView* CreateMessageView( virtual message_center::MessageView* CreateMessageView(
...@@ -78,6 +82,20 @@ class ASH_EXPORT UnifiedMessageListView ...@@ -78,6 +82,20 @@ class ASH_EXPORT UnifiedMessageListView
friend class UnifiedMessageListViewTest; friend class UnifiedMessageListViewTest;
class MessageViewContainer; 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); MessageViewContainer* GetContainer(int index);
const MessageViewContainer* GetContainer(int index) const; const MessageViewContainer* GetContainer(int index) const;
...@@ -101,6 +119,9 @@ class ASH_EXPORT UnifiedMessageListView ...@@ -101,6 +119,9 @@ class ASH_EXPORT UnifiedMessageListView
// |final_bounds|. // |final_bounds|.
void ResetBounds(); void ResetBounds();
// Deletes all the MessageViewContainer marked as |is_removed|.
void DeleteRemovedNotifications();
NewUnifiedMessageCenterView* const message_center_view_; NewUnifiedMessageCenterView* const message_center_view_;
// If true, ChildPreferredSizeChanged() will be ignored. This is used in // If true, ChildPreferredSizeChanged() will be ignored. This is used in
...@@ -112,6 +133,8 @@ class ASH_EXPORT UnifiedMessageListView ...@@ -112,6 +133,8 @@ class ASH_EXPORT UnifiedMessageListView
// implicit animation. // implicit animation.
const std::unique_ptr<gfx::LinearAnimation> animation_; const std::unique_ptr<gfx::LinearAnimation> animation_;
State state_ = State::IDLE;
// The height the UnifiedMessageListView starts animating from. If not // The height the UnifiedMessageListView starts animating from. If not
// animating, it's ignored. // animating, it's ignored.
int start_height_ = 0; int start_height_ = 0;
...@@ -120,6 +143,9 @@ class ASH_EXPORT UnifiedMessageListView ...@@ -120,6 +143,9 @@ class ASH_EXPORT UnifiedMessageListView
// as height(). // as height().
int ideal_height_ = 0; int ideal_height_ = 0;
// If false, disables animation on notification removal.
bool enable_animation_ = true;
DISALLOW_COPY_AND_ASSIGN(UnifiedMessageListView); DISALLOW_COPY_AND_ASSIGN(UnifiedMessageListView);
}; };
......
...@@ -112,18 +112,26 @@ class UnifiedMessageListViewTest : public AshTestBase, ...@@ -112,18 +112,26 @@ class UnifiedMessageListViewTest : public AshTestBase,
} }
void AnimateToMiddle() { void AnimateToMiddle() {
EXPECT_TRUE(IsAnimating());
message_list_view()->animation_->SetCurrentValue(0.5); message_list_view()->animation_->SetCurrentValue(0.5);
message_list_view()->AnimationProgressed( message_list_view()->AnimationProgressed(
message_list_view()->animation_.get()); message_list_view()->animation_.get());
} }
void AnimateToEnd() { message_list_view()->animation_->End(); } void AnimateToEnd() {
EXPECT_TRUE(IsAnimating());
message_list_view()->animation_->End();
}
void AnimateUntilIdle() { void AnimateUntilIdle() {
while (message_list_view()->animation_->is_animating()) while (message_list_view()->animation_->is_animating())
message_list_view()->animation_->End(); 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 { UnifiedMessageListView* message_list_view() const {
return message_list_view_.get(); return message_list_view_.get();
} }
...@@ -216,7 +224,7 @@ TEST_F(UnifiedMessageListViewTest, RemoveNotification) { ...@@ -216,7 +224,7 @@ TEST_F(UnifiedMessageListViewTest, RemoveNotification) {
gfx::Rect previous_bounds = GetMessageViewBounds(0); gfx::Rect previous_bounds = GetMessageViewBounds(0);
MessageCenter::Get()->RemoveNotification(id0, true /* by_user */); MessageCenter::Get()->RemoveNotification(id0, true /* by_user */);
AnimateUntilIdle(); AnimateUntilIdle();
EXPECT_EQ(1, size_changed_count()); EXPECT_EQ(3, size_changed_count());
EXPECT_EQ(previous_bounds.y(), GetMessageViewBounds(0).y()); EXPECT_EQ(previous_bounds.y(), GetMessageViewBounds(0).y());
EXPECT_LT(0, message_list_view()->GetPreferredSize().height()); EXPECT_LT(0, message_list_view()->GetPreferredSize().height());
EXPECT_GT(previous_height, message_list_view()->GetPreferredSize().height()); EXPECT_GT(previous_height, message_list_view()->GetPreferredSize().height());
...@@ -226,7 +234,7 @@ TEST_F(UnifiedMessageListViewTest, RemoveNotification) { ...@@ -226,7 +234,7 @@ TEST_F(UnifiedMessageListViewTest, RemoveNotification) {
MessageCenter::Get()->RemoveNotification(id1, true /* by_user */); MessageCenter::Get()->RemoveNotification(id1, true /* by_user */);
AnimateUntilIdle(); AnimateUntilIdle();
EXPECT_EQ(2, size_changed_count()); EXPECT_EQ(6, size_changed_count());
EXPECT_EQ(0, message_list_view()->GetPreferredSize().height()); EXPECT_EQ(0, message_list_view()->GetPreferredSize().height());
} }
...@@ -264,6 +272,11 @@ TEST_F(UnifiedMessageListViewTest, RemovingNotificationAnimation) { ...@@ -264,6 +272,11 @@ TEST_F(UnifiedMessageListViewTest, RemovingNotificationAnimation) {
gfx::Rect bounds1 = GetMessageViewBounds(1); gfx::Rect bounds1 = GetMessageViewBounds(1);
MessageCenter::Get()->RemoveNotification(id1, true /* by_user */); MessageCenter::Get()->RemoveNotification(id1, true /* by_user */);
AnimateToMiddle();
gfx::Rect slided_bounds = GetMessageViewBounds(1);
EXPECT_LT(bounds1.x(), slided_bounds.x());
AnimateToEnd();
AnimateToMiddle(); AnimateToMiddle();
EXPECT_GT(previous_height, message_list_view()->GetPreferredSize().height()); EXPECT_GT(previous_height, message_list_view()->GetPreferredSize().height());
previous_height = message_list_view()->GetPreferredSize().height(); previous_height = message_list_view()->GetPreferredSize().height();
...@@ -274,6 +287,7 @@ TEST_F(UnifiedMessageListViewTest, RemovingNotificationAnimation) { ...@@ -274,6 +287,7 @@ TEST_F(UnifiedMessageListViewTest, RemovingNotificationAnimation) {
EXPECT_EQ(bounds1, GetMessageViewBounds(1)); EXPECT_EQ(bounds1, GetMessageViewBounds(1));
MessageCenter::Get()->RemoveNotification(id2, true /* by_user */); MessageCenter::Get()->RemoveNotification(id2, true /* by_user */);
AnimateToEnd();
AnimateToMiddle(); AnimateToMiddle();
EXPECT_GT(previous_height, message_list_view()->GetPreferredSize().height()); EXPECT_GT(previous_height, message_list_view()->GetPreferredSize().height());
previous_height = message_list_view()->GetPreferredSize().height(); previous_height = message_list_view()->GetPreferredSize().height();
...@@ -283,6 +297,7 @@ TEST_F(UnifiedMessageListViewTest, RemovingNotificationAnimation) { ...@@ -283,6 +297,7 @@ TEST_F(UnifiedMessageListViewTest, RemovingNotificationAnimation) {
EXPECT_EQ(bounds0, GetMessageViewBounds(0)); EXPECT_EQ(bounds0, GetMessageViewBounds(0));
MessageCenter::Get()->RemoveNotification(id0, true /* by_user */); MessageCenter::Get()->RemoveNotification(id0, true /* by_user */);
AnimateToEnd();
AnimateToMiddle(); AnimateToMiddle();
EXPECT_GT(previous_height, message_list_view()->GetPreferredSize().height()); EXPECT_GT(previous_height, message_list_view()->GetPreferredSize().height());
previous_height = message_list_view()->GetPreferredSize().height(); previous_height = message_list_view()->GetPreferredSize().height();
...@@ -291,4 +306,32 @@ TEST_F(UnifiedMessageListViewTest, RemovingNotificationAnimation) { ...@@ -291,4 +306,32 @@ TEST_F(UnifiedMessageListViewTest, RemovingNotificationAnimation) {
EXPECT_EQ(0, message_list_view()->GetPreferredSize().height()); 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 } // 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