blob: a8acf67e6f4955726db1e955d37c6b0dfd074265 [file] [log] [blame]
// Copyright (c) 2013 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 <list>
#include <map>
#include "base/compiler_specific.h"
#include "base/gtest_prod_util.h"
#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/message_center/message_center_export.h"
#include "ui/message_center/message_center_observer.h"
#include "ui/message_center/views/message_center_controller.h"
#include "ui/message_center/views/toast_contents_view.h"
#include "ui/views/widget/widget_observer.h"
namespace base {
class RunLoop;
namespace views {
class Widget;
namespace gfx {
class Display;
class Screen;
namespace message_center {
namespace test {
class MessagePopupCollectionTest;
class MessageCenter;
class MessageCenterTray;
class MessageViewContextMenuController;
class PopupAlignmentDelegate;
// Container for popup toasts. Because each toast is a frameless window rather
// than a view in a bubble, now the container just manages all of those toasts.
// This is similar to chrome/browser/notifications/balloon_collection, but the
// contents of each toast are for the message center and layout strategy would
// be slightly different.
class MESSAGE_CENTER_EXPORT MessagePopupCollection
: public MessageCenterController,
public MessageCenterObserver {
// |parent| specifies the parent widget of the toast windows. The default
// parent will be used for NULL. Usually each icon is spacing against its
// predecessor.
MessagePopupCollection(gfx::NativeView parent,
MessageCenter* message_center,
MessageCenterTray* tray,
PopupAlignmentDelegate* alignment_delegate);
~MessagePopupCollection() override;
// Overridden from MessageCenterController:
void ClickOnNotification(const std::string& notification_id) override;
void RemoveNotification(const std::string& notification_id,
bool by_user) override;
scoped_ptr<ui::MenuModel> CreateMenuModel(
const NotifierId& notifier_id,
const base::string16& display_source) override;
bool HasClickedListener(const std::string& notification_id) override;
void ClickOnNotificationButton(const std::string& notification_id,
int button_index) override;
void MarkAllPopupsShown();
// Since these events are really coming from individual toast widgets,
// it helps to be able to keep track of the sender.
void OnMouseEntered(ToastContentsView* toast_entered);
void OnMouseExited(ToastContentsView* toast_exited);
// Invoked by toasts when they start/finish their animations.
// While "defer counter" is greater then zero, the popup collection does
// not perform updates. It is used to wait for various animations and user
// actions like serial closing of the toasts, when the remaining toasts "flow
// under the mouse".
void IncrementDeferCounter();
void DecrementDeferCounter();
// Runs the next step in update/animate sequence, if the defer counter is not
// zero. Otherwise, simply waits when it becomes zero.
void DoUpdateIfPossible();
// Removes the toast from our internal list of toasts; this is called when the
// toast is irrevocably closed (such as within RemoveToast).
void ForgetToast(ToastContentsView* toast);
// Called when the display bounds has been changed. Used in Windows only.
void OnDisplayMetricsChanged(const gfx::Display& display);
// Used by ToastContentsView to locate itself.
gfx::NativeView parent() const { return parent_; }
friend class test::MessagePopupCollectionTest;
typedef std::list<ToastContentsView*> Toasts;
// Iterates toasts and starts closing them.
std::set<std::string> CloseAllWidgets();
// Called by ToastContentsView when its window is closed.
void RemoveToast(ToastContentsView* toast, bool mark_as_shown);
// Creates new widgets for new toast notifications, and updates |toasts_| and
// |widgets_| correctly.
void UpdateWidgets();
// Repositions all of the widgets based on the current work area.
void RepositionWidgets();
// Repositions widgets to the top edge of the notification toast that was
// just removed, so that the user can click close button without mouse moves.
// See
void RepositionWidgetsWithTarget();
// The base line is an (imaginary) line that would touch the bottom of the
// next created notification if bottom-aligned or its top if top-aligned.
int GetBaseLine(ToastContentsView* last_toast) const;
// Overridden from MessageCenterObserver:
void OnNotificationAdded(const std::string& notification_id) override;
void OnNotificationRemoved(const std::string& notification_id,
bool by_user) override;
void OnNotificationUpdated(const std::string& notification_id) override;
ToastContentsView* FindToast(const std::string& notification_id) const;
// While the toasts are animated, avoid updating the collection, to reduce
// user confusion. Instead, update the collection when all animations are
// done. This method is run when defer counter is zero, may initiate next
// update/animation step.
void OnDeferTimerExpired();
// "ForTest" methods.
views::Widget* GetWidgetForTest(const std::string& id) const;
void CreateRunLoopForTest();
void WaitForTest();
gfx::Rect GetToastRectAt(size_t index) const;
gfx::NativeView parent_;
MessageCenter* message_center_;
MessageCenterTray* tray_;
Toasts toasts_;
PopupAlignmentDelegate* alignment_delegate_;
int defer_counter_;
// This is only used to compare with incoming events, do not assume that
// the toast will be valid if this pointer is non-NULL.
ToastContentsView* latest_toast_entered_;
// Denotes a mode when user is clicking the Close button of toasts in a
// sequence, w/o moving the mouse. We reposition the toasts so the next one
// happens to be right under the mouse, and the user can just dispose of
// multipel toasts by clicking. The mode ends when defer_timer_ expires.
bool user_is_closing_toasts_by_clicking_;
scoped_ptr<base::OneShotTimer<MessagePopupCollection> > defer_timer_;
// The top edge to align the position of the next toast during 'close by
// clicking" mode.
// Only to be used when user_is_closing_toasts_by_clicking_ is true.
int target_top_edge_;
// Weak, only exists temporarily in tests.
scoped_ptr<base::RunLoop> run_loop_for_test_;
scoped_ptr<MessageViewContextMenuController> context_menu_controller_;
// Gives out weak pointers to toast contents views which have an unrelated
// lifetime. Must remain the last member variable.
base::WeakPtrFactory<MessagePopupCollection> weak_factory_;
} // namespace message_center