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

Unified Diff: ui/message_center/views/message_list_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_list_view.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ui/message_center/views/message_list_view.cc
diff --git a/ui/message_center/views/message_list_view.cc b/ui/message_center/views/message_list_view.cc
new file mode 100644
index 0000000000000000000000000000000000000000..161d0bfe5d069662b55bb576c0aae08eab950585
--- /dev/null
+++ b/ui/message_center/views/message_list_view.cc
@@ -0,0 +1,383 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+#include "ui/gfx/animation/slide_animation.h"
+#include "ui/message_center/message_center_style.h"
+#include "ui/message_center/message_center_switches.h"
+#include "ui/message_center/views/message_center_view.h"
+#include "ui/message_center/views/message_list_view.h"
+#include "ui/message_center/views/message_view.h"
+#include "ui/views/animation/bounds_animator.h"
+#include "ui/views/background.h"
+#include "ui/views/border.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/widget/widget.h"
+
+namespace message_center {
+
+namespace {
+const int kAnimateClearingNextNotificationDelayMS = 40;
+} // namespace
+
+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));
+ }
+}
+
+} // namespace message_center
« no previous file with comments | « ui/message_center/views/message_list_view.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698