summaryrefslogtreecommitdiffstats
path: root/ash/system/web_notification
diff options
context:
space:
mode:
authormiket@chromium.org <miket@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-27 16:24:20 +0000
committermiket@chromium.org <miket@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-27 16:24:20 +0000
commitadbc18d00e892b6afce23472f42aca7f31a7f6e7 (patch)
tree7906892756c182b7c9d57b57215e31680ff1a15e /ash/system/web_notification
parentc1f323fb9c6c66ae73ca887f55a3e1d3392ec77b (diff)
downloadchromium_src-adbc18d00e892b6afce23472f42aca7f31a7f6e7.zip
chromium_src-adbc18d00e892b6afce23472f42aca7f31a7f6e7.tar.gz
chromium_src-adbc18d00e892b6afce23472f42aca7f31a7f6e7.tar.bz2
Extract internal classes into separate message_center namespace for eventual move to ui/.
TBR for ash OWNERS. BUG=150872 TBR=ben@chromium.org Review URL: https://chromiumcodereview.appspot.com/10960022 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@159039 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash/system/web_notification')
-rw-r--r--ash/system/web_notification/message_center_bubble.cc229
-rw-r--r--ash/system/web_notification/message_center_bubble.h45
-rw-r--r--ash/system/web_notification/popup_bubble.cc141
-rw-r--r--ash/system/web_notification/popup_bubble.h56
-rw-r--r--ash/system/web_notification/web_notification.cc20
-rw-r--r--ash/system/web_notification/web_notification.h35
-rw-r--r--ash/system/web_notification/web_notification_bubble.cc108
-rw-r--r--ash/system/web_notification/web_notification_bubble.h72
-rw-r--r--ash/system/web_notification/web_notification_contents_view.cc31
-rw-r--r--ash/system/web_notification/web_notification_contents_view.h33
-rw-r--r--ash/system/web_notification/web_notification_list.cc194
-rw-r--r--ash/system/web_notification/web_notification_list.h96
-rw-r--r--ash/system/web_notification/web_notification_tray.cc1056
-rw-r--r--ash/system/web_notification/web_notification_tray.h38
-rw-r--r--ash/system/web_notification/web_notification_view.cc326
-rw-r--r--ash/system/web_notification/web_notification_view.h88
16 files changed, 1515 insertions, 1053 deletions
diff --git a/ash/system/web_notification/message_center_bubble.cc b/ash/system/web_notification/message_center_bubble.cc
new file mode 100644
index 0000000..fee5ec4
--- /dev/null
+++ b/ash/system/web_notification/message_center_bubble.cc
@@ -0,0 +1,229 @@
+// Copyright (c) 2012 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 "ash/system/web_notification/message_center_bubble.h"
+
+#include "ash/system/tray/tray_constants.h"
+#include "ash/system/tray/tray_views.h"
+#include "ash/system/web_notification/web_notification_contents_view.h"
+#include "ash/system/web_notification/web_notification_list.h"
+#include "ash/system/web_notification/web_notification_tray.h"
+#include "ash/system/web_notification/web_notification_view.h"
+#include "grit/ash_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/size.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/layout/grid_layout.h"
+#include "ui/views/painter.h"
+#include "ui/views/view.h"
+#include "ui/views/widget/widget.h"
+
+namespace ash {
+
+using internal::TrayBubbleView;
+using internal::TrayPopupTextButton;
+
+namespace message_center {
+
+// Web Notification Bubble constants
+const int kWebNotificationBubbleMinHeight = 80;
+const int kWebNotificationBubbleMaxHeight = 400;
+
+// The view for the buttons at the bottom of the web notification tray.
+class WebNotificationButtonView : public views::View,
+ public views::ButtonListener {
+ public:
+ explicit WebNotificationButtonView(WebNotificationTray* tray)
+ : tray_(tray),
+ close_all_button_(NULL) {
+ set_background(views::Background::CreateBackgroundPainter(
+ true,
+ views::Painter::CreateVerticalGradient(
+ kHeaderBackgroundColorLight,
+ kHeaderBackgroundColorDark)));
+ set_border(views::Border::CreateSolidSidedBorder(
+ 2, 0, 0, 0, ash::kBorderDarkColor));
+
+ views::GridLayout* layout = new views::GridLayout(this);
+ SetLayoutManager(layout);
+ views::ColumnSet* columns = layout->AddColumnSet(0);
+ columns->AddPaddingColumn(100, 0);
+ columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::CENTER,
+ 0, /* resize percent */
+ views::GridLayout::USE_PREF, 0, 0);
+
+ ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+ close_all_button_ = new TrayPopupTextButton(
+ this, rb.GetLocalizedString(IDS_ASH_WEB_NOTFICATION_TRAY_CLEAR_ALL));
+
+ layout->StartRow(0, 0);
+ layout->AddView(close_all_button_);
+ }
+
+ virtual ~WebNotificationButtonView() {}
+
+ void SetCloseAllVisible(bool visible) {
+ close_all_button_->SetVisible(visible);
+ }
+
+ // Overridden from ButtonListener.
+ virtual void ButtonPressed(views::Button* sender,
+ const ui::Event& event) OVERRIDE {
+ if (sender == close_all_button_)
+ tray_->SendRemoveAllNotifications();
+ }
+
+ private:
+ WebNotificationTray* tray_;
+ internal::TrayPopupTextButton* close_all_button_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebNotificationButtonView);
+};
+
+
+// Message Center contents.
+class MessageCenterContentsView : public WebNotificationContentsView {
+ public:
+ class ScrollContentView : public views::View {
+ public:
+ ScrollContentView() {
+ views::BoxLayout* layout =
+ new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1);
+ layout->set_spread_blank_space(true);
+ SetLayoutManager(layout);
+ }
+
+ virtual ~ScrollContentView() {
+ }
+
+ virtual gfx::Size GetPreferredSize() OVERRIDE {
+ if (!preferred_size_.IsEmpty())
+ return preferred_size_;
+ return views::View::GetPreferredSize();
+ }
+
+ void set_preferred_size(const gfx::Size& size) { preferred_size_ = size; }
+
+ private:
+ gfx::Size preferred_size_;
+ DISALLOW_COPY_AND_ASSIGN(ScrollContentView);
+ };
+
+ explicit MessageCenterContentsView(WebNotificationTray* tray)
+ : WebNotificationContentsView(tray) {
+ SetLayoutManager(
+ new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1));
+ set_background(views::Background::CreateSolidBackground(kBackgroundColor));
+
+ scroll_content_ = new ScrollContentView;
+ scroller_ = new internal::FixedSizedScrollView;
+ scroller_->SetContentsView(scroll_content_);
+ AddChildView(scroller_);
+
+ scroller_->SetPaintToLayer(true);
+ scroller_->SetFillsBoundsOpaquely(false);
+ scroller_->layer()->SetMasksToBounds(true);
+
+ button_view_ = new WebNotificationButtonView(tray);
+ AddChildView(button_view_);
+ }
+
+ void Update(const WebNotificationList::Notifications& notifications) {
+ scroll_content_->RemoveAllChildViews(true);
+ scroll_content_->set_preferred_size(gfx::Size());
+ size_t num_children = 0;
+ for (WebNotificationList::Notifications::const_iterator iter =
+ notifications.begin(); iter != notifications.end(); ++iter) {
+ WebNotificationView* view = new WebNotificationView(tray_, *iter);
+ view->set_scroller(scroller_);
+ scroll_content_->AddChildView(view);
+ if (++num_children >= WebNotificationTray::kMaxVisibleTrayNotifications)
+ break;
+ }
+ if (num_children == 0) {
+ views::Label* label = new views::Label(l10n_util::GetStringUTF16(
+ IDS_ASH_WEB_NOTFICATION_TRAY_NO_MESSAGES));
+ label->SetFont(label->font().DeriveFont(1));
+ label->SetHorizontalAlignment(views::Label::ALIGN_CENTER);
+ label->SetEnabledColor(SK_ColorGRAY);
+ scroll_content_->AddChildView(label);
+ button_view_->SetCloseAllVisible(false);
+ } else {
+ button_view_->SetCloseAllVisible(true);
+ }
+ SizeScrollContent();
+ Layout();
+ if (GetWidget())
+ GetWidget()->GetRootView()->SchedulePaint();
+ }
+
+ size_t NumMessageViewsForTest() const {
+ return scroll_content_->child_count();
+ }
+
+ private:
+ void SizeScrollContent() {
+ gfx::Size scroll_size = scroll_content_->GetPreferredSize();
+ const int button_height = button_view_->GetPreferredSize().height();
+ const int min_height = kWebNotificationBubbleMinHeight - button_height;
+ const int max_height = kWebNotificationBubbleMaxHeight - button_height;
+ int scroll_height = std::min(std::max(
+ scroll_size.height(), min_height), max_height);
+ scroll_size.set_height(scroll_height);
+ if (scroll_height == min_height)
+ scroll_content_->set_preferred_size(scroll_size);
+ else
+ scroll_content_->set_preferred_size(gfx::Size());
+ scroller_->SetFixedSize(scroll_size);
+ scroller_->SizeToPreferredSize();
+ }
+
+ internal::FixedSizedScrollView* scroller_;
+ ScrollContentView* scroll_content_;
+ WebNotificationButtonView* button_view_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessageCenterContentsView);
+};
+
+// Message Center Bubble.
+MessageCenterBubble::MessageCenterBubble(WebNotificationTray* tray) :
+ WebNotificationBubble(tray),
+ contents_view_(NULL) {
+ TrayBubbleView::InitParams init_params = GetInitParams();
+ init_params.max_height = message_center::kWebNotificationBubbleMaxHeight;
+ init_params.can_activate = true;
+ views::View* anchor = tray_->tray_container();
+ bubble_view_ = TrayBubbleView::Create(anchor, this, init_params);
+ contents_view_ = new MessageCenterContentsView(tray);
+
+ Initialize(contents_view_);
+}
+
+MessageCenterBubble::~MessageCenterBubble() {}
+
+size_t MessageCenterBubble::NumMessageViewsForTest() const {
+ return contents_view_->NumMessageViewsForTest();
+}
+
+void MessageCenterBubble::BubbleViewDestroyed() {
+ contents_view_ = NULL;
+ WebNotificationBubble::BubbleViewDestroyed();
+}
+
+void MessageCenterBubble::UpdateBubbleView() {
+ contents_view_->Update(tray_->notification_list()->notifications());
+ bubble_view_->Show();
+ bubble_view_->UpdateBubble();
+}
+
+void MessageCenterBubble::OnClickedOutsideView() {
+ // May delete |this|.
+ tray_->HideMessageCenterBubble();
+}
+
+} // namespace message_center
+
+} // namespace ash
diff --git a/ash/system/web_notification/message_center_bubble.h b/ash/system/web_notification/message_center_bubble.h
new file mode 100644
index 0000000..a0c6a82
--- /dev/null
+++ b/ash/system/web_notification/message_center_bubble.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2012 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.
+
+#ifndef ASH_SYSTEM_NOTIFICATION_MESSAGE_CENTER_BUBBLE_H_
+#define ASH_SYSTEM_NOTIFICATION_MESSAGE_CENTER_BUBBLE_H_
+
+#include "ash/system/web_notification/web_notification_bubble.h"
+
+namespace ash {
+
+class WebNotificationTray;
+
+namespace message_center {
+
+class MessageCenterContentsView;
+
+// Bubble for message center.
+class MessageCenterBubble : public WebNotificationBubble {
+ public:
+ explicit MessageCenterBubble(WebNotificationTray* tray);
+
+ virtual ~MessageCenterBubble();
+
+ size_t NumMessageViewsForTest() const;
+
+ // Overridden from TrayBubbleView::Host.
+ virtual void BubbleViewDestroyed() OVERRIDE;
+
+ virtual void OnClickedOutsideView() OVERRIDE;
+
+ private:
+ // Overridden from WebNotificationBubble.
+ virtual void UpdateBubbleView() OVERRIDE;
+
+ MessageCenterContentsView* contents_view_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessageCenterBubble);
+};
+
+} // namespace message_center
+
+} // namespace ash
+
+#endif // ASH_SYSTEM_NOTIFICATION_MESSAGE_CENTER_BUBBLE_H_
diff --git a/ash/system/web_notification/popup_bubble.cc b/ash/system/web_notification/popup_bubble.cc
new file mode 100644
index 0000000..93e1901
--- /dev/null
+++ b/ash/system/web_notification/popup_bubble.cc
@@ -0,0 +1,141 @@
+// Copyright (c) 2012 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 "ash/system/web_notification/popup_bubble.h"
+
+#include "ash/system/tray/tray_bubble_view.h"
+#include "ash/system/tray/tray_constants.h"
+#include "ash/system/web_notification/web_notification_contents_view.h"
+#include "ash/system/web_notification/web_notification_list.h"
+#include "ash/system/web_notification/web_notification_tray.h"
+#include "ash/system/web_notification/web_notification_view.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/view.h"
+#include "ui/views/widget/widget.h"
+
+namespace ash {
+
+using internal::TrayBubbleView;
+
+namespace message_center {
+
+const int kAutocloseDelaySeconds = 5;
+
+// Popup notifications contents.
+class PopupBubbleContentsView : public WebNotificationContentsView {
+ public:
+ explicit PopupBubbleContentsView(WebNotificationTray* tray)
+ : WebNotificationContentsView(tray) {
+ SetLayoutManager(
+ new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1));
+ set_background(views::Background::CreateSolidBackground(kBackgroundColor));
+
+ content_ = new views::View;
+ content_->SetLayoutManager(
+ new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1));
+ AddChildView(content_);
+
+ content_->SetPaintToLayer(true);
+ content_->SetFillsBoundsOpaquely(false);
+ content_->layer()->SetMasksToBounds(true);
+ }
+
+ void Update(const WebNotificationList::Notifications& popup_notifications) {
+ content_->RemoveAllChildViews(true);
+ for (WebNotificationList::Notifications::const_iterator iter =
+ popup_notifications.begin();
+ iter != popup_notifications.end(); ++iter) {
+ WebNotificationView* view = new WebNotificationView(tray_, *iter);
+ content_->AddChildView(view);
+ }
+ content_->SizeToPreferredSize();
+ Layout();
+ if (GetWidget())
+ GetWidget()->GetRootView()->SchedulePaint();
+ }
+
+ size_t NumMessageViewsForTest() const {
+ return content_->child_count();
+ }
+
+ private:
+ views::View* content_;
+
+ DISALLOW_COPY_AND_ASSIGN(PopupBubbleContentsView);
+};
+
+// PopupBubble
+PopupBubble::PopupBubble(WebNotificationTray* tray) :
+ WebNotificationBubble(tray),
+ contents_view_(NULL),
+ num_popups_(0) {
+ TrayBubbleView::InitParams init_params = GetInitParams();
+ init_params.arrow_color = kBackgroundColor;
+ init_params.close_on_deactivate = false;
+ views::View* anchor = tray_->tray_container();
+ bubble_view_ = TrayBubbleView::Create(anchor, this, init_params);
+ contents_view_ = new PopupBubbleContentsView(tray);
+
+ Initialize(contents_view_);
+}
+
+PopupBubble::~PopupBubble() {}
+
+size_t PopupBubble::NumMessageViewsForTest() const {
+ return contents_view_->NumMessageViewsForTest();
+}
+
+void PopupBubble::BubbleViewDestroyed() {
+ contents_view_ = NULL;
+ WebNotificationBubble::BubbleViewDestroyed();
+}
+
+void PopupBubble::OnMouseEnteredView() {
+ StopAutoCloseTimer();
+ WebNotificationBubble::OnMouseEnteredView();
+}
+
+void PopupBubble::OnMouseExitedView() {
+ StartAutoCloseTimer();
+ WebNotificationBubble::OnMouseExitedView();
+}
+
+void PopupBubble::UpdateBubbleView() {
+ WebNotificationList::Notifications popup_notifications;
+ tray_->notification_list()->GetPopupNotifications(&popup_notifications);
+ if (popup_notifications.size() == 0) {
+ tray_->HideBubble(this); // deletes |this|!
+ return;
+ }
+ // Only update the popup tray if the number of visible popup notifications
+ // has changed.
+ if (popup_notifications.size() != num_popups_) {
+ num_popups_ = popup_notifications.size();
+ contents_view_->Update(popup_notifications);
+ bubble_view_->Show();
+ bubble_view_->UpdateBubble();
+ StartAutoCloseTimer();
+ }
+}
+
+void PopupBubble::StartAutoCloseTimer() {
+ autoclose_.Start(FROM_HERE,
+ base::TimeDelta::FromSeconds(
+ kAutocloseDelaySeconds),
+ this, &PopupBubble::OnAutoClose);
+}
+
+void PopupBubble::StopAutoCloseTimer() {
+ autoclose_.Stop();
+}
+
+void PopupBubble::OnAutoClose() {
+ tray_->notification_list()->MarkPopupsAsShown();
+ num_popups_ = 0;
+ UpdateBubbleView();
+}
+
+} // namespace message_center
+
+} // namespace ash
diff --git a/ash/system/web_notification/popup_bubble.h b/ash/system/web_notification/popup_bubble.h
new file mode 100644
index 0000000..3d4bd7a
--- /dev/null
+++ b/ash/system/web_notification/popup_bubble.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2012 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.
+
+#ifndef ASH_SYSTEM_NOTIFICATION_POPUP_BUBBLE_H_
+#define ASH_SYSTEM_NOTIFICATION_POPUP_BUBBLE_H_
+
+#include "ash/system/web_notification/web_notification_bubble.h"
+#include "base/timer.h"
+
+namespace ash {
+
+class WebNotificationTray;
+
+namespace message_center {
+
+class PopupBubbleContentsView;
+
+// Bubble for popup notifications.
+class PopupBubble : public WebNotificationBubble {
+ public:
+ explicit PopupBubble(WebNotificationTray* tray);
+
+ virtual ~PopupBubble();
+
+ size_t NumMessageViewsForTest() const;
+
+ // Overridden from TrayBubbleView::Host.
+ virtual void BubbleViewDestroyed() OVERRIDE;
+
+ virtual void OnMouseEnteredView() OVERRIDE;
+
+ virtual void OnMouseExitedView() OVERRIDE;
+
+ private:
+ // Overridden from WebNotificationBubble.
+ virtual void UpdateBubbleView() OVERRIDE;
+
+ void StartAutoCloseTimer();
+
+ void StopAutoCloseTimer();
+
+ void OnAutoClose();
+
+ base::OneShotTimer<PopupBubble> autoclose_;
+ PopupBubbleContentsView* contents_view_;
+ size_t num_popups_;
+
+ DISALLOW_COPY_AND_ASSIGN(PopupBubble);
+};
+
+} // namespace message_center
+
+} // namespace ash
+
+#endif // ASH_SYSTEM_NOTIFICATION_POPUP_BUBBLE_H_
diff --git a/ash/system/web_notification/web_notification.cc b/ash/system/web_notification/web_notification.cc
new file mode 100644
index 0000000..3a60669
--- /dev/null
+++ b/ash/system/web_notification/web_notification.cc
@@ -0,0 +1,20 @@
+// Copyright (c) 2012 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 "ash/system/web_notification/web_notification.h"
+
+namespace ash {
+
+namespace message_center {
+
+WebNotification::WebNotification() : is_read(false),
+ shown_as_popup(false) {
+}
+
+WebNotification::~WebNotification() {
+}
+
+} // namespace message_center
+
+} // namespace ash
diff --git a/ash/system/web_notification/web_notification.h b/ash/system/web_notification/web_notification.h
new file mode 100644
index 0000000..8380f57
--- /dev/null
+++ b/ash/system/web_notification/web_notification.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2012 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.
+
+#ifndef ASH_SYSTEM_NOTIFICATION_WEB_NOTIFICATION_H_
+#define ASH_SYSTEM_NOTIFICATION_WEB_NOTIFICATION_H_
+
+#include <string>
+
+#include "base/string16.h"
+#include "ui/gfx/image/image_skia.h"
+
+namespace ash {
+
+namespace message_center {
+
+struct WebNotification {
+ WebNotification();
+ virtual ~WebNotification();
+
+ std::string id;
+ string16 title;
+ string16 message;
+ string16 display_source;
+ std::string extension_id;
+ gfx::ImageSkia image;
+ bool is_read; // True if this has been seen in the message center
+ bool shown_as_popup; // True if this has been shown as a popup notification
+};
+
+} // namespace message_center
+
+} // namespace ash
+
+#endif // ASH_SYSTEM_NOTIFICATION_WEB_NOTIFICATION_H_
diff --git a/ash/system/web_notification/web_notification_bubble.cc b/ash/system/web_notification/web_notification_bubble.cc
new file mode 100644
index 0000000..6dd34a5
--- /dev/null
+++ b/ash/system/web_notification/web_notification_bubble.cc
@@ -0,0 +1,108 @@
+// Copyright (c) 2012 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 "ash/system/web_notification/web_notification_bubble.h"
+
+#include "ash/system/web_notification/web_notification_contents_view.h"
+#include "ash/system/web_notification/web_notification_tray.h"
+#include "ash/system/web_notification/web_notification_view.h"
+#include "base/bind.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_observer.h"
+
+namespace ash {
+
+using internal::TrayBubbleView;
+
+namespace message_center {
+
+// Delay laying out the WebNotificationBubble until all notifications have been
+// added and icons have had a chance to load.
+const int kUpdateDelayMs = 50;
+
+WebNotificationBubble::WebNotificationBubble(WebNotificationTray* tray)
+ : tray_(tray),
+ bubble_view_(NULL),
+ bubble_widget_(NULL),
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
+}
+
+void WebNotificationBubble::Initialize(
+ WebNotificationContentsView* contents_view) {
+ DCHECK(bubble_view_);
+
+ bubble_view_->AddChildView(contents_view);
+
+ bubble_widget_ = views::BubbleDelegateView::CreateBubble(bubble_view_);
+ bubble_widget_->AddObserver(this);
+
+ InitializeAndShowBubble(bubble_widget_, bubble_view_, tray_);
+ UpdateBubbleView();
+}
+
+WebNotificationBubble::~WebNotificationBubble() {
+ if (bubble_view_)
+ bubble_view_->reset_host();
+ if (bubble_widget_) {
+ bubble_widget_->RemoveObserver(this);
+ bubble_widget_->Close();
+ }
+}
+
+void WebNotificationBubble::ScheduleUpdate() {
+ weak_ptr_factory_.InvalidateWeakPtrs(); // Cancel any pending update.
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&WebNotificationBubble::UpdateBubbleView,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::TimeDelta::FromMilliseconds(kUpdateDelayMs));
+}
+
+bool WebNotificationBubble::IsVisible() const {
+ return bubble_widget_ && bubble_widget_->IsVisible();
+}
+
+void WebNotificationBubble::BubbleViewDestroyed() {
+ bubble_view_ = NULL;
+}
+
+void WebNotificationBubble::OnMouseEnteredView() {
+}
+
+void WebNotificationBubble::OnMouseExitedView() {
+}
+
+void WebNotificationBubble::OnClickedOutsideView() {
+ // May delete |this|.
+ tray_->HideMessageCenterBubble();
+}
+
+string16 WebNotificationBubble::GetAccessibleName() {
+ return tray_->GetAccessibleName();
+}
+
+// Overridden from views::WidgetObserver:
+void WebNotificationBubble::OnWidgetClosing(views::Widget* widget) {
+ CHECK_EQ(bubble_widget_, widget);
+ bubble_widget_ = NULL;
+ tray_->HideBubble(this); // Will destroy |this|.
+}
+
+TrayBubbleView::InitParams WebNotificationBubble::GetInitParams() {
+ TrayBubbleView::InitParams init_params(TrayBubbleView::ANCHOR_TYPE_TRAY,
+ tray_->shelf_alignment());
+ init_params.bubble_width = kWebNotificationWidth;
+ if (tray_->shelf_alignment() == SHELF_ALIGNMENT_BOTTOM) {
+ views::View* anchor = tray_->tray_container();
+ gfx::Point bounds(anchor->width() / 2, 0);
+
+ views::View::ConvertPointToWidget(anchor, &bounds);
+ init_params.arrow_offset = bounds.x();
+ }
+ return init_params;
+}
+
+} // namespace message_center
+
+} // namespace ash
diff --git a/ash/system/web_notification/web_notification_bubble.h b/ash/system/web_notification/web_notification_bubble.h
new file mode 100644
index 0000000..0699325
--- /dev/null
+++ b/ash/system/web_notification/web_notification_bubble.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2012 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.
+
+#ifndef ASH_SYSTEM_NOTIFICATION_WEB_NOTIFICATION_BUBBLE_H_
+#define ASH_SYSTEM_NOTIFICATION_WEB_NOTIFICATION_BUBBLE_H_
+
+#include "ash/system/tray/tray_bubble_view.h"
+#include "ui/views/widget/widget_observer.h"
+
+namespace ash {
+
+class WebNotificationTray;
+
+using internal::TrayBubbleView;
+
+namespace message_center {
+
+class WebNotificationContentsView;
+class WebNotificationView;
+
+class WebNotificationBubble : public TrayBubbleView::Host,
+ public views::WidgetObserver {
+ public:
+ explicit WebNotificationBubble(WebNotificationTray* tray);
+
+ virtual ~WebNotificationBubble();
+
+ void Initialize(WebNotificationContentsView* contents_view);
+
+ // Updates the bubble; implementation dependent.
+ virtual void UpdateBubbleView() = 0;
+
+ // Schedules bubble for layout after all notifications have been
+ // added and icons have had a chance to load.
+ void ScheduleUpdate();
+
+ bool IsVisible() const;
+
+ views::Widget* bubble_widget() const { return bubble_widget_; }
+ TrayBubbleView* bubble_view() const { return bubble_view_; }
+
+ // Overridden from TrayBubbleView::Host.
+ virtual void BubbleViewDestroyed() OVERRIDE;
+
+ virtual void OnMouseEnteredView() OVERRIDE;
+
+ virtual void OnMouseExitedView() OVERRIDE;
+
+ virtual void OnClickedOutsideView() OVERRIDE;
+
+ virtual string16 GetAccessibleName() OVERRIDE;
+
+ // Overridden from views::WidgetObserver:
+ virtual void OnWidgetClosing(views::Widget* widget) OVERRIDE;
+
+ protected:
+ TrayBubbleView::InitParams GetInitParams();
+
+ WebNotificationTray* tray_;
+ TrayBubbleView* bubble_view_;
+ views::Widget* bubble_widget_;
+ base::WeakPtrFactory<WebNotificationBubble> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebNotificationBubble);
+};
+
+} // namespace message_center
+
+} // namespace ash
+
+#endif // ASH_SYSTEM_NOTIFICATION_WEB_NOTIFICATION_BUBBLE_H_
diff --git a/ash/system/web_notification/web_notification_contents_view.cc b/ash/system/web_notification/web_notification_contents_view.cc
new file mode 100644
index 0000000..e68a6fa
--- /dev/null
+++ b/ash/system/web_notification/web_notification_contents_view.cc
@@ -0,0 +1,31 @@
+// Copyright (c) 2012 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 "ash/system/web_notification/web_notification_contents_view.h"
+
+#include "ash/system/tray/tray_constants.h"
+#include "ash/system/web_notification/web_notification_tray.h"
+#include "ash/wm/shelf_types.h"
+
+namespace ash {
+
+namespace message_center {
+
+WebNotificationContentsView::WebNotificationContentsView(
+ WebNotificationTray* tray) : tray_(tray) {
+ // TODO(stevenjb): Remove this border when TrayBubbleBorder is integrated
+ // with BubbleBorder.
+ int left = (tray->shelf_alignment() == SHELF_ALIGNMENT_LEFT) ? 0 : 1;
+ int right = (tray->shelf_alignment() == SHELF_ALIGNMENT_RIGHT) ? 0 : 1;
+ int bottom = (tray->shelf_alignment() == SHELF_ALIGNMENT_BOTTOM) ? 0 : 1;
+ set_border(views::Border::CreateSolidSidedBorder(
+ 1, left, bottom, right, ash::kBorderDarkColor));
+ set_notify_enter_exit_on_child(true);
+}
+
+WebNotificationContentsView::~WebNotificationContentsView() {}
+
+} // namespace message_center
+
+} // namespace ash
diff --git a/ash/system/web_notification/web_notification_contents_view.h b/ash/system/web_notification/web_notification_contents_view.h
new file mode 100644
index 0000000..a5f8a8f
--- /dev/null
+++ b/ash/system/web_notification/web_notification_contents_view.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2012 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.
+
+#ifndef ASH_SYSTEM_NOTIFICATION_WEB_NOTIFICATION_CONTENTS_VIEW_H_
+#define ASH_SYSTEM_NOTIFICATION_WEB_NOTIFICATION_CONTENTS_VIEW_H_
+
+#include "ui/views/view.h"
+
+namespace ash {
+
+class WebNotificationTray;
+
+namespace message_center {
+
+// Base class for the contents of a web notification bubble.
+class WebNotificationContentsView : public views::View {
+ public:
+ explicit WebNotificationContentsView(WebNotificationTray* tray);
+ virtual ~WebNotificationContentsView();
+
+ protected:
+ WebNotificationTray* tray_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WebNotificationContentsView);
+};
+
+} // namespace message_center
+
+} // namespace ash
+
+#endif // ASH_SYSTEM_NOTIFICATION_WEB_NOTIFICATION_CONTENTS_VIEW_H_
diff --git a/ash/system/web_notification/web_notification_list.cc b/ash/system/web_notification/web_notification_list.cc
new file mode 100644
index 0000000..1190ddf
--- /dev/null
+++ b/ash/system/web_notification/web_notification_list.cc
@@ -0,0 +1,194 @@
+// Copyright (c) 2012 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 "ash/system/web_notification/web_notification_list.h"
+
+#include "ash/system/web_notification/web_notification_tray.h"
+
+namespace ash {
+
+namespace message_center {
+
+WebNotificationList::WebNotificationList()
+ : message_center_visible_(false),
+ unread_count_(0) {
+}
+
+WebNotificationList::~WebNotificationList() {
+}
+
+void WebNotificationList::SetMessageCenterVisible(bool visible) {
+ if (message_center_visible_ == visible)
+ return;
+ message_center_visible_ = visible;
+ if (!visible) {
+ // When the list is hidden, clear the unread count, and mark all
+ // notifications as read and shown.
+ unread_count_ = 0;
+ for (Notifications::iterator iter = notifications_.begin();
+ iter != notifications_.end(); ++iter) {
+ iter->is_read = true;
+ iter->shown_as_popup = true;
+ }
+ }
+}
+
+void WebNotificationList::AddNotification(const std::string& id,
+ const string16& title,
+ const string16& message,
+ const string16& display_source,
+ const std::string& extension_id) {
+ WebNotification notification;
+ notification.id = id;
+ notification.title = title;
+ notification.message = message;
+ notification.display_source = display_source;
+ notification.extension_id = extension_id;
+ PushNotification(notification);
+}
+
+void WebNotificationList::UpdateNotificationMessage(const std::string& old_id,
+ const std::string& new_id,
+ const string16& title,
+ const string16& message) {
+ Notifications::iterator iter = GetNotification(old_id);
+ if (iter == notifications_.end())
+ return;
+ // Copy and update notification, then move it to the front of the list.
+ WebNotification notification(*iter);
+ notification.id = new_id;
+ notification.title = title;
+ notification.message = message;
+ EraseNotification(iter);
+ PushNotification(notification);
+}
+
+bool WebNotificationList::RemoveNotification(const std::string& id) {
+ Notifications::iterator iter = GetNotification(id);
+ if (iter == notifications_.end())
+ return false;
+ EraseNotification(iter);
+ return true;
+}
+
+void WebNotificationList::RemoveAllNotifications() {
+ notifications_.clear();
+}
+
+void WebNotificationList::SendRemoveNotificationsBySource(
+ WebNotificationTray* tray,
+ const std::string& id) {
+ Notifications::iterator source_iter = GetNotification(id);
+ if (source_iter == notifications_.end())
+ return;
+ string16 display_source = source_iter->display_source;
+ for (Notifications::iterator loopiter = notifications_.begin();
+ loopiter != notifications_.end(); ) {
+ Notifications::iterator curiter = loopiter++;
+ if (curiter->display_source == display_source)
+ tray->SendRemoveNotification(curiter->id);
+ }
+}
+
+void WebNotificationList::SendRemoveNotificationsByExtension(
+ WebNotificationTray* tray,
+ const std::string& id) {
+ Notifications::iterator source_iter = GetNotification(id);
+ if (source_iter == notifications_.end())
+ return;
+ std::string extension_id = source_iter->extension_id;
+ for (Notifications::iterator loopiter = notifications_.begin();
+ loopiter != notifications_.end(); ) {
+ Notifications::iterator curiter = loopiter++;
+ if (curiter->extension_id == extension_id)
+ tray->SendRemoveNotification(curiter->id);
+ }
+}
+
+bool WebNotificationList::SetNotificationImage(const std::string& id,
+ const gfx::ImageSkia& image) {
+ Notifications::iterator iter = GetNotification(id);
+ if (iter == notifications_.end())
+ return false;
+ iter->image = image;
+ return true;
+}
+
+bool WebNotificationList::HasNotification(const std::string& id) {
+ return GetNotification(id) != notifications_.end();
+}
+
+bool WebNotificationList::HasPopupNotifications() {
+ return !notifications_.empty() && !notifications_.front().shown_as_popup;
+}
+
+void WebNotificationList::GetPopupNotifications(
+ WebNotificationList::Notifications* notifications) {
+ Notifications::iterator first, last;
+ GetPopupIterators(first, last);
+ notifications->clear();
+ for (Notifications::iterator iter = first; iter != last; ++iter)
+ notifications->push_back(*iter);
+}
+
+void WebNotificationList::MarkPopupsAsShown() {
+ Notifications::iterator first, last;
+ GetPopupIterators(first, last);
+ for (Notifications::iterator iter = first; iter != last; ++iter)
+ iter->shown_as_popup = true;
+}
+
+WebNotificationList::Notifications::iterator
+WebNotificationList::GetNotification(
+ const std::string& id) {
+ for (Notifications::iterator iter = notifications_.begin();
+ iter != notifications_.end(); ++iter) {
+ if (iter->id == id)
+ return iter;
+ }
+ return notifications_.end();
+}
+
+void WebNotificationList::EraseNotification(
+ WebNotificationList::Notifications::iterator iter) {
+ if (!message_center_visible_ && !iter->is_read)
+ --unread_count_;
+ notifications_.erase(iter);
+}
+
+void WebNotificationList::PushNotification(WebNotification& notification) {
+ // Ensure that notification.id is unique by erasing any existing
+ // notification with the same id (shouldn't normally happen).
+ Notifications::iterator iter = GetNotification(notification.id);
+ if (iter != notifications_.end())
+ EraseNotification(iter);
+ // Add the notification to the front (top) of the list and mark it
+ // unread and unshown.
+ if (!message_center_visible_) {
+ ++unread_count_;
+ notification.is_read = false;
+ notification.shown_as_popup = false;
+ }
+ notifications_.push_front(notification);
+}
+
+void WebNotificationList::GetPopupIterators(Notifications::iterator& first,
+ Notifications::iterator& last) {
+ size_t popup_count = 0;
+ first = notifications_.begin();
+ last = first;
+ while (last != notifications_.end()) {
+ if (last->shown_as_popup)
+ break;
+ ++last;
+ if (popup_count < WebNotificationTray::kMaxVisiblePopupNotifications)
+ ++popup_count;
+ else
+ ++first;
+ }
+}
+
+} // namespace message_center
+
+} // namespace ash
diff --git a/ash/system/web_notification/web_notification_list.h b/ash/system/web_notification/web_notification_list.h
new file mode 100644
index 0000000..a7a78ab
--- /dev/null
+++ b/ash/system/web_notification/web_notification_list.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2012 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.
+
+#ifndef ASH_SYSTEM_NOTIFICATION_WEB_NOTIFICATION_LIST_H_
+#define ASH_SYSTEM_NOTIFICATION_WEB_NOTIFICATION_LIST_H_
+
+#include <list>
+#include <string>
+
+#include "ash/system/web_notification/web_notification.h"
+
+namespace ash {
+
+class WebNotificationTray;
+
+namespace message_center {
+
+// A helper class to manage the list of notifications.
+class WebNotificationList {
+ public:
+ typedef std::list<WebNotification> Notifications;
+
+ WebNotificationList();
+ virtual ~WebNotificationList();
+
+ void SetMessageCenterVisible(bool visible);
+
+ void AddNotification(const std::string& id,
+ const string16& title,
+ const string16& message,
+ const string16& display_source,
+ const std::string& extension_id);
+
+ void UpdateNotificationMessage(const std::string& old_id,
+ const std::string& new_id,
+ const string16& title,
+ const string16& message);
+
+ // Returns true if the notification was removed.
+ bool RemoveNotification(const std::string& id);
+
+ void RemoveAllNotifications();
+
+ void SendRemoveNotificationsBySource(WebNotificationTray* tray,
+ const std::string& id);
+
+ void SendRemoveNotificationsByExtension(WebNotificationTray* tray,
+ const std::string& id);
+
+ // Returns true if the notification exists and was updated.
+ bool SetNotificationImage(const std::string& id,
+ const gfx::ImageSkia& image);
+
+ bool HasNotification(const std::string& id);
+
+ // Returns false if the first notification has been shown as a popup (which
+ // means that all notifications have been shown).
+ bool HasPopupNotifications();
+
+ // Modifies |notifications| to contain the |kMaxVisiblePopupNotifications|
+ // least recent notifications that have not been shown as a popup.
+ void GetPopupNotifications(Notifications* notifications);
+
+ // Marks the popups returned by GetPopupNotifications() as shown.
+ void MarkPopupsAsShown();
+
+ const Notifications& notifications() const { return notifications_; }
+ int unread_count() const { return unread_count_; }
+
+ private:
+ // Iterates through the list and returns the first notification matching |id|
+ // (should always be unique).
+ Notifications::iterator GetNotification(const std::string& id);
+
+ void EraseNotification(Notifications::iterator iter);
+
+ void PushNotification(WebNotification& notification);
+
+ // Returns the |kMaxVisiblePopupNotifications| least recent notifications
+ // that have not been shown as a popup.
+ void GetPopupIterators(Notifications::iterator& first,
+ Notifications::iterator& last);
+
+ Notifications notifications_;
+ bool message_center_visible_;
+ int unread_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebNotificationList);
+};
+
+} // namespace message_center
+
+} // namespace ash
+
+#endif // ASH_SYSTEM_NOTIFICATION_WEB_NOTIFICATION_LIST_H_
diff --git a/ash/system/web_notification/web_notification_tray.cc b/ash/system/web_notification/web_notification_tray.cc
index ecfeb21..27432bb 100644
--- a/ash/system/web_notification/web_notification_tray.cc
+++ b/ash/system/web_notification/web_notification_tray.cc
@@ -9,66 +9,33 @@
#include "ash/system/tray/tray_bubble_view.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/tray/tray_views.h"
+#include "ash/system/web_notification/message_center_bubble.h"
+#include "ash/system/web_notification/popup_bubble.h"
+#include "ash/system/web_notification/web_notification.h"
+#include "ash/system/web_notification/web_notification_bubble.h"
+#include "ash/system/web_notification/web_notification_contents_view.h"
+#include "ash/system/web_notification/web_notification_list.h"
+#include "ash/system/web_notification/web_notification_view.h"
#include "ash/wm/shelf_layout_manager.h"
-#include "base/bind.h"
#include "base/message_loop.h"
#include "base/stringprintf.h"
-#include "base/timer.h"
-#include "base/utf_string_conversions.h"
#include "grit/ash_resources.h"
#include "grit/ash_strings.h"
-#include "ui/aura/window.h"
#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/models/simple_menu_model.h"
#include "ui/base/resource/resource_bundle.h"
-#include "ui/compositor/layer_animation_observer.h"
-#include "ui/compositor/scoped_layer_animation_settings.h"
-#include "ui/gfx/image/image_skia_operations.h"
#include "ui/gfx/screen.h"
-#include "ui/views/controls/button/button.h"
-#include "ui/views/controls/button/image_button.h"
-#include "ui/views/controls/button/menu_button.h"
-#include "ui/views/controls/button/menu_button_listener.h"
-#include "ui/views/controls/label.h"
-#include "ui/views/controls/menu/menu_model_adapter.h"
-#include "ui/views/controls/menu/menu_runner.h"
-#include "ui/views/layout/box_layout.h"
-#include "ui/views/layout/grid_layout.h"
-#include "ui/views/painter.h"
#include "ui/views/widget/widget_observer.h"
namespace {
// Tray constants
-const int kTrayContainerVeritcalPaddingBottomAlignment = 3;
+const int kTrayContainerVerticalPaddingBottomAlignment = 3;
const int kTrayContainerHorizontalPaddingBottomAlignment = 1;
const int kTrayContainerVerticalPaddingVerticalAlignment = 1;
const int kTrayContainerHorizontalPaddingVerticalAlignment = 0;
const int kPaddingFromLeftEdgeOfSystemTrayBottomAlignment = 8;
const int kPaddingFromTopEdgeOfSystemTrayVerticalAlignment = 10;
-// Web Notification Bubble constants
-const int kWebNotificationBubbleMinHeight = 80;
-const int kWebNotificationBubbleMaxHeight = 400;
-// Delay laying out the Bubble until all notifications have been added and icons
-// have had a chance to load.
-const int kUpdateDelayMs = 50;
-const int kAutocloseDelaySeconds = 5;
-const SkColor kMessageCountColor = SkColorSetARGB(0xff, 0xff, 0xff, 0xff);
-const SkColor kNotificationColor = SkColorSetRGB(0xfe, 0xfe, 0xfe);
-const SkColor kNotificationReadColor = SkColorSetRGB(0xfa, 0xfa, 0xfa);
-
-
-// Individual notifications constants
-const int kWebNotificationWidth = 320;
-const int kWebNotificationButtonWidth = 32;
-const int kWebNotificationIconSize = 40;
-
-// Menu constants
-const int kTogglePermissionCommand = 0;
-const int kToggleExtensionCommand = 1;
-const int kShowSettingsCommand = 2;
-
std::string GetNotificationText(int notification_count) {
if (notification_count >= 100)
return "99+";
@@ -85,999 +52,12 @@ namespace ash {
const size_t WebNotificationTray::kMaxVisibleTrayNotifications = 100;
const size_t WebNotificationTray::kMaxVisiblePopupNotifications = 5;
-namespace internal {
-
-struct WebNotification {
- WebNotification()
- : is_read(false),
- shown_as_popup(false) {
- }
- std::string id;
- string16 title;
- string16 message;
- string16 display_source;
- std::string extension_id;
- gfx::ImageSkia image;
- bool is_read; // True if this has been seen in the message center
- bool shown_as_popup; // True if this has been shown as a popup notification
-};
-
-// Web Notification List -------------------------------------------------------
-
-// A helper class to manage the list of notifications.
-class WebNotificationList {
- public:
- typedef std::list<WebNotification> Notifications;
-
- WebNotificationList()
- : message_center_visible_(false),
- unread_count_(0) {
- }
-
- void SetMessageCenterVisible(bool visible) {
- if (message_center_visible_ == visible)
- return;
- message_center_visible_ = visible;
- if (!visible) {
- // When the list is hidden, clear the unread count, and mark all
- // notifications as read and shown.
- unread_count_ = 0;
- for (Notifications::iterator iter = notifications_.begin();
- iter != notifications_.end(); ++iter) {
- iter->is_read = true;
- iter->shown_as_popup = true;
- }
- }
- }
-
- void AddNotification(const std::string& id,
- const string16& title,
- const string16& message,
- const string16& display_source,
- const std::string& extension_id) {
- WebNotification notification;
- notification.id = id;
- notification.title = title;
- notification.message = message;
- notification.display_source = display_source;
- notification.extension_id = extension_id;
- PushNotification(notification);
- }
-
- void UpdateNotificationMessage(const std::string& old_id,
- const std::string& new_id,
- const string16& title,
- const string16& message) {
- Notifications::iterator iter = GetNotification(old_id);
- if (iter == notifications_.end())
- return;
- // Copy and update notification, then move it to the front of the list.
- WebNotification notification(*iter);
- notification.id = new_id;
- notification.title = title;
- notification.message = message;
- EraseNotification(iter);
- PushNotification(notification);
- }
-
- // Returns true if the notification was removed.
- bool RemoveNotification(const std::string& id) {
- Notifications::iterator iter = GetNotification(id);
- if (iter == notifications_.end())
- return false;
- EraseNotification(iter);
- return true;
- }
-
- void RemoveAllNotifications() {
- notifications_.clear();
- }
-
- void SendRemoveNotificationsBySource(WebNotificationTray* tray,
- const std::string& id) {
- Notifications::iterator source_iter = GetNotification(id);
- if (source_iter == notifications_.end())
- return;
- string16 display_source = source_iter->display_source;
- for (Notifications::iterator loopiter = notifications_.begin();
- loopiter != notifications_.end(); ) {
- Notifications::iterator curiter = loopiter++;
- if (curiter->display_source == display_source)
- tray->SendRemoveNotification(curiter->id);
- }
- }
-
- void SendRemoveNotificationsByExtension(WebNotificationTray* tray,
- const std::string& id) {
- Notifications::iterator source_iter = GetNotification(id);
- if (source_iter == notifications_.end())
- return;
- std::string extension_id = source_iter->extension_id;
- for (Notifications::iterator loopiter = notifications_.begin();
- loopiter != notifications_.end(); ) {
- Notifications::iterator curiter = loopiter++;
- if (curiter->extension_id == extension_id)
- tray->SendRemoveNotification(curiter->id);
- }
- }
-
- // Returns true if the notification exists and was updated.
- bool SetNotificationImage(const std::string& id,
- const gfx::ImageSkia& image) {
- Notifications::iterator iter = GetNotification(id);
- if (iter == notifications_.end())
- return false;
- iter->image = image;
- return true;
- }
-
- bool HasNotification(const std::string& id) {
- return GetNotification(id) != notifications_.end();
- }
-
- // Returns false if the first notification has been shown as a popup (which
- // means that all notifications have been shown).
- bool HasPopupNotifications() {
- return !notifications_.empty() && !notifications_.front().shown_as_popup;
- }
-
- // Modifies |notifications| to contain the |kMaxVisiblePopupNotifications|
- // least recent notifications that have not been shown as a popup.
- void GetPopupNotifications(Notifications* notifications) {
- Notifications::iterator first, last;
- GetPopupIterators(first, last);
- notifications->clear();
- for (Notifications::iterator iter = first; iter != last; ++iter)
- notifications->push_back(*iter);
- }
-
- // Marks the popups returned by GetPopupNotifications() as shown.
- void MarkPopupsAsShown() {
- Notifications::iterator first, last;
- GetPopupIterators(first, last);
- for (Notifications::iterator iter = first; iter != last; ++iter)
- iter->shown_as_popup = true;
- }
-
- const Notifications& notifications() const { return notifications_; }
- int unread_count() const { return unread_count_; }
-
- private:
- // Iterates through the list and returns the first notification matching |id|
- // (should always be unique).
- Notifications::iterator GetNotification(const std::string& id) {
- for (Notifications::iterator iter = notifications_.begin();
- iter != notifications_.end(); ++iter) {
- if (iter->id == id)
- return iter;
- }
- return notifications_.end();
- }
-
- void EraseNotification(Notifications::iterator iter) {
- if (!message_center_visible_ && !iter->is_read)
- --unread_count_;
- notifications_.erase(iter);
- }
-
- void PushNotification(WebNotification& notification) {
- // Ensure that notification.id is unique by erasing any existing
- // notification with the same id (shouldn't normally happen).
- Notifications::iterator iter = GetNotification(notification.id);
- if (iter != notifications_.end())
- EraseNotification(iter);
- // Add the notification to the front (top) of the list and mark it
- // unread and unshown.
- if (!message_center_visible_) {
- ++unread_count_;
- notification.is_read = false;
- notification.shown_as_popup = false;
- }
- notifications_.push_front(notification);
- }
-
- // Returns the |kMaxVisiblePopupNotifications| least recent notifications that
- // have not been shown as a popup.
- void GetPopupIterators(Notifications::iterator& first,
- Notifications::iterator& last) {
- size_t popup_count = 0;
- first = notifications_.begin();
- last = first;
- while (last != notifications_.end()) {
- if (last->shown_as_popup)
- break;
- ++last;
- if (popup_count < WebNotificationTray::kMaxVisiblePopupNotifications)
- ++popup_count;
- else
- ++first;
- }
- }
-
- Notifications notifications_;
- bool message_center_visible_;
- int unread_count_;
-
- DISALLOW_COPY_AND_ASSIGN(WebNotificationList);
-};
-
-// Web notification view -------------------------------------------------------
-
-// A dropdown menu for notifications.
-class WebNotificationMenuModel : public ui::SimpleMenuModel,
- public ui::SimpleMenuModel::Delegate {
- public:
- explicit WebNotificationMenuModel(WebNotificationTray* tray,
- const WebNotification& notification)
- : ALLOW_THIS_IN_INITIALIZER_LIST(ui::SimpleMenuModel(this)),
- tray_(tray),
- notification_(notification) {
- // Add 'disable notifications' menu item.
- if (!notification.extension_id.empty()) {
- AddItem(kToggleExtensionCommand,
- GetLabelForCommandId(kToggleExtensionCommand));
- } else if (!notification.display_source.empty()) {
- AddItem(kTogglePermissionCommand,
- GetLabelForCommandId(kTogglePermissionCommand));
- }
- // Add settings menu item.
- if (!notification.display_source.empty()) {
- AddItem(kShowSettingsCommand,
- GetLabelForCommandId(kShowSettingsCommand));
- }
- }
-
- virtual ~WebNotificationMenuModel() {
- }
-
- // Overridden from ui::SimpleMenuModel:
- virtual string16 GetLabelForCommandId(int command_id) const OVERRIDE {
- switch (command_id) {
- case kToggleExtensionCommand:
- return l10n_util::GetStringUTF16(
- IDS_ASH_WEB_NOTFICATION_TRAY_EXTENSIONS_DISABLE);
- case kTogglePermissionCommand:
- return l10n_util::GetStringFUTF16(
- IDS_ASH_WEB_NOTFICATION_TRAY_SITE_DISABLE,
- notification_.display_source);
- case kShowSettingsCommand:
- return l10n_util::GetStringUTF16(
- IDS_ASH_WEB_NOTFICATION_TRAY_SETTINGS);
- default:
- NOTREACHED();
- }
- return string16();
- }
-
- // Overridden from ui::SimpleMenuModel::Delegate:
- virtual bool IsCommandIdChecked(int command_id) const OVERRIDE {
- return false;
- }
-
- virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE {
- return true;
- }
-
- virtual bool GetAcceleratorForCommandId(
- int command_id,
- ui::Accelerator* accelerator) OVERRIDE {
- return false;
- }
-
- virtual void ExecuteCommand(int command_id) OVERRIDE {
- switch (command_id) {
- case kToggleExtensionCommand:
- tray_->DisableByExtension(notification_.id);
- break;
- case kTogglePermissionCommand:
- tray_->DisableByUrl(notification_.id);
- break;
- case kShowSettingsCommand:
- tray_->ShowSettings(notification_.id);
- break;
- default:
- NOTREACHED();
- }
- }
-
- private:
- WebNotificationTray* tray_;
- WebNotification notification_;
-
- DISALLOW_COPY_AND_ASSIGN(WebNotificationMenuModel);
-};
-
-// The view for a notification entry (icon + message + buttons).
-class WebNotificationView : public views::View,
- public views::ButtonListener,
- public ui::ImplicitAnimationObserver {
- public:
- WebNotificationView(WebNotificationTray* tray,
- const WebNotification& notification)
- : tray_(tray),
- notification_(notification),
- icon_(NULL),
- close_button_(NULL),
- scroller_(NULL),
- gesture_scroll_amount_(0.f) {
- InitView(tray, notification);
- }
-
- virtual ~WebNotificationView() {
- }
-
- void set_scroller(views::ScrollView* scroller) { scroller_ = scroller; }
-
- void InitView(WebNotificationTray* tray,
- const WebNotification& notification) {
- set_border(views::Border::CreateSolidSidedBorder(
- 1, 0, 0, 0, kBorderLightColor));
- SkColor bg_color = notification.is_read ?
- kNotificationReadColor : kNotificationColor;
- set_background(views::Background::CreateSolidBackground(bg_color));
- SetPaintToLayer(true);
- SetFillsBoundsOpaquely(false);
-
- icon_ = new views::ImageView;
- icon_->SetImageSize(
- gfx::Size(kWebNotificationIconSize, kWebNotificationIconSize));
- icon_->SetImage(notification.image);
-
- views::Label* title = new views::Label(notification.title);
- title->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
- title->SetFont(title->font().DeriveFont(0, gfx::Font::BOLD));
- views::Label* message = new views::Label(notification.message);
- message->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
- message->SetMultiLine(true);
-
- close_button_ = new views::ImageButton(this);
- close_button_->SetImage(
- views::CustomButton::BS_NORMAL,
- ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
- IDR_AURA_UBER_TRAY_NOTIFY_CLOSE));
- close_button_->SetImageAlignment(views::ImageButton::ALIGN_CENTER,
- views::ImageButton::ALIGN_MIDDLE);
-
- views::GridLayout* layout = new views::GridLayout(this);
- SetLayoutManager(layout);
-
- views::ColumnSet* columns = layout->AddColumnSet(0);
-
- const int padding_width = kTrayPopupPaddingHorizontal/2;
- columns->AddPaddingColumn(0, padding_width);
-
- // Notification Icon.
- columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::LEADING,
- 0, /* resize percent */
- views::GridLayout::FIXED,
- kWebNotificationIconSize, kWebNotificationIconSize);
-
- columns->AddPaddingColumn(0, padding_width);
-
- // Notification message text.
- const int message_width = kWebNotificationWidth - kWebNotificationIconSize -
- kWebNotificationButtonWidth - (padding_width * 3);
- columns->AddColumn(views::GridLayout::FILL, views::GridLayout::LEADING,
- 100, /* resize percent */
- views::GridLayout::FIXED, message_width, message_width);
-
- columns->AddPaddingColumn(0, padding_width);
-
- // Close button.
- columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::LEADING,
- 0, /* resize percent */
- views::GridLayout::FIXED,
- kWebNotificationButtonWidth,
- kWebNotificationButtonWidth);
-
- // Layout rows
- layout->AddPaddingRow(0, kTrayPopupPaddingBetweenItems);
-
- layout->StartRow(0, 0);
- layout->AddView(icon_, 1, 2);
- layout->AddView(title, 1, 1);
- layout->AddView(close_button_, 1, 1);
-
- layout->StartRow(0, 0);
- layout->SkipColumns(2);
- layout->AddView(message, 1, 1);
- layout->AddPaddingRow(0, kTrayPopupPaddingBetweenItems);
- }
-
- // views::View overrides.
- virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE {
- if (event.flags() & ui::EF_RIGHT_MOUSE_BUTTON) {
- ShowMenu(event.location());
- return true;
- }
- tray_->OnClicked(notification_.id);
- return true;
- }
-
- virtual ui::EventResult OnGestureEvent(
- const ui::GestureEvent& event) OVERRIDE {
- if (event.type() == ui::ET_GESTURE_TAP) {
- tray_->OnClicked(notification_.id);
- return ui::ER_CONSUMED;
- }
-
- if (event.type() == ui::ET_GESTURE_LONG_PRESS) {
- ShowMenu(event.location());
- return ui::ER_CONSUMED;
- }
-
- if (event.type() == ui::ET_SCROLL_FLING_START) {
- // The threshold for the fling velocity is computed empirically.
- // The unit is in pixels/second.
- const float kFlingThresholdForClose = 800.f;
- if (fabsf(event.details().velocity_x()) > kFlingThresholdForClose) {
- SlideOutAndClose(event.details().velocity_x() < 0 ? SLIDE_LEFT :
- SLIDE_RIGHT);
- } else if (scroller_) {
- RestoreVisualState();
- scroller_->OnGestureEvent(event);
- }
- return ui::ER_CONSUMED;
- }
-
- if (!event.IsScrollGestureEvent())
- return ui::ER_UNHANDLED;
-
- if (event.type() == ui::ET_GESTURE_SCROLL_BEGIN) {
- gesture_scroll_amount_ = 0.f;
- } else if (event.type() == ui::ET_GESTURE_SCROLL_UPDATE) {
- // The scroll-update events include the incremental scroll amount.
- gesture_scroll_amount_ += event.details().scroll_x();
-
- ui::Transform transform;
- transform.SetTranslateX(gesture_scroll_amount_);
- layer()->SetTransform(transform);
- layer()->SetOpacity(
- 1.f - std::min(fabsf(gesture_scroll_amount_) / width(), 1.f));
-
- } else if (event.type() == ui::ET_GESTURE_SCROLL_END) {
- const float kScrollRatioForClosingNotification = 0.5f;
- float scrolled_ratio = fabsf(gesture_scroll_amount_) / width();
- if (scrolled_ratio >= kScrollRatioForClosingNotification)
- SlideOutAndClose(gesture_scroll_amount_ < 0 ? SLIDE_LEFT : SLIDE_RIGHT);
- else
- RestoreVisualState();
- }
-
- if (scroller_)
- scroller_->OnGestureEvent(event);
- return ui::ER_CONSUMED;
- }
-
- // Overridden from ButtonListener.
- virtual void ButtonPressed(views::Button* sender,
- const ui::Event& event) OVERRIDE {
- if (sender == close_button_)
- tray_->SendRemoveNotification(notification_.id);
- }
-
- // Overridden from ImplicitAnimationObserver.
- virtual void OnImplicitAnimationsCompleted() OVERRIDE {
- tray_->SendRemoveNotification(notification_.id);
- }
-
- private:
- enum SlideDirection {
- SLIDE_LEFT,
- SLIDE_RIGHT
- };
-
- // Shows the menu for the notification.
- void ShowMenu(gfx::Point screen_location) {
- WebNotificationMenuModel menu_model(tray_, notification_);
- if (menu_model.GetItemCount() == 0)
- return;
-
- views::MenuModelAdapter menu_model_adapter(&menu_model);
- views::MenuRunner menu_runner(menu_model_adapter.CreateMenu());
-
- views::View::ConvertPointToScreen(this, &screen_location);
- ignore_result(menu_runner.RunMenuAt(
- GetWidget()->GetTopLevelWidget(),
- NULL,
- gfx::Rect(screen_location, gfx::Size()),
- views::MenuItemView::TOPRIGHT,
- views::MenuRunner::HAS_MNEMONICS));
- }
-
- // Restores the transform and opacity of the view.
- void RestoreVisualState() {
- // Restore the layer state.
- const int kSwipeRestoreDurationMS = 150;
- ui::ScopedLayerAnimationSettings settings(layer()->GetAnimator());
- settings.SetTransitionDuration(
- base::TimeDelta::FromMilliseconds(kSwipeRestoreDurationMS));
- layer()->SetTransform(ui::Transform());
- layer()->SetOpacity(1.f);
- }
-
- // Slides the view out and closes it after the animation.
- void SlideOutAndClose(SlideDirection direction) {
- const int kSwipeOutTotalDurationMS = 150;
- int swipe_out_duration = kSwipeOutTotalDurationMS * layer()->opacity();
- ui::ScopedLayerAnimationSettings settings(layer()->GetAnimator());
- settings.SetTransitionDuration(
- base::TimeDelta::FromMilliseconds(swipe_out_duration));
- settings.AddObserver(this);
-
- ui::Transform transform;
- transform.SetTranslateX(direction == SLIDE_LEFT ? -width() : width());
- layer()->SetTransform(transform);
- layer()->SetOpacity(0.f);
- }
-
- WebNotificationTray* tray_;
- WebNotification notification_;
- views::ImageView* icon_;
- views::ImageButton* close_button_;
-
- views::ScrollView* scroller_;
- float gesture_scroll_amount_;
-
- DISALLOW_COPY_AND_ASSIGN(WebNotificationView);
-};
-
-// The view for the buttons at the bottom of the web notification tray.
-class WebNotificationButtonView : public views::View,
- public views::ButtonListener {
- public:
- explicit WebNotificationButtonView(WebNotificationTray* tray)
- : tray_(tray),
- close_all_button_(NULL) {
- set_background(views::Background::CreateBackgroundPainter(
- true,
- views::Painter::CreateVerticalGradient(
- kHeaderBackgroundColorLight,
- kHeaderBackgroundColorDark)));
- set_border(views::Border::CreateSolidSidedBorder(
- 2, 0, 0, 0, ash::kBorderDarkColor));
-
- views::GridLayout* layout = new views::GridLayout(this);
- SetLayoutManager(layout);
- views::ColumnSet* columns = layout->AddColumnSet(0);
- columns->AddPaddingColumn(100, 0);
- columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::CENTER,
- 0, /* resize percent */
- views::GridLayout::USE_PREF, 0, 0);
-
- ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
- close_all_button_ = new TrayPopupTextButton(
- this, rb.GetLocalizedString(IDS_ASH_WEB_NOTFICATION_TRAY_CLEAR_ALL));
-
- layout->StartRow(0, 0);
- layout->AddView(close_all_button_);
- }
-
- virtual ~WebNotificationButtonView() {
- }
-
- void SetCloseAllVisible(bool visible) {
- close_all_button_->SetVisible(visible);
- }
-
- // Overridden from ButtonListener.
- virtual void ButtonPressed(views::Button* sender,
- const ui::Event& event) OVERRIDE {
- if (sender == close_all_button_)
- tray_->SendRemoveAllNotifications();
- }
-
- private:
- WebNotificationTray* tray_;
- TrayPopupTextButton* close_all_button_;
-
- DISALLOW_COPY_AND_ASSIGN(WebNotificationButtonView);
-};
-
-// Web notification bubble contents --------------------------------------------
-
-// Base class for the contents of a web notification bubble.
-class WebContentsView : public views::View {
- public:
- explicit WebContentsView(WebNotificationTray* tray)
- : tray_(tray) {
- // TODO(stevenjb): Remove this border when TrayBubbleBorder is integrated
- // with BubbleBorder.
- int left = (tray->shelf_alignment() == SHELF_ALIGNMENT_LEFT) ? 0 : 1;
- int right = (tray->shelf_alignment() == SHELF_ALIGNMENT_RIGHT) ? 0 : 1;
- int bottom = (tray->shelf_alignment() == SHELF_ALIGNMENT_BOTTOM) ? 0 : 1;
- set_border(views::Border::CreateSolidSidedBorder(
- 1, left, bottom, right, ash::kBorderDarkColor));
- set_notify_enter_exit_on_child(true);
- }
- virtual ~WebContentsView() {}
-
- protected:
- WebNotificationTray* tray_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(WebContentsView);
-};
-
-// Message Center contents.
-class MessageCenterContentsView : public WebContentsView {
- public:
- class ScrollContentView : public views::View {
- public:
- ScrollContentView() {
- views::BoxLayout* layout =
- new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1);
- layout->set_spread_blank_space(true);
- SetLayoutManager(layout);
- }
-
- virtual ~ScrollContentView() {};
-
- virtual gfx::Size GetPreferredSize() OVERRIDE {
- if (!preferred_size_.IsEmpty())
- return preferred_size_;
- return views::View::GetPreferredSize();
- }
-
- void set_preferred_size(const gfx::Size& size) { preferred_size_ = size; }
-
- private:
- gfx::Size preferred_size_;
- DISALLOW_COPY_AND_ASSIGN(ScrollContentView);
- };
-
- explicit MessageCenterContentsView(WebNotificationTray* tray)
- : WebContentsView(tray) {
- SetLayoutManager(
- new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1));
- set_background(views::Background::CreateSolidBackground(kBackgroundColor));
-
- scroll_content_ = new ScrollContentView;
- scroller_ = new internal::FixedSizedScrollView;
- scroller_->SetContentsView(scroll_content_);
- AddChildView(scroller_);
-
- scroller_->SetPaintToLayer(true);
- scroller_->SetFillsBoundsOpaquely(false);
- scroller_->layer()->SetMasksToBounds(true);
-
- button_view_ = new internal::WebNotificationButtonView(tray);
- AddChildView(button_view_);
- }
-
- void Update(const WebNotificationList::Notifications& notifications) {
- scroll_content_->RemoveAllChildViews(true);
- scroll_content_->set_preferred_size(gfx::Size());
- size_t num_children = 0;
- for (WebNotificationList::Notifications::const_iterator iter =
- notifications.begin(); iter != notifications.end(); ++iter) {
- WebNotificationView* view = new WebNotificationView(tray_, *iter);
- view->set_scroller(scroller_);
- scroll_content_->AddChildView(view);
- if (++num_children >= WebNotificationTray::kMaxVisibleTrayNotifications)
- break;
- }
- if (num_children == 0) {
- views::Label* label = new views::Label(l10n_util::GetStringUTF16(
- IDS_ASH_WEB_NOTFICATION_TRAY_NO_MESSAGES));
- label->SetFont(label->font().DeriveFont(1));
- label->SetHorizontalAlignment(views::Label::ALIGN_CENTER);
- label->SetEnabledColor(SK_ColorGRAY);
- scroll_content_->AddChildView(label);
- button_view_->SetCloseAllVisible(false);
- } else {
- button_view_->SetCloseAllVisible(true);
- }
- SizeScrollContent();
- Layout();
- if (GetWidget())
- GetWidget()->GetRootView()->SchedulePaint();
- }
-
- size_t MessageViewsForTest() const {
- return scroll_content_->child_count();
- }
-
- private:
- void SizeScrollContent() {
- gfx::Size scroll_size = scroll_content_->GetPreferredSize();
- const int button_height = button_view_->GetPreferredSize().height();
- const int min_height = kWebNotificationBubbleMinHeight - button_height;
- const int max_height = kWebNotificationBubbleMaxHeight - button_height;
- int scroll_height = std::min(std::max(
- scroll_size.height(), min_height), max_height);
- scroll_size.set_height(scroll_height);
- if (scroll_height == min_height)
- scroll_content_->set_preferred_size(scroll_size);
- else
- scroll_content_->set_preferred_size(gfx::Size());
- scroller_->SetFixedSize(scroll_size);
- scroller_->SizeToPreferredSize();
- }
-
- internal::FixedSizedScrollView* scroller_;
- ScrollContentView* scroll_content_;
- internal::WebNotificationButtonView* button_view_;
-
- DISALLOW_COPY_AND_ASSIGN(MessageCenterContentsView);
-};
-
-// Popup notifications contents.
-class PopupBubbleContentsView : public WebContentsView {
- public:
- explicit PopupBubbleContentsView(WebNotificationTray* tray)
- : WebContentsView(tray) {
- SetLayoutManager(
- new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1));
- set_background(views::Background::CreateSolidBackground(kBackgroundColor));
-
- content_ = new views::View;
- content_->SetLayoutManager(
- new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1));
- AddChildView(content_);
-
- content_->SetPaintToLayer(true);
- content_->SetFillsBoundsOpaquely(false);
- content_->layer()->SetMasksToBounds(true);
- }
-
- void Update(const WebNotificationList::Notifications& popup_notifications) {
- content_->RemoveAllChildViews(true);
- for (WebNotificationList::Notifications::const_iterator iter =
- popup_notifications.begin();
- iter != popup_notifications.end(); ++iter) {
- WebNotificationView* view = new WebNotificationView(tray_, *iter);
- content_->AddChildView(view);
- }
- content_->SizeToPreferredSize();
- Layout();
- if (GetWidget())
- GetWidget()->GetRootView()->SchedulePaint();
- }
-
- size_t MessageViewsForTest() const {
- return content_->child_count();
- }
-
- private:
- views::View* content_;
-
- DISALLOW_COPY_AND_ASSIGN(PopupBubbleContentsView);
-};
-
-} // namespace internal
-
-using internal::TrayBubbleView;
-using internal::WebNotificationList;
-using internal::WebContentsView;
-
-// Web notification bubbles ----------------------------------------------------
-
-class WebNotificationTray::Bubble : public TrayBubbleView::Host,
- public views::WidgetObserver {
- public:
- explicit Bubble(WebNotificationTray* tray)
- : tray_(tray),
- bubble_view_(NULL),
- bubble_widget_(NULL),
- ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
- }
-
- void Initialize(WebContentsView* contents_view) {
- DCHECK(bubble_view_);
-
- bubble_view_->AddChildView(contents_view);
-
- bubble_widget_ = views::BubbleDelegateView::CreateBubble(bubble_view_);
- bubble_widget_->AddObserver(this);
-
- InitializeAndShowBubble(bubble_widget_, bubble_view_, tray_);
- UpdateBubbleView();
- }
-
- virtual ~Bubble() {
- if (bubble_view_)
- bubble_view_->reset_host();
- if (bubble_widget_) {
- bubble_widget_->RemoveObserver(this);
- bubble_widget_->Close();
- }
- }
-
- void ScheduleUpdate() {
- weak_ptr_factory_.InvalidateWeakPtrs(); // Cancel any pending update.
- MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- base::Bind(&WebNotificationTray::Bubble::UpdateBubbleView,
- weak_ptr_factory_.GetWeakPtr()),
- base::TimeDelta::FromMilliseconds(kUpdateDelayMs));
- }
-
- // Updates the bubble; implementation dependent.
- virtual void UpdateBubbleView() = 0;
-
- bool IsVisible() const {
- return bubble_widget_ && bubble_widget_->IsVisible();
- }
-
- views::Widget* bubble_widget() const { return bubble_widget_; }
- TrayBubbleView* bubble_view() const { return bubble_view_; }
-
- // Overridden from TrayBubbleView::Host.
- virtual void BubbleViewDestroyed() OVERRIDE {
- bubble_view_ = NULL;
- }
-
- virtual void OnMouseEnteredView() OVERRIDE {
- }
-
- virtual void OnMouseExitedView() OVERRIDE {
- }
-
- virtual void OnClickedOutsideView() OVERRIDE {
- // May delete |this|.
- tray_->HideMessageCenterBubble();
- }
-
- virtual string16 GetAccessibleName() OVERRIDE {
- return tray_->GetAccessibleName();
- }
-
- // Overridden from views::WidgetObserver:
- virtual void OnWidgetClosing(views::Widget* widget) OVERRIDE {
- CHECK_EQ(bubble_widget_, widget);
- bubble_widget_ = NULL;
- tray_->HideBubble(this); // Will destroy |this|.
- }
-
- protected:
- TrayBubbleView::InitParams GetInitParams() {
- TrayBubbleView::InitParams init_params(TrayBubbleView::ANCHOR_TYPE_TRAY,
- tray_->shelf_alignment());
- init_params.bubble_width = kWebNotificationWidth;
- if (tray_->shelf_alignment() == SHELF_ALIGNMENT_BOTTOM) {
- views::View* anchor = tray_->tray_container();
- gfx::Point bounds(anchor->width() / 2, 0);
- ConvertPointToWidget(anchor, &bounds);
- init_params.arrow_offset = bounds.x();
- }
- return init_params;
- }
-
- protected:
- WebNotificationTray* tray_;
- TrayBubbleView* bubble_view_;
- views::Widget* bubble_widget_;
- base::WeakPtrFactory<Bubble> weak_ptr_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(Bubble);
-};
-
-// Bubble for message center.
-class WebNotificationTray::MessageCenterBubble :
- public WebNotificationTray::Bubble {
- public:
- explicit MessageCenterBubble(WebNotificationTray* tray) :
- WebNotificationTray::Bubble(tray),
- contents_view_(NULL) {
- TrayBubbleView::InitParams init_params = GetInitParams();
- init_params.max_height = kWebNotificationBubbleMaxHeight;
- init_params.can_activate = true;
- views::View* anchor = tray_->tray_container();
- bubble_view_ = TrayBubbleView::Create(anchor, this, init_params);
- contents_view_ = new internal::MessageCenterContentsView(tray);
-
- Initialize(contents_view_);
- }
-
- virtual ~MessageCenterBubble() {}
-
- size_t MessageViewsForTest() const {
- return contents_view_->MessageViewsForTest();
- }
-
- // Overridden from TrayBubbleView::Host.
- virtual void BubbleViewDestroyed() OVERRIDE {
- contents_view_ = NULL;
- WebNotificationTray::Bubble::BubbleViewDestroyed();
- }
-
- private:
- // Overridden from Bubble.
- virtual void UpdateBubbleView() OVERRIDE {
- contents_view_->Update(tray_->notification_list()->notifications());
- bubble_view_->Show();
- bubble_view_->UpdateBubble();
- }
-
- internal::MessageCenterContentsView* contents_view_;
-
- DISALLOW_COPY_AND_ASSIGN(MessageCenterBubble);
-};
-
-// Bubble for popup notifications.
-class WebNotificationTray::PopupBubble : public WebNotificationTray::Bubble {
- public:
- explicit PopupBubble(WebNotificationTray* tray) :
- WebNotificationTray::Bubble(tray),
- contents_view_(NULL),
- num_popups_(0) {
- TrayBubbleView::InitParams init_params = GetInitParams();
- init_params.arrow_color = kBackgroundColor;
- init_params.close_on_deactivate = false;
- views::View* anchor = tray_->tray_container();
- bubble_view_ = TrayBubbleView::Create(anchor, this, init_params);
- contents_view_ = new internal::PopupBubbleContentsView(tray);
-
- Initialize(contents_view_);
- }
-
- virtual ~PopupBubble() {}
-
- size_t MessageViewsForTest() const {
- return contents_view_->MessageViewsForTest();
- }
-
- // Overridden from TrayBubbleView::Host.
- virtual void BubbleViewDestroyed() OVERRIDE {
- contents_view_ = NULL;
- WebNotificationTray::Bubble::BubbleViewDestroyed();
- }
-
- virtual void OnMouseEnteredView() OVERRIDE {
- StopAutoCloseTimer();
- WebNotificationTray::Bubble::OnMouseEnteredView();
- }
-
- virtual void OnMouseExitedView() OVERRIDE {
- StartAutoCloseTimer();
- WebNotificationTray::Bubble::OnMouseExitedView();
- }
-
- private:
- // Overridden from Bubble.
- virtual void UpdateBubbleView() OVERRIDE {
- WebNotificationList::Notifications popup_notifications;
- tray_->notification_list()->GetPopupNotifications(&popup_notifications);
- if (popup_notifications.size() == 0) {
- tray_->HideBubble(this); // deletes |this|!
- return;
- }
- // Only update the popup tray if the number of visible popup notifications
- // has changed.
- if (popup_notifications.size() != num_popups_) {
- num_popups_ = popup_notifications.size();
- contents_view_->Update(popup_notifications);
- bubble_view_->Show();
- bubble_view_->UpdateBubble();
- StartAutoCloseTimer();
- }
- }
-
- void StartAutoCloseTimer() {
- autoclose_.Start(FROM_HERE,
- base::TimeDelta::FromSeconds(kAutocloseDelaySeconds),
- this, &PopupBubble::OnAutoClose);
- }
-
- void StopAutoCloseTimer() {
- autoclose_.Stop();
- }
-
- void OnAutoClose() {
- tray_->notification_list()->MarkPopupsAsShown();
- num_popups_ = 0;
- UpdateBubbleView();
- }
-
- base::OneShotTimer<PopupBubble> autoclose_;
- internal::PopupBubbleContentsView* contents_view_;
- size_t num_popups_;
-
- DISALLOW_COPY_AND_ASSIGN(PopupBubble);
-};
-
-// WebNotificationTray ---------------------------------------------------------
+using message_center::MessageCenterBubble;
+using message_center::PopupBubble;
+using message_center::WebNotification;
+using message_center::WebNotificationBubble;
+using message_center::WebNotificationList;
+using message_center::WebNotificationView;
WebNotificationTray::WebNotificationTray(
internal::StatusAreaWidget* status_area_widget)
@@ -1248,7 +228,7 @@ string16 WebNotificationTray::GetAccessibleName() {
IDS_ASH_WEB_NOTIFICATION_TRAY_ACCESSIBLE_NAME);
}
-// Private methods invoked by Bubble and its child classes
+// Private methods invoked by WebNotificationBubble and its child classes
void WebNotificationTray::SendRemoveNotification(const std::string& id) {
// If this is the only notification in the list, close the bubble.
@@ -1369,7 +349,7 @@ void WebNotificationTray::UpdateTrayAndBubble() {
UpdateTray();
}
-void WebNotificationTray::HideBubble(Bubble* bubble) {
+void WebNotificationTray::HideBubble(WebNotificationBubble* bubble) {
if (bubble == message_center_bubble()) {
HideMessageCenterBubble();
} else if (bubble == popup_bubble()) {
@@ -1394,13 +374,13 @@ void WebNotificationTray::RemoveAllNotificationsForTest() {
size_t WebNotificationTray::GetMessageCenterNotificationCountForTest() const {
if (!message_center_bubble())
return 0;
- return message_center_bubble()->MessageViewsForTest();
+ return message_center_bubble()->NumMessageViewsForTest();
}
size_t WebNotificationTray::GetPopupNotificationCountForTest() const {
if (!popup_bubble())
return 0;
- return popup_bubble()->MessageViewsForTest();
+ return popup_bubble()->NumMessageViewsForTest();
}
} // namespace ash
diff --git a/ash/system/web_notification/web_notification_tray.h b/ash/system/web_notification/web_notification_tray.h
index b76a714..3aa6fa6 100644
--- a/ash/system/web_notification/web_notification_tray.h
+++ b/ash/system/web_notification/web_notification_tray.h
@@ -7,9 +7,9 @@
#include "ash/ash_export.h"
#include "ash/system/tray/tray_background_view.h"
+#include "ash/system/tray/tray_views.h"
#include "ash/system/user/login_status.h"
#include "base/gtest_prod_util.h"
-#include "ui/aura/event_filter.h"
namespace aura {
class LocatedEvent;
@@ -27,6 +27,12 @@ namespace ash {
namespace internal {
class StatusAreaWidget;
+}
+
+namespace message_center {
+class MessageCenterBubble;
+class PopupBubble;
+class WebNotificationBubble;
class WebNotificationButtonView;
class WebNotificationList;
class WebNotificationMenuModel;
@@ -131,13 +137,13 @@ class ASH_EXPORT WebNotificationTray : public internal::TrayBackgroundView,
static const size_t kMaxVisiblePopupNotifications;
private:
- class Bubble;
- class MessageCenterBubble;
- class PopupBubble;
- friend class internal::WebNotificationButtonView;
- friend class internal::WebNotificationMenuModel;
- friend class internal::WebNotificationList;
- friend class internal::WebNotificationView;
+ friend class message_center::MessageCenterBubble;
+ friend class message_center::PopupBubble;
+ friend class message_center::WebNotificationBubble;
+ friend class message_center::WebNotificationButtonView;
+ friend class message_center::WebNotificationList;
+ friend class message_center::WebNotificationMenuModel;
+ friend class message_center::WebNotificationView;
FRIEND_TEST_ALL_PREFIXES(WebNotificationTrayTest, WebNotifications);
FRIEND_TEST_ALL_PREFIXES(WebNotificationTrayTest, WebNotificationPopupBubble);
FRIEND_TEST_ALL_PREFIXES(WebNotificationTrayTest,
@@ -182,7 +188,7 @@ class ASH_EXPORT WebNotificationTray : public internal::TrayBackgroundView,
void UpdateTrayAndBubble();
// Hides the specified bubble (called when |bubble| is closed from Views).
- void HideBubble(Bubble* bubble);
+ void HideBubble(message_center::WebNotificationBubble* bubble);
// Testing accessors.
size_t GetNotificationCountForTest() const;
@@ -191,17 +197,19 @@ class ASH_EXPORT WebNotificationTray : public internal::TrayBackgroundView,
size_t GetMessageCenterNotificationCountForTest() const;
size_t GetPopupNotificationCountForTest() const;
- internal::WebNotificationList* notification_list() {
+ message_center::WebNotificationList* notification_list() {
return notification_list_.get();
}
- MessageCenterBubble* message_center_bubble() const {
+ message_center::MessageCenterBubble* message_center_bubble() const {
return message_center_bubble_.get();
}
- PopupBubble* popup_bubble() const { return popup_bubble_.get(); }
+ message_center::PopupBubble* popup_bubble() const {
+ return popup_bubble_.get();
+ }
- scoped_ptr<internal::WebNotificationList> notification_list_;
- scoped_ptr<MessageCenterBubble> message_center_bubble_;
- scoped_ptr<PopupBubble> popup_bubble_;
+ scoped_ptr<message_center::WebNotificationList> notification_list_;
+ scoped_ptr<message_center::MessageCenterBubble> message_center_bubble_;
+ scoped_ptr<message_center::PopupBubble> popup_bubble_;
views::ImageButton* button_;
Delegate* delegate_;
bool show_message_center_on_unlock_;
diff --git a/ash/system/web_notification/web_notification_view.cc b/ash/system/web_notification/web_notification_view.cc
new file mode 100644
index 0000000..be6119b
--- /dev/null
+++ b/ash/system/web_notification/web_notification_view.cc
@@ -0,0 +1,326 @@
+// Copyright (c) 2012 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 "ash/system/web_notification/web_notification_view.h"
+
+#include "ash/system/tray/tray_constants.h"
+#include "ash/system/tray/tray_views.h"
+#include "ash/system/web_notification/web_notification.h"
+#include "ash/system/web_notification/web_notification_tray.h"
+#include "grit/ash_resources.h"
+#include "grit/ash_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/models/simple_menu_model.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/controls/menu/menu_model_adapter.h"
+#include "ui/views/controls/menu/menu_runner.h"
+#include "ui/views/layout/grid_layout.h"
+#include "ui/views/widget/widget.h"
+
+namespace ash {
+
+namespace message_center {
+
+const SkColor kNotificationColor = SkColorSetRGB(0xfe, 0xfe, 0xfe);
+const SkColor kNotificationReadColor = SkColorSetRGB(0xfa, 0xfa, 0xfa);
+
+// Menu constants
+const int kTogglePermissionCommand = 0;
+const int kToggleExtensionCommand = 1;
+const int kShowSettingsCommand = 2;
+
+// A dropdown menu for notifications.
+class WebNotificationMenuModel : public ui::SimpleMenuModel,
+ public ui::SimpleMenuModel::Delegate {
+ public:
+ explicit WebNotificationMenuModel(WebNotificationTray* tray,
+ const WebNotification& notification)
+ : ALLOW_THIS_IN_INITIALIZER_LIST(ui::SimpleMenuModel(this)),
+ tray_(tray),
+ notification_(notification) {
+ // Add 'disable notifications' menu item.
+ if (!notification.extension_id.empty()) {
+ AddItem(kToggleExtensionCommand,
+ GetLabelForCommandId(kToggleExtensionCommand));
+ } else if (!notification.display_source.empty()) {
+ AddItem(kTogglePermissionCommand,
+ GetLabelForCommandId(kTogglePermissionCommand));
+ }
+ // Add settings menu item.
+ if (!notification.display_source.empty()) {
+ AddItem(kShowSettingsCommand,
+ GetLabelForCommandId(kShowSettingsCommand));
+ }
+ }
+
+ virtual ~WebNotificationMenuModel() {
+ }
+
+ // Overridden from ui::SimpleMenuModel:
+ virtual string16 GetLabelForCommandId(int command_id) const OVERRIDE {
+ switch (command_id) {
+ case kToggleExtensionCommand:
+ return l10n_util::GetStringUTF16(
+ IDS_ASH_WEB_NOTFICATION_TRAY_EXTENSIONS_DISABLE);
+ case kTogglePermissionCommand:
+ return l10n_util::GetStringFUTF16(
+ IDS_ASH_WEB_NOTFICATION_TRAY_SITE_DISABLE,
+ notification_.display_source);
+ case kShowSettingsCommand:
+ return l10n_util::GetStringUTF16(
+ IDS_ASH_WEB_NOTFICATION_TRAY_SETTINGS);
+ default:
+ NOTREACHED();
+ }
+ return string16();
+ }
+
+ // Overridden from ui::SimpleMenuModel::Delegate:
+ virtual bool IsCommandIdChecked(int command_id) const OVERRIDE {
+ return false;
+ }
+
+ virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE {
+ return false;
+ }
+
+ virtual bool GetAcceleratorForCommandId(
+ int command_id,
+ ui::Accelerator* accelerator) OVERRIDE {
+ return false;
+ }
+
+ virtual void ExecuteCommand(int command_id) OVERRIDE {
+ switch (command_id) {
+ case kToggleExtensionCommand:
+ tray_->DisableByExtension(notification_.id);
+ break;
+ case kTogglePermissionCommand:
+ tray_->DisableByUrl(notification_.id);
+ break;
+ case kShowSettingsCommand:
+ tray_->ShowSettings(notification_.id);
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+
+ private:
+ WebNotificationTray* tray_;
+ WebNotification notification_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebNotificationMenuModel);
+};
+
+WebNotificationView::WebNotificationView(WebNotificationTray* tray,
+ const WebNotification& notification)
+ : tray_(tray),
+ notification_(notification),
+ icon_(NULL),
+ close_button_(NULL),
+ scroller_(NULL),
+ gesture_scroll_amount_(0.f) {
+ InitView(tray, notification);
+}
+
+WebNotificationView::~WebNotificationView() {
+}
+
+void WebNotificationView::InitView(WebNotificationTray* tray,
+ const WebNotification& notification) {
+ set_border(views::Border::CreateSolidSidedBorder(
+ 1, 0, 0, 0, kBorderLightColor));
+ SkColor bg_color = notification.is_read ?
+ kNotificationReadColor : kNotificationColor;
+ set_background(views::Background::CreateSolidBackground(bg_color));
+ SetPaintToLayer(true);
+ SetFillsBoundsOpaquely(false);
+
+ icon_ = new views::ImageView;
+ icon_->SetImageSize(
+ gfx::Size(kWebNotificationIconSize, kWebNotificationIconSize));
+ icon_->SetImage(notification.image);
+
+ views::Label* title = new views::Label(notification.title);
+ title->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+ title->SetFont(title->font().DeriveFont(0, gfx::Font::BOLD));
+ views::Label* message = new views::Label(notification.message);
+ message->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+ message->SetMultiLine(true);
+
+ close_button_ = new views::ImageButton(this);
+ close_button_->SetImage(
+ views::CustomButton::BS_NORMAL,
+ ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
+ IDR_AURA_UBER_TRAY_NOTIFY_CLOSE));
+ close_button_->SetImageAlignment(views::ImageButton::ALIGN_CENTER,
+ views::ImageButton::ALIGN_MIDDLE);
+
+ views::GridLayout* layout = new views::GridLayout(this);
+ SetLayoutManager(layout);
+
+ views::ColumnSet* columns = layout->AddColumnSet(0);
+
+ const int padding_width = kTrayPopupPaddingHorizontal/2;
+ columns->AddPaddingColumn(0, padding_width);
+
+ // Notification Icon.
+ columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::LEADING,
+ 0, /* resize percent */
+ views::GridLayout::FIXED,
+ kWebNotificationIconSize, kWebNotificationIconSize);
+
+ columns->AddPaddingColumn(0, padding_width);
+
+ // Notification message text.
+ const int message_width = kWebNotificationWidth - kWebNotificationIconSize -
+ kWebNotificationButtonWidth - (padding_width * 3);
+ columns->AddColumn(views::GridLayout::FILL, views::GridLayout::LEADING,
+ 100, /* resize percent */
+ views::GridLayout::FIXED, message_width, message_width);
+
+ columns->AddPaddingColumn(0, padding_width);
+
+ // Close button.
+ columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::LEADING,
+ 0, /* resize percent */
+ views::GridLayout::FIXED,
+ kWebNotificationButtonWidth,
+ kWebNotificationButtonWidth);
+
+ // Layout rows
+ layout->AddPaddingRow(0, kTrayPopupPaddingBetweenItems);
+
+ layout->StartRow(0, 0);
+ layout->AddView(icon_, 1, 2);
+ layout->AddView(title, 1, 1);
+ layout->AddView(close_button_, 1, 1);
+
+ layout->StartRow(0, 0);
+ layout->SkipColumns(2);
+ layout->AddView(message, 1, 1);
+ layout->AddPaddingRow(0, kTrayPopupPaddingBetweenItems);
+}
+
+bool WebNotificationView::OnMousePressed(const ui::MouseEvent& event) {
+ if (event.flags() & ui::EF_RIGHT_MOUSE_BUTTON) {
+ ShowMenu(event.location());
+ return true;
+ }
+ tray_->OnClicked(notification_.id);
+ return true;
+}
+
+ui::EventResult WebNotificationView::OnGestureEvent(
+ const ui::GestureEvent& event) {
+ if (event.type() == ui::ET_GESTURE_TAP) {
+ tray_->OnClicked(notification_.id);
+ return ui::ER_CONSUMED;
+ }
+
+ if (event.type() == ui::ET_GESTURE_LONG_PRESS) {
+ ShowMenu(event.location());
+ return ui::ER_CONSUMED;
+ }
+
+ if (event.type() == ui::ET_SCROLL_FLING_START) {
+ // The threshold for the fling velocity is computed empirically.
+ // The unit is in pixels/second.
+ const float kFlingThresholdForClose = 800.f;
+ if (fabsf(event.details().velocity_x()) > kFlingThresholdForClose) {
+ SlideOutAndClose(event.details().velocity_x() < 0 ? SLIDE_LEFT :
+ SLIDE_RIGHT);
+ } else if (scroller_) {
+ RestoreVisualState();
+ scroller_->OnGestureEvent(event);
+ }
+ return ui::ER_CONSUMED;
+ }
+
+ if (!event.IsScrollGestureEvent())
+ return ui::ER_UNHANDLED;
+
+ if (event.type() == ui::ET_GESTURE_SCROLL_BEGIN) {
+ gesture_scroll_amount_ = 0.f;
+ } else if (event.type() == ui::ET_GESTURE_SCROLL_UPDATE) {
+ // The scroll-update events include the incremental scroll amount.
+ gesture_scroll_amount_ += event.details().scroll_x();
+
+ ui::Transform transform;
+ transform.SetTranslateX(gesture_scroll_amount_);
+ layer()->SetTransform(transform);
+ layer()->SetOpacity(
+ 1.f - std::min(fabsf(gesture_scroll_amount_) / width(), 1.f));
+
+ } else if (event.type() == ui::ET_GESTURE_SCROLL_END) {
+ const float kScrollRatioForClosingNotification = 0.5f;
+ float scrolled_ratio = fabsf(gesture_scroll_amount_) / width();
+ if (scrolled_ratio >= kScrollRatioForClosingNotification)
+ SlideOutAndClose(gesture_scroll_amount_ < 0 ? SLIDE_LEFT : SLIDE_RIGHT);
+ else
+ RestoreVisualState();
+ }
+
+ if (scroller_)
+ scroller_->OnGestureEvent(event);
+ return ui::ER_CONSUMED;
+}
+
+void WebNotificationView::ButtonPressed(views::Button* sender,
+ const ui::Event& event) {
+ if (sender == close_button_)
+ tray_->SendRemoveNotification(notification_.id);
+}
+
+void WebNotificationView::OnImplicitAnimationsCompleted() {
+ tray_->SendRemoveNotification(notification_.id);
+}
+
+void WebNotificationView::ShowMenu(gfx::Point screen_location) {
+ WebNotificationMenuModel menu_model(tray_, notification_);
+ if (menu_model.GetItemCount() == 0)
+ return;
+
+ views::MenuModelAdapter menu_model_adapter(&menu_model);
+ views::MenuRunner menu_runner(menu_model_adapter.CreateMenu());
+
+ views::View::ConvertPointToScreen(this, &screen_location);
+ ignore_result(menu_runner.RunMenuAt(
+ GetWidget()->GetTopLevelWidget(),
+ NULL,
+ gfx::Rect(screen_location, gfx::Size()),
+ views::MenuItemView::TOPRIGHT,
+ views::MenuRunner::HAS_MNEMONICS));
+}
+
+void WebNotificationView::RestoreVisualState() {
+ // Restore the layer state.
+ const int kSwipeRestoreDurationMS = 150;
+ ui::ScopedLayerAnimationSettings settings(layer()->GetAnimator());
+ settings.SetTransitionDuration(
+ base::TimeDelta::FromMilliseconds(kSwipeRestoreDurationMS));
+ layer()->SetTransform(ui::Transform());
+ layer()->SetOpacity(1.f);
+}
+
+void WebNotificationView::SlideOutAndClose(SlideDirection direction) {
+ const int kSwipeOutTotalDurationMS = 150;
+ int swipe_out_duration = kSwipeOutTotalDurationMS * layer()->opacity();
+ ui::ScopedLayerAnimationSettings settings(layer()->GetAnimator());
+ settings.SetTransitionDuration(
+ base::TimeDelta::FromMilliseconds(swipe_out_duration));
+ settings.AddObserver(this);
+
+ ui::Transform transform;
+ transform.SetTranslateX(direction == SLIDE_LEFT ? -width() : width());
+ layer()->SetTransform(transform);
+ layer()->SetOpacity(0.f);
+}
+
+} // namespace message_center
+
+} // namespace ash
diff --git a/ash/system/web_notification/web_notification_view.h b/ash/system/web_notification/web_notification_view.h
new file mode 100644
index 0000000..1acbff9
--- /dev/null
+++ b/ash/system/web_notification/web_notification_view.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2012 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.
+
+#ifndef ASH_SYSTEM_NOTIFICATION_WEB_NOTIFICATION_VIEW_H_
+#define ASH_SYSTEM_NOTIFICATION_WEB_NOTIFICATION_VIEW_H_
+
+#include "ash/system/web_notification/web_notification.h"
+#include "ui/compositor/layer_animation_observer.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/view.h"
+
+namespace views {
+class ImageButton;
+class ImageView;
+class ScrollView;
+}
+
+namespace ash {
+
+class WebNotificationTray;
+
+namespace message_center {
+
+// Individual notifications constants
+const int kWebNotificationWidth = 320;
+const int kWebNotificationButtonWidth = 32;
+const int kWebNotificationIconSize = 40;
+
+// The view for a notification entry (icon + message + buttons).
+class WebNotificationView : public views::View,
+ public views::ButtonListener,
+ public ui::ImplicitAnimationObserver {
+ public:
+ WebNotificationView(WebNotificationTray* tray,
+ const WebNotification& notification);
+
+ virtual ~WebNotificationView();
+
+ void set_scroller(views::ScrollView* scroller) { scroller_ = scroller; }
+
+ void InitView(WebNotificationTray* tray,
+ const WebNotification& notification);
+
+ // views::View overrides.
+ virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE;
+
+ virtual ui::EventResult OnGestureEvent(
+ const ui::GestureEvent& event) OVERRIDE;
+
+ // Overridden from ButtonListener.
+ virtual void ButtonPressed(views::Button* sender,
+ const ui::Event& event) OVERRIDE;
+
+ // Overridden from ImplicitAnimationObserver.
+ virtual void OnImplicitAnimationsCompleted() OVERRIDE;
+
+ private:
+ enum SlideDirection {
+ SLIDE_LEFT,
+ SLIDE_RIGHT
+ };
+
+ // Shows the menu for the notification.
+ void ShowMenu(gfx::Point screen_location);
+
+ // Restores the transform and opacity of the view.
+ void RestoreVisualState();
+
+ // Slides the view out and closes it after the animation.
+ void SlideOutAndClose(SlideDirection direction);
+
+ WebNotificationTray* tray_;
+ WebNotification notification_;
+ views::ImageView* icon_;
+ views::ImageButton* close_button_;
+
+ views::ScrollView* scroller_;
+ float gesture_scroll_amount_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebNotificationView);
+};
+
+} // namespace message_center
+
+} // namespace ash
+
+#endif // ASH_SYSTEM_NOTIFICATION_WEB_NOTIFICATION_VIEW_H_