Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(490)

Unified Diff: ui/message_center/views/message_center_view.cc

Issue 961993007: Pull MessageListView into its own file for easier testing. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix build Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ui/message_center/views/message_center_view.h ('k') | ui/message_center/views/message_list_view.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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,
« no previous file with comments | « ui/message_center/views/message_center_view.h ('k') | ui/message_center/views/message_list_view.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698