diff options
author | miket@chromium.org <miket@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-27 16:24:20 +0000 |
---|---|---|
committer | miket@chromium.org <miket@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-27 16:24:20 +0000 |
commit | adbc18d00e892b6afce23472f42aca7f31a7f6e7 (patch) | |
tree | 7906892756c182b7c9d57b57215e31680ff1a15e /ash/system/web_notification | |
parent | c1f323fb9c6c66ae73ca887f55a3e1d3392ec77b (diff) | |
download | chromium_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')
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_ |