Commit e587b05b authored by Tetsui Ohkubo's avatar Tetsui Ohkubo Committed by Commit Bot

NewMessageListView: Implement removing animation.

This CL adds animation to NewMessageListView when notifications are
removed.

Video: http://shortn/_hXnIGaYesF

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

Design doc: go/chrome-popup-refactoring

TEST=UnifiedMessageListViewTest.RemovingNotificationAnimation
BUG=769219

Change-Id: I06c909acb227f9bfa484083dca36a95d79ece7f5
Reviewed-on: https://chromium-review.googlesource.com/c/1280087
Commit-Queue: Tetsui Ohkubo <tetsui@chromium.org>
Reviewed-by: default avatarYoshiki Iguchi <yoshiki@chromium.org>
Cr-Commit-Position: refs/heads/master@{#599852}
parent 7f0c13c7
......@@ -195,7 +195,7 @@ void NewUnifiedMessageCenterView::SetNotificationHeightBelowScroll(
void NewUnifiedMessageCenterView::UpdateVisibility() {
SessionController* session_controller = Shell::Get()->session_controller();
SetVisible(message_list_view_->child_count() > 0 &&
SetVisible(message_list_view_->GetPreferredSize().height() > 0 &&
session_controller->ShouldShowNotificationTray() &&
!session_controller->IsScreenLocked());
// When notification list went invisible, |position_from_bottom_| should be
......
......@@ -93,6 +93,19 @@ class NewUnifiedMessageCenterViewTest : public AshTestBase,
size_changed_count_ = 0;
}
void AnimateToMiddle() {
GetMessageListView()->animation_->SetCurrentValue(0.5);
GetMessageListView()->AnimationProgressed(
GetMessageListView()->animation_.get());
}
void AnimateToEnd() { GetMessageListView()->animation_->End(); }
void AnimateUntilIdle() {
while (GetMessageListView()->animation_->is_animating())
GetMessageListView()->animation_->End();
}
gfx::Rect GetMessageViewVisibleBounds(int index) {
gfx::Rect bounds = GetMessageListView()->child_at(index)->bounds();
bounds -= GetScroller()->GetVisibleRect().OffsetFromOrigin();
......@@ -141,6 +154,9 @@ TEST_F(NewUnifiedMessageCenterViewTest, AddAndRemoveNotification) {
EXPECT_TRUE(message_center_view()->visible());
MessageCenter::Get()->RemoveNotification(id0, true /* by_user */);
AnimateToMiddle();
EXPECT_TRUE(message_center_view()->visible());
AnimateToEnd();
EXPECT_FALSE(message_center_view()->visible());
}
......@@ -157,6 +173,7 @@ TEST_F(NewUnifiedMessageCenterViewTest, ContentsRelayout) {
const int previous_list_height = GetMessageListView()->height();
MessageCenter::Get()->RemoveNotification(ids.back(), true /* by_user */);
AnimateUntilIdle();
EXPECT_TRUE(message_center_view()->visible());
EXPECT_GT(previous_contents_height, GetScrollerContents()->height());
EXPECT_GT(previous_list_height, GetMessageListView()->height());
......@@ -197,6 +214,7 @@ TEST_F(NewUnifiedMessageCenterViewTest, ClearAllPressed) {
// When Clear All button is pressed, all notifications are removed and the
// view becomes invisible.
message_center_view()->ButtonPressed(nullptr, DummyEvent());
AnimateUntilIdle();
EXPECT_FALSE(message_center_view()->visible());
}
......@@ -345,8 +363,10 @@ TEST_F(NewUnifiedMessageCenterViewTest,
message_center_view()->bounds().height());
EXPECT_TRUE(GetStackingCounter()->visible());
for (size_t i = 0; i < 5; ++i)
for (size_t i = 0; i < 5; ++i) {
MessageCenter::Get()->RemoveNotification(ids[i], true /* by_user */);
AnimateUntilIdle();
}
EXPECT_FALSE(GetStackingCounter()->visible());
}
......
......@@ -8,6 +8,7 @@
#include "ash/system/message_center/notification_swipe_control_view.h"
#include "ash/system/tray/tray_constants.h"
#include "base/auto_reset.h"
#include "ui/gfx/animation/linear_animation.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/views/message_view_factory.h"
#include "ui/views/border.h"
......@@ -20,6 +21,13 @@ using message_center::MessageView;
namespace ash {
namespace {
constexpr base::TimeDelta kClosingAnimationDuration =
base::TimeDelta::FromMilliseconds(330);
} // namespace
// Container view of notification and swipe control.
// All children of UnifiedMessageListView should be MessageViewContainer.
class UnifiedMessageListView::MessageViewContainer
......@@ -78,7 +86,26 @@ class UnifiedMessageListView::MessageViewContainer
control_view_->UpdateButtonsVisibility();
}
gfx::Rect start_bounds() const { return start_bounds_; }
gfx::Rect ideal_bounds() const { return ideal_bounds_; }
void set_start_bounds(const gfx::Rect& start_bounds) {
start_bounds_ = start_bounds;
}
void set_ideal_bounds(const gfx::Rect& ideal_bounds) {
ideal_bounds_ = ideal_bounds;
}
private:
// The bounds that the container starts animating from. If not animating, it's
// ignored.
gfx::Rect start_bounds_;
// The final bounds of the container. If not animating, it's same as the
// actual bounds().
gfx::Rect ideal_bounds_;
MessageView* const message_view_;
NotificationSwipeControlView* const control_view_;
......@@ -87,11 +114,11 @@ class UnifiedMessageListView::MessageViewContainer
UnifiedMessageListView::UnifiedMessageListView(
NewUnifiedMessageCenterView* message_center_view)
: message_center_view_(message_center_view) {
: message_center_view_(message_center_view),
animation_(std::make_unique<gfx::LinearAnimation>(this)) {
MessageCenter::Get()->AddObserver(this);
SetLayoutManager(
std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical));
animation_->SetDuration(kClosingAnimationDuration);
animation_->SetCurrentValue(1.0);
}
UnifiedMessageListView::~UnifiedMessageListView() {
......@@ -110,6 +137,7 @@ void UnifiedMessageListView::Init() {
notification->id(), message_center::DISPLAY_SOURCE_MESSAGE_CENTER);
}
UpdateBorders();
UpdateBounds();
}
int UnifiedMessageListView::GetLastNotificationHeight() const {
......@@ -129,7 +157,7 @@ int UnifiedMessageListView::CountNotificationsAboveY(int y_offset) const {
void UnifiedMessageListView::ChildPreferredSizeChanged(views::View* child) {
if (ignore_size_change_)
return;
PreferredSizeChanged();
ResetBounds();
}
void UnifiedMessageListView::PreferredSizeChanged() {
......@@ -138,6 +166,20 @@ void UnifiedMessageListView::PreferredSizeChanged() {
message_center_view_->ListPreferredSizeChanged();
}
void UnifiedMessageListView::Layout() {
for (int i = 0; i < child_count(); ++i) {
auto* view = GetContainer(i);
view->SetBoundsRect(gfx::Tween::RectValueBetween(
GetCurrentValue(), view->start_bounds(), view->ideal_bounds()));
}
}
gfx::Size UnifiedMessageListView::CalculatePreferredSize() const {
return gfx::Size(kTrayMenuWidth,
gfx::Tween::IntValueBetween(GetCurrentValue(), start_height_,
ideal_height_));
}
void UnifiedMessageListView::OnNotificationAdded(const std::string& id) {
auto* notification = MessageCenter::Get()->FindVisibleNotificationById(id);
if (!notification)
......@@ -151,7 +193,7 @@ void UnifiedMessageListView::OnNotificationAdded(const std::string& id) {
view->SetExpanded(view->IsAutoExpandingAllowed());
AddChildView(new MessageViewContainer(view));
UpdateBorders();
PreferredSizeChanged();
ResetBounds();
}
void UnifiedMessageListView::OnNotificationRemoved(const std::string& id,
......@@ -165,7 +207,8 @@ void UnifiedMessageListView::OnNotificationRemoved(const std::string& id,
}
UpdateBorders();
PreferredSizeChanged();
UpdateBounds();
animation_->Start();
}
void UnifiedMessageListView::OnNotificationUpdated(const std::string& id) {
......@@ -181,7 +224,7 @@ void UnifiedMessageListView::OnNotificationUpdated(const std::string& id) {
}
}
PreferredSizeChanged();
ResetBounds();
}
void UnifiedMessageListView::OnSlideChanged(
......@@ -196,6 +239,22 @@ void UnifiedMessageListView::OnSlideChanged(
}
}
void UnifiedMessageListView::AnimationEnded(const gfx::Animation* animation) {
// This is also called from AnimationCanceled().
animation_->SetCurrentValue(1.0);
PreferredSizeChanged();
}
void UnifiedMessageListView::AnimationProgressed(
const gfx::Animation* animation) {
PreferredSizeChanged();
}
void UnifiedMessageListView::AnimationCanceled(
const gfx::Animation* animation) {
AnimationEnded(animation);
}
MessageView* UnifiedMessageListView::CreateMessageView(
const Notification& notification) {
auto* view = message_center::MessageViewFactory::Create(notification);
......@@ -207,7 +266,13 @@ MessageView* UnifiedMessageListView::CreateMessageView(
UnifiedMessageListView::MessageViewContainer*
UnifiedMessageListView::GetContainer(int index) {
return static_cast<MessageViewContainer*>(child_at(index));
return const_cast<MessageViewContainer*>(
const_cast<const UnifiedMessageListView*>(this)->GetContainer(index));
}
const UnifiedMessageListView::MessageViewContainer*
UnifiedMessageListView::GetContainer(int index) const {
return static_cast<const MessageViewContainer*>(child_at(index));
}
void UnifiedMessageListView::CollapseAllNotifications() {
......@@ -224,4 +289,31 @@ void UnifiedMessageListView::UpdateBorders() {
}
}
void UnifiedMessageListView::UpdateBounds() {
int y = 0;
for (int i = 0; i < child_count(); ++i) {
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));
y += height;
}
start_height_ = ideal_height_;
ideal_height_ = y;
}
void UnifiedMessageListView::ResetBounds() {
UpdateBounds();
if (animation_->is_animating())
animation_->End();
else
PreferredSizeChanged();
}
double UnifiedMessageListView::GetCurrentValue() const {
return gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT,
animation_->GetCurrentValue());
}
} // namespace ash
......@@ -6,10 +6,15 @@
#define ASH_SYSTEM_MESSAGE_CENTER_UNIFIED_MESSAGE_LIST_VIEW_H_
#include "ash/ash_export.h"
#include "ui/gfx/animation/animation_delegate.h"
#include "ui/message_center/message_center_observer.h"
#include "ui/message_center/views/message_view.h"
#include "ui/views/view.h"
namespace gfx {
class LinearAnimation;
} // namespace gfx
namespace message_center {
class MessageView;
class Notification;
......@@ -24,7 +29,8 @@ class NewUnifiedMessageCenterView;
class ASH_EXPORT UnifiedMessageListView
: public views::View,
public message_center::MessageCenterObserver,
public message_center::MessageView::SlideObserver {
public message_center::MessageView::SlideObserver,
public gfx::AnimationDelegate {
public:
// |message_center_view| can be null in unit tests.
explicit UnifiedMessageListView(
......@@ -46,6 +52,8 @@ class ASH_EXPORT UnifiedMessageListView
// views::View:
void ChildPreferredSizeChanged(views::View* child) override;
void PreferredSizeChanged() override;
void Layout() override;
gfx::Size CalculatePreferredSize() const override;
// message_center::MessageCenterObserver:
void OnNotificationAdded(const std::string& id) override;
......@@ -55,18 +63,44 @@ class ASH_EXPORT UnifiedMessageListView
// message_center::MessageView::SlideObserver:
void OnSlideChanged(const std::string& notification_id) override;
// gfx::AnimationDelegate:
void AnimationEnded(const gfx::Animation* animation) override;
void AnimationProgressed(const gfx::Animation* animation) override;
void AnimationCanceled(const gfx::Animation* animation) override;
protected:
// Virtual for testing.
virtual message_center::MessageView* CreateMessageView(
const message_center::Notification& notification);
private:
friend class NewUnifiedMessageCenterViewTest;
friend class UnifiedMessageListViewTest;
class MessageViewContainer;
MessageViewContainer* GetContainer(int index);
const MessageViewContainer* GetContainer(int index) const;
// Current progress of the animation between 0.0 and 1.0. Returns 1.0 when
// it's not animating.
double GetCurrentValue() const;
// Collapses all the existing notifications. It does not trigger
// PreferredSizeChanged() (See |ignore_size_change_|).
void CollapseAllNotifications();
// Updates the borders of notifications. It adds separators between
// notifications, and rounds notification corners at the top and the bottom.
void UpdateBorders();
// Updates |final_bounds| of all notifications and moves old |final_bounds| to
// |start_bounds|.
void UpdateBounds();
// Resets the animation, and makes all notifications immediately positioned at
// |final_bounds|.
void ResetBounds();
NewUnifiedMessageCenterView* const message_center_view_;
// If true, ChildPreferredSizeChanged() will be ignored. This is used in
......@@ -74,6 +108,18 @@ class ASH_EXPORT UnifiedMessageListView
// multiple times because of sequential SetExpanded() calls.
bool ignore_size_change_ = false;
// Manages notification closing animation. UnifiedMessageListView does not use
// implicit animation.
const std::unique_ptr<gfx::LinearAnimation> animation_;
// The height the UnifiedMessageListView starts animating from. If not
// animating, it's ignored.
int start_height_ = 0;
// The final height of the UnifiedMessageListView. If not animating, it's same
// as height().
int ideal_height_ = 0;
DISALLOW_COPY_AND_ASSIGN(UnifiedMessageListView);
};
......
......@@ -111,6 +111,19 @@ class UnifiedMessageListViewTest : public AshTestBase,
return message_list_view()->child_at(index)->bounds();
}
void AnimateToMiddle() {
message_list_view()->animation_->SetCurrentValue(0.5);
message_list_view()->AnimationProgressed(
message_list_view()->animation_.get());
}
void AnimateToEnd() { message_list_view()->animation_->End(); }
void AnimateUntilIdle() {
while (message_list_view()->animation_->is_animating())
message_list_view()->animation_->End();
}
UnifiedMessageListView* message_list_view() const {
return message_list_view_.get();
}
......@@ -202,6 +215,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(previous_bounds.y(), GetMessageViewBounds(0).y());
EXPECT_LT(0, message_list_view()->GetPreferredSize().height());
......@@ -211,6 +225,7 @@ TEST_F(UnifiedMessageListViewTest, RemoveNotification) {
EXPECT_EQ(kUnifiedTrayCornerRadius, GetMessageViewAt(0)->bottom_radius());
MessageCenter::Get()->RemoveNotification(id1, true /* by_user */);
AnimateUntilIdle();
EXPECT_EQ(2, size_changed_count());
EXPECT_EQ(0, message_list_view()->GetPreferredSize().height());
}
......@@ -239,4 +254,41 @@ TEST_F(UnifiedMessageListViewTest, CollapseOlderNotifications) {
EXPECT_TRUE(GetMessageViewAt(3)->IsExpanded());
}
TEST_F(UnifiedMessageListViewTest, RemovingNotificationAnimation) {
auto id0 = AddNotification();
auto id1 = AddNotification();
auto id2 = AddNotification();
CreateMessageListView();
int previous_height = message_list_view()->GetPreferredSize().height();
gfx::Rect bounds0 = GetMessageViewBounds(0);
gfx::Rect bounds1 = GetMessageViewBounds(1);
MessageCenter::Get()->RemoveNotification(id1, true /* by_user */);
AnimateToMiddle();
EXPECT_GT(previous_height, message_list_view()->GetPreferredSize().height());
previous_height = message_list_view()->GetPreferredSize().height();
AnimateToEnd();
// Now it lost separator border.
bounds1.Inset(gfx::Insets(0, 0, 1, 0));
EXPECT_EQ(bounds0, GetMessageViewBounds(0));
EXPECT_EQ(bounds1, GetMessageViewBounds(1));
MessageCenter::Get()->RemoveNotification(id2, true /* by_user */);
AnimateToMiddle();
EXPECT_GT(previous_height, message_list_view()->GetPreferredSize().height());
previous_height = message_list_view()->GetPreferredSize().height();
AnimateToEnd();
// Now it lost separator border.
bounds0.Inset(gfx::Insets(0, 0, 1, 0));
EXPECT_EQ(bounds0, GetMessageViewBounds(0));
MessageCenter::Get()->RemoveNotification(id0, true /* by_user */);
AnimateToMiddle();
EXPECT_GT(previous_height, message_list_view()->GetPreferredSize().height());
previous_height = message_list_view()->GetPreferredSize().height();
AnimateToEnd();
EXPECT_EQ(0, message_list_view()->GetPreferredSize().height());
}
} // 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