Index: ui/message_center/views/message_center_view.cc |
diff --git a/ui/message_center/views/message_center_view.cc b/ui/message_center/views/message_center_view.cc |
index 94dd9bb8e605dcc452dda85e4e007acaa726ec67..486feebd7c4a69b51f0047bc00b4f2d3126f3e27 100644 |
--- a/ui/message_center/views/message_center_view.cc |
+++ b/ui/message_center/views/message_center_view.cc |
@@ -7,38 +7,33 @@ |
#include <list> |
#include <map> |
-#include "base/command_line.h" |
#include "base/memory/weak_ptr.h" |
#include "base/message_loop/message_loop.h" |
#include "base/stl_util.h" |
#include "ui/base/l10n/l10n_util.h" |
#include "ui/gfx/animation/multi_animation.h" |
-#include "ui/gfx/animation/slide_animation.h" |
#include "ui/gfx/canvas.h" |
#include "ui/gfx/geometry/insets.h" |
#include "ui/gfx/geometry/rect.h" |
#include "ui/gfx/geometry/size.h" |
#include "ui/message_center/message_center.h" |
#include "ui/message_center/message_center_style.h" |
-#include "ui/message_center/message_center_switches.h" |
#include "ui/message_center/message_center_tray.h" |
#include "ui/message_center/message_center_types.h" |
#include "ui/message_center/views/message_center_button_bar.h" |
+#include "ui/message_center/views/message_list_view.h" |
#include "ui/message_center/views/message_view.h" |
#include "ui/message_center/views/message_view_context_menu_controller.h" |
#include "ui/message_center/views/notification_view.h" |
#include "ui/message_center/views/notifier_settings_view.h" |
#include "ui/resources/grit/ui_resources.h" |
#include "ui/strings/grit/ui_strings.h" |
-#include "ui/views/animation/bounds_animator.h" |
-#include "ui/views/animation/bounds_animator_observer.h" |
#include "ui/views/background.h" |
#include "ui/views/border.h" |
#include "ui/views/controls/button/button.h" |
#include "ui/views/controls/label.h" |
#include "ui/views/controls/scroll_view.h" |
#include "ui/views/controls/scrollbar/overlay_scroll_bar.h" |
-#include "ui/views/layout/box_layout.h" |
#include "ui/views/layout/fill_layout.h" |
#include "ui/views/widget/widget.h" |
@@ -50,7 +45,6 @@ const SkColor kNoNotificationsTextColor = SkColorSetRGB(0xb4, 0xb4, 0xb4); |
#if defined(OS_LINUX) && defined(OS_CHROMEOS) |
const SkColor kTransparentColor = SkColorSetARGB(0, 0, 0, 0); |
#endif |
-const int kAnimateClearingNextNotificationDelayMS = 40; |
const int kDefaultAnimationDurationMs = 120; |
const int kDefaultFrameRateHz = 60; |
@@ -109,433 +103,6 @@ void NoNotificationMessageView::Layout() { |
label_->SetBounds(0, margin, width(), text_height); |
} |
-// Displays a list of messages for rich notifications. Functions as an array of |
-// MessageViews and animates them on transitions. It also supports |
-// repositioning. |
-class MessageListView : public views::View, |
- public views::BoundsAnimatorObserver { |
- public: |
- explicit MessageListView(MessageCenterView* message_center_view, |
- bool top_down); |
- ~MessageListView() override; |
- |
- void AddNotificationAt(MessageView* view, int i); |
- void RemoveNotification(MessageView* view); |
- void UpdateNotification(MessageView* view, const Notification& notification); |
- void SetRepositionTarget(const gfx::Rect& target_rect); |
- void ResetRepositionSession(); |
- void ClearAllNotifications(const gfx::Rect& visible_scroll_rect); |
- |
- protected: |
- // Overridden from views::View. |
- void Layout() override; |
- gfx::Size GetPreferredSize() const override; |
- int GetHeightForWidth(int width) const override; |
- void PaintChildren(const ui::PaintContext& context) override; |
- void ReorderChildLayers(ui::Layer* parent_layer) override; |
- |
- // Overridden from views::BoundsAnimatorObserver. |
- void OnBoundsAnimatorProgressed(views::BoundsAnimator* animator) override; |
- void OnBoundsAnimatorDone(views::BoundsAnimator* animator) override; |
- |
- private: |
- bool IsValidChild(const views::View* child) const; |
- void DoUpdateIfPossible(); |
- |
- // Animates all notifications below target upwards to align with the top of |
- // the last closed notification. |
- void AnimateNotificationsBelowTarget(); |
- // Animates all notifications above target downwards to align with the top of |
- // the last closed notification. |
- void AnimateNotificationsAboveTarget(); |
- |
- // Schedules animation for a child to the specified position. Returns false |
- // if |child| will disappear after the animation. |
- bool AnimateChild(views::View* child, int top, int height); |
- |
- // Animate clearing one notification. |
- void AnimateClearingOneNotification(); |
- MessageCenterView* message_center_view() const { |
- return message_center_view_; |
- } |
- |
- MessageCenterView* message_center_view_; // Weak reference. |
- // The top position of the reposition target rectangle. |
- int reposition_top_; |
- int fixed_height_; |
- bool has_deferred_task_; |
- bool clear_all_started_; |
- bool top_down_; |
- std::set<views::View*> adding_views_; |
- std::set<views::View*> deleting_views_; |
- std::set<views::View*> deleted_when_done_; |
- std::list<views::View*> clearing_all_views_; |
- scoped_ptr<views::BoundsAnimator> animator_; |
- base::WeakPtrFactory<MessageListView> weak_ptr_factory_; |
- |
- DISALLOW_COPY_AND_ASSIGN(MessageListView); |
-}; |
- |
-MessageListView::MessageListView(MessageCenterView* message_center_view, |
- bool top_down) |
- : message_center_view_(message_center_view), |
- reposition_top_(-1), |
- fixed_height_(0), |
- has_deferred_task_(false), |
- clear_all_started_(false), |
- top_down_(top_down), |
- weak_ptr_factory_(this) { |
- views::BoxLayout* layout = |
- new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1); |
- layout->SetDefaultFlex(1); |
- SetLayoutManager(layout); |
- |
- // Set the margin to 0 for the layout. BoxLayout assumes the same margin |
- // for top and bottom, but the bottom margin here should be smaller |
- // because of the shadow of message view. Use an empty border instead |
- // to provide this margin. |
- gfx::Insets shadow_insets = MessageView::GetShadowInsets(); |
- set_background(views::Background::CreateSolidBackground( |
- kMessageCenterBackgroundColor)); |
- SetBorder(views::Border::CreateEmptyBorder( |
- top_down ? 0 : kMarginBetweenItems - shadow_insets.top(), /* top */ |
- kMarginBetweenItems - shadow_insets.left(), /* left */ |
- top_down ? kMarginBetweenItems - shadow_insets.bottom() : 0, /* bottom */ |
- kMarginBetweenItems - shadow_insets.right() /* right */)); |
-} |
- |
-MessageListView::~MessageListView() { |
- if (animator_.get()) |
- animator_->RemoveObserver(this); |
-} |
- |
-void MessageListView::Layout() { |
- if (animator_.get()) |
- return; |
- |
- gfx::Rect child_area = GetContentsBounds(); |
- int top = child_area.y(); |
- int between_items = |
- kMarginBetweenItems - MessageView::GetShadowInsets().bottom(); |
- |
- for (int i = 0; i < child_count(); ++i) { |
- views::View* child = child_at(i); |
- if (!child->visible()) |
- continue; |
- int height = child->GetHeightForWidth(child_area.width()); |
- child->SetBounds(child_area.x(), top, child_area.width(), height); |
- top += height + between_items; |
- } |
-} |
- |
-void MessageListView::AddNotificationAt(MessageView* view, int index) { |
- // |index| refers to a position in a subset of valid children. |real_index| |
- // in a list includes the invalid children, so we compute the real index by |
- // walking the list until |index| number of valid children are encountered, |
- // or to the end of the list. |
- int real_index = 0; |
- while (real_index < child_count()) { |
- if (IsValidChild(child_at(real_index))) { |
- --index; |
- if (index < 0) |
- break; |
- } |
- ++real_index; |
- } |
- |
- AddChildViewAt(view, real_index); |
- if (GetContentsBounds().IsEmpty()) |
- return; |
- |
- adding_views_.insert(view); |
- DoUpdateIfPossible(); |
-} |
- |
-void MessageListView::RemoveNotification(MessageView* view) { |
- DCHECK_EQ(view->parent(), this); |
- if (GetContentsBounds().IsEmpty()) { |
- delete view; |
- } else { |
- if (view->layer()) { |
- deleting_views_.insert(view); |
- } else { |
- if (animator_.get()) |
- animator_->StopAnimatingView(view); |
- delete view; |
- } |
- DoUpdateIfPossible(); |
- } |
-} |
- |
-void MessageListView::UpdateNotification(MessageView* view, |
- const Notification& notification) { |
- int index = GetIndexOf(view); |
- DCHECK_LE(0, index); // GetIndexOf is negative if not a child. |
- |
- if (animator_.get()) |
- animator_->StopAnimatingView(view); |
- if (deleting_views_.find(view) != deleting_views_.end()) |
- deleting_views_.erase(view); |
- if (deleted_when_done_.find(view) != deleted_when_done_.end()) |
- deleted_when_done_.erase(view); |
- view->UpdateWithNotification(notification); |
- DoUpdateIfPossible(); |
-} |
- |
-gfx::Size MessageListView::GetPreferredSize() const { |
- int width = 0; |
- for (int i = 0; i < child_count(); i++) { |
- const views::View* child = child_at(i); |
- if (IsValidChild(child)) |
- width = std::max(width, child->GetPreferredSize().width()); |
- } |
- |
- return gfx::Size(width + GetInsets().width(), |
- GetHeightForWidth(width + GetInsets().width())); |
-} |
- |
-int MessageListView::GetHeightForWidth(int width) const { |
- if (fixed_height_ > 0) |
- return fixed_height_; |
- |
- width -= GetInsets().width(); |
- int height = 0; |
- int padding = 0; |
- for (int i = 0; i < child_count(); ++i) { |
- const views::View* child = child_at(i); |
- if (!IsValidChild(child)) |
- continue; |
- height += child->GetHeightForWidth(width) + padding; |
- padding = kMarginBetweenItems - MessageView::GetShadowInsets().bottom(); |
- } |
- |
- return height + GetInsets().height(); |
-} |
- |
-void MessageListView::PaintChildren(const ui::PaintContext& context) { |
- // Paint in the inversed order. Otherwise upper notification may be |
- // hidden by the lower one. |
- for (int i = child_count() - 1; i >= 0; --i) { |
- if (!child_at(i)->layer()) |
- child_at(i)->Paint(context); |
- } |
-} |
- |
-void MessageListView::ReorderChildLayers(ui::Layer* parent_layer) { |
- // Reorder children to stack the last child layer at the top. Otherwise |
- // upper notification may be hidden by the lower one. |
- for (int i = 0; i < child_count(); ++i) { |
- if (child_at(i)->layer()) |
- parent_layer->StackAtBottom(child_at(i)->layer()); |
- } |
-} |
- |
-void MessageListView::SetRepositionTarget(const gfx::Rect& target) { |
- reposition_top_ = target.y(); |
- fixed_height_ = GetHeightForWidth(width()); |
-} |
- |
-void MessageListView::ResetRepositionSession() { |
- // Don't call DoUpdateIfPossible(), but let Layout() do the task without |
- // animation. Reset will cause the change of the bubble size itself, and |
- // animation from the old location will look weird. |
- if (reposition_top_ >= 0 && animator_.get()) { |
- has_deferred_task_ = false; |
- // cancel cause OnBoundsAnimatorDone which deletes |deleted_when_done_|. |
- animator_->Cancel(); |
- STLDeleteContainerPointers(deleting_views_.begin(), deleting_views_.end()); |
- deleting_views_.clear(); |
- adding_views_.clear(); |
- animator_.reset(); |
- } |
- |
- reposition_top_ = -1; |
- fixed_height_ = 0; |
-} |
- |
-void MessageListView::ClearAllNotifications( |
- const gfx::Rect& visible_scroll_rect) { |
- for (int i = 0; i < child_count(); ++i) { |
- views::View* child = child_at(i); |
- if (!child->visible()) |
- continue; |
- if (gfx::IntersectRects(child->bounds(), visible_scroll_rect).IsEmpty()) |
- continue; |
- clearing_all_views_.push_back(child); |
- } |
- DoUpdateIfPossible(); |
-} |
- |
-void MessageListView::OnBoundsAnimatorProgressed( |
- views::BoundsAnimator* animator) { |
- DCHECK_EQ(animator_.get(), animator); |
- for (std::set<views::View*>::iterator iter = deleted_when_done_.begin(); |
- iter != deleted_when_done_.end(); ++iter) { |
- const gfx::SlideAnimation* animation = animator->GetAnimationForView(*iter); |
- if (animation) |
- (*iter)->layer()->SetOpacity(animation->CurrentValueBetween(1.0, 0.0)); |
- } |
-} |
- |
-void MessageListView::OnBoundsAnimatorDone(views::BoundsAnimator* animator) { |
- STLDeleteContainerPointers( |
- deleted_when_done_.begin(), deleted_when_done_.end()); |
- deleted_when_done_.clear(); |
- |
- if (clear_all_started_) { |
- clear_all_started_ = false; |
- message_center_view()->OnAllNotificationsCleared(); |
- } |
- |
- if (has_deferred_task_) { |
- has_deferred_task_ = false; |
- DoUpdateIfPossible(); |
- } |
- |
- if (GetWidget()) |
- GetWidget()->SynthesizeMouseMoveEvent(); |
-} |
- |
-bool MessageListView::IsValidChild(const views::View* child) const { |
- return child->visible() && |
- deleting_views_.find(const_cast<views::View*>(child)) == |
- deleting_views_.end() && |
- deleted_when_done_.find(const_cast<views::View*>(child)) == |
- deleted_when_done_.end(); |
-} |
- |
-void MessageListView::DoUpdateIfPossible() { |
- gfx::Rect child_area = GetContentsBounds(); |
- if (child_area.IsEmpty()) |
- return; |
- |
- if (animator_.get() && animator_->IsAnimating()) { |
- has_deferred_task_ = true; |
- return; |
- } |
- |
- if (!animator_.get()) { |
- animator_.reset(new views::BoundsAnimator(this)); |
- animator_->AddObserver(this); |
- } |
- |
- if (!clearing_all_views_.empty()) { |
- AnimateClearingOneNotification(); |
- return; |
- } |
- |
- if (top_down_ || |
- base::CommandLine::ForCurrentProcess()->HasSwitch( |
- switches::kEnableMessageCenterAlwaysScrollUpUponNotificationRemoval)) |
- AnimateNotificationsBelowTarget(); |
- else |
- AnimateNotificationsAboveTarget(); |
- |
- adding_views_.clear(); |
- deleting_views_.clear(); |
-} |
- |
-void MessageListView::AnimateNotificationsBelowTarget() { |
- int last_index = -1; |
- for (int i = 0; i < child_count(); ++i) { |
- views::View* child = child_at(i); |
- if (!IsValidChild(child)) { |
- AnimateChild(child, child->y(), child->height()); |
- } else if (reposition_top_ < 0 || child->y() > reposition_top_) { |
- // Find first notification below target (or all notifications if no |
- // target). |
- last_index = i; |
- break; |
- } |
- } |
- if (last_index > 0) { |
- int between_items = |
- kMarginBetweenItems - MessageView::GetShadowInsets().bottom(); |
- int top = (reposition_top_ > 0) ? reposition_top_ : GetInsets().top(); |
- |
- for (int i = last_index; i < child_count(); ++i) { |
- // Animate notifications below target upwards. |
- views::View* child = child_at(i); |
- if (AnimateChild(child, top, child->height())) |
- top += child->height() + between_items; |
- } |
- } |
-} |
- |
-void MessageListView::AnimateNotificationsAboveTarget() { |
- int last_index = -1; |
- for (int i = child_count() - 1; i >= 0; --i) { |
- views::View* child = child_at(i); |
- if (!IsValidChild(child)) { |
- AnimateChild(child, child->y(), child->height()); |
- } else if (reposition_top_ < 0 || child->y() < reposition_top_) { |
- // Find first notification above target (or all notifications if no |
- // target). |
- last_index = i; |
- break; |
- } |
- } |
- if (last_index >= 0) { |
- int between_items = |
- kMarginBetweenItems - MessageView::GetShadowInsets().bottom(); |
- int bottom = (reposition_top_ > 0) |
- ? reposition_top_ + child_at(last_index)->height() |
- : GetHeightForWidth(width()) - GetInsets().bottom(); |
- for (int i = last_index; i >= 0; --i) { |
- // Animate notifications above target downwards. |
- views::View* child = child_at(i); |
- if (AnimateChild(child, bottom - child->height(), child->height())) |
- bottom -= child->height() + between_items; |
- } |
- } |
-} |
- |
-bool MessageListView::AnimateChild(views::View* child, int top, int height) { |
- gfx::Rect child_area = GetContentsBounds(); |
- if (adding_views_.find(child) != adding_views_.end()) { |
- child->SetBounds(child_area.right(), top, child_area.width(), height); |
- animator_->AnimateViewTo( |
- child, gfx::Rect(child_area.x(), top, child_area.width(), height)); |
- } else if (deleting_views_.find(child) != deleting_views_.end()) { |
- DCHECK(child->layer()); |
- // No moves, but animate to fade-out. |
- animator_->AnimateViewTo(child, child->bounds()); |
- deleted_when_done_.insert(child); |
- return false; |
- } else { |
- gfx::Rect target(child_area.x(), top, child_area.width(), height); |
- if (child->bounds().origin() != target.origin()) |
- animator_->AnimateViewTo(child, target); |
- else |
- child->SetBoundsRect(target); |
- } |
- return true; |
-} |
- |
-void MessageListView::AnimateClearingOneNotification() { |
- DCHECK(!clearing_all_views_.empty()); |
- |
- clear_all_started_ = true; |
- |
- views::View* child = clearing_all_views_.front(); |
- clearing_all_views_.pop_front(); |
- |
- // Slide from left to right. |
- gfx::Rect new_bounds = child->bounds(); |
- new_bounds.set_x(new_bounds.right() + kMarginBetweenItems); |
- animator_->AnimateViewTo(child, new_bounds); |
- |
- // Schedule to start sliding out next notification after a short delay. |
- if (!clearing_all_views_.empty()) { |
- base::MessageLoop::current()->PostDelayedTask( |
- FROM_HERE, |
- base::Bind(&MessageListView::AnimateClearingOneNotification, |
- weak_ptr_factory_.GetWeakPtr()), |
- base::TimeDelta::FromMilliseconds( |
- kAnimateClearingNextNotificationDelayMS)); |
- } |
-} |
- |
// MessageCenterView /////////////////////////////////////////////////////////// |
MessageCenterView::MessageCenterView(MessageCenter* message_center, |