summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordewittj@chromium.org <dewittj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-31 06:07:33 +0000
committerdewittj@chromium.org <dewittj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-31 06:07:33 +0000
commitc404bd8b7f13cb246d6537cd6ab9d2caa5ff4012 (patch)
tree38f000ab2a0b0c3b45790fd93dcb96cdd3a8cdd9
parent360ef9f95352c591a2c07b384f4b9cd9a8d6974a (diff)
downloadchromium_src-c404bd8b7f13cb246d6537cd6ab9d2caa5ff4012.zip
chromium_src-c404bd8b7f13cb246d6537cd6ab9d2caa5ff4012.tar.gz
chromium_src-c404bd8b7f13cb246d6537cd6ab9d2caa5ff4012.tar.bz2
Implement message center on Windows.
This takes the existing message center code for Ash, and refactors it into two layers - a system-specific layer (WebNotificationTray) and a platform-independent (modulo Views) layer (MessageCenterTray). The WebNotificationTray is responsible for rendering the tray icon and noticing system changes that cause differences in rendering. The MessageCenterTray delegates responsibility for rendering the message center and notification bubbles to the WebNotificationTray. This patch also adds a Windows port of MessageCenterTray - MessageCenterTrayWin. BUG=168605 TEST: message_center_unittests browser_tests (WebNotificationTrayWinTest.*) ash_unittests (WebNotificationTrayTest.*) Review URL: https://chromiumcodereview.appspot.com/11819048 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@179807 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--ash/system/web_notification/web_notification_tray.cc255
-rw-r--r--ash/system/web_notification/web_notification_tray.h71
-rw-r--r--ash/system/web_notification/web_notification_tray_unittest.cc56
-rw-r--r--chrome/browser/notifications/message_center_notification_manager.cc7
-rw-r--r--chrome/browser/notifications/message_center_notification_manager.h2
-rw-r--r--chrome/browser/ui/views/message_center/notification_bubble_wrapper_win.cc109
-rw-r--r--chrome/browser/ui/views/message_center/notification_bubble_wrapper_win.h65
-rw-r--r--chrome/browser/ui/views/message_center/web_notification_tray_win.cc237
-rw-r--r--chrome/browser/ui/views/message_center/web_notification_tray_win.h95
-rw-r--r--chrome/browser/ui/views/message_center/web_notification_tray_win_browsertest.cc214
-rw-r--r--chrome/chrome_browser_ui.gypi4
-rw-r--r--chrome/chrome_tests.gypi3
-rw-r--r--ui/message_center/message_center.gyp4
-rw-r--r--ui/message_center/message_center.h2
-rw-r--r--ui/message_center/message_center_tray.cc111
-rw-r--r--ui/message_center/message_center_tray.h73
-rw-r--r--ui/message_center/message_center_tray_delegate.h44
-rw-r--r--ui/message_center/message_center_tray_unittest.cc208
-rw-r--r--ui/views/bubble/tray_bubble_view.cc9
-rw-r--r--ui/views/bubble/tray_bubble_view.h8
20 files changed, 1373 insertions, 204 deletions
diff --git a/ash/system/web_notification/web_notification_tray.cc b/ash/system/web_notification/web_notification_tray.cc
index b04b761..b3e8df4 100644
--- a/ash/system/web_notification/web_notification_tray.cc
+++ b/ash/system/web_notification/web_notification_tray.cc
@@ -4,38 +4,40 @@
#include "ash/system/web_notification/web_notification_tray.h"
+#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "ash/shell_window_ids.h"
#include "ash/system/status_area_widget.h"
+#include "ash/system/tray/tray_background_view.h"
#include "ash/system/tray/tray_bubble_wrapper.h"
-#include "ash/system/tray/tray_constants.h"
-#include "ash/system/tray/tray_views.h"
#include "ash/wm/shelf_layout_manager.h"
-#include "base/message_loop.h"
-#include "base/stringprintf.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/resource/resource_bundle.h"
#include "ui/gfx/screen.h"
+#include "ui/message_center/message_bubble_base.h"
#include "ui/message_center/message_center_bubble.h"
+#include "ui/message_center/message_center_tray_delegate.h"
#include "ui/message_center/message_popup_bubble.h"
#include "ui/message_center/quiet_mode_bubble.h"
#include "ui/views/bubble/tray_bubble_view.h"
#include "ui/views/widget/widget_observer.h"
-namespace {
+#if defined(OS_CHROMEOS)
-// Tray constants
-const int kTrayContainerVerticalPaddingBottomAlignment = 3;
-const int kTrayContainerHorizontalPaddingBottomAlignment = 1;
-const int kTrayContainerVerticalPaddingVerticalAlignment = 1;
-const int kTrayContainerHorizontalPaddingVerticalAlignment = 0;
-const int kPaddingFromLeftEdgeOfSystemTrayBottomAlignment = 8;
-const int kPaddingFromTopEdgeOfSystemTrayVerticalAlignment = 10;
+namespace message_center {
-} // namespace
+MessageCenterTrayDelegate* CreateMessageCenterTray() {
+ // On Windows+Ash the Tray will not be hosted in ash::Shell.
+ NOTREACHED();
+ return NULL;
+}
+
+} // namespace message_center
+
+#endif // defined(OS_CHROMEOS)
namespace ash {
@@ -70,6 +72,9 @@ class WebNotificationBubbleWrapper {
// Convenience accessors.
views::TrayBubbleView* bubble_view() const { return bubble_->bubble_view(); }
+ views::Widget* bubble_widget() const {
+ return bubble_wrapper_->bubble_widget();
+ }
private:
scoped_ptr<message_center::MessageBubbleBase> bubble_;
@@ -80,46 +85,38 @@ class WebNotificationBubbleWrapper {
WebNotificationTray::WebNotificationTray(
internal::StatusAreaWidget* status_area_widget)
- : internal::TrayBackgroundView(status_area_widget),
+ : TrayBackgroundView(status_area_widget),
button_(NULL),
show_message_center_on_unlock_(false) {
- message_center_ = ash::Shell::GetInstance()->message_center();
- message_center_->AddObserver(this);
button_ = new views::ImageButton(this);
button_->set_triggerable_event_flags(
ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON);
tray_container()->AddChildView(button_);
SetVisible(false);
- UpdateTray();
+ message_center_tray_.reset(new message_center::MessageCenterTray(
+ this,
+ Shell::GetInstance()->message_center()));
+ OnMessageCenterTrayChanged();
}
WebNotificationTray::~WebNotificationTray() {
- // Ensure the message center doesn't notify an object under destruction.
- message_center_->RemoveObserver(this);
// Release any child views that might have back pointers before ~View().
message_center_bubble_.reset();
popup_bubble_.reset();
- if (quiet_mode_bubble() && quiet_mode_bubble_->GetBubbleWidget())
- quiet_mode_bubble_->GetBubbleWidget()->RemoveObserver(this);
+ if (quiet_mode_bubble() && quiet_mode_bubble()->GetBubbleWidget())
+ quiet_mode_bubble()->GetBubbleWidget()->RemoveObserver(this);
quiet_mode_bubble_.reset();
}
-void WebNotificationTray::ShowMessageCenterBubble() {
- if (status_area_widget()->login_status() == user::LOGGED_IN_LOCKED)
- return;
- if (quiet_mode_bubble())
- quiet_mode_bubble_.reset();
- if (message_center_bubble()) {
- UpdateTray();
- return;
- }
- // Indicate that the message center is visible. Clears the unread count.
- message_center_->SetMessageCenterVisible(true);
- UpdateTray();
- HidePopupBubble();
- message_center::MessageCenterBubble* bubble =
- new message_center::MessageCenterBubble(message_center_);
- // Sets the maximum height of the bubble based on the screen.
+// Public methods.
+
+bool WebNotificationTray::ShowMessageCenter() {
+ if (!ShouldShowMessageCenter())
+ return false;
+
+ message_center::MessageCenterBubble* message_center_bubble =
+ new message_center::MessageCenterBubble(message_center());
+
// TODO(mukai): move this to WebNotificationBubbleWrapper if it's safe
// to set the height of the popup.
int max_height = 0;
@@ -132,53 +129,65 @@ void WebNotificationTray::ShowMessageCenterBubble() {
aura::Window* status_area_window = status_area_widget()->GetNativeWindow();
max_height = status_area_window->GetBoundsInRootWindow().bottom();
}
- bubble->SetMaxHeight(max_height);
+ message_center_bubble->SetMaxHeight(max_height);
message_center_bubble_.reset(
- new internal::WebNotificationBubbleWrapper(this, bubble));
+ new internal::WebNotificationBubbleWrapper(this, message_center_bubble));
status_area_widget()->SetHideSystemNotifications(true);
GetShelfLayoutManager()->UpdateAutoHideState();
+ return true;
+}
+
+void WebNotificationTray::UpdateMessageCenter() {
+ if (message_center_bubble())
+ message_center_bubble()->bubble()->ScheduleUpdate();
}
-void WebNotificationTray::HideMessageCenterBubble() {
+void WebNotificationTray::HideMessageCenter() {
if (!message_center_bubble())
return;
message_center_bubble_.reset();
show_message_center_on_unlock_ = false;
- message_center_->SetMessageCenterVisible(false);
- UpdateTray();
status_area_widget()->SetHideSystemNotifications(false);
GetShelfLayoutManager()->UpdateAutoHideState();
}
void WebNotificationTray::SetHidePopupBubble(bool hide) {
if (hide)
- HidePopupBubble();
+ message_center_tray_->HidePopupBubble();
else
- ShowPopupBubble();
+ message_center_tray_->ShowPopupBubble();
}
-void WebNotificationTray::ShowPopupBubble() {
- if (status_area_widget()->login_status() == user::LOGGED_IN_LOCKED)
- return;
- if (message_center_bubble())
- return;
- if (!status_area_widget()->ShouldShowWebNotifications())
- return;
- UpdateTray();
- if (popup_bubble()) {
- popup_bubble()->bubble()->ScheduleUpdate();
- } else if (message_center_->HasPopupNotifications()) {
- popup_bubble_.reset(
- new internal::WebNotificationBubbleWrapper(
- this, new message_center::MessagePopupBubble(message_center_)));
+bool WebNotificationTray::ShowPopups() {
+ if (status_area_widget()->login_status() == user::LOGGED_IN_LOCKED ||
+ message_center_bubble() ||
+ !status_area_widget()->ShouldShowWebNotifications()) {
+ return false;
}
+ message_center::MessagePopupBubble* popup_bubble =
+ new message_center::MessagePopupBubble(message_center());
+ popup_bubble_.reset(new internal::WebNotificationBubbleWrapper(
+ this, popup_bubble));
+ return true;
}
-void WebNotificationTray::HidePopupBubble() {
+void WebNotificationTray::UpdatePopups() {
+ if (popup_bubble())
+ popup_bubble()->bubble()->ScheduleUpdate();
+};
+
+void WebNotificationTray::HidePopups() {
popup_bubble_.reset();
}
+// Private methods.
+
+bool WebNotificationTray::ShouldShowMessageCenter() {
+ return status_area_widget()->login_status() != user::LOGGED_IN_LOCKED &&
+ status_area_widget()->ShouldShowWebNotifications();
+}
+
bool WebNotificationTray::ShouldShowQuietModeBubble(const ui::Event& event) {
// TODO(mukai): Add keyboard event handler.
if (!event.IsMouseEvent())
@@ -195,29 +204,31 @@ void WebNotificationTray::ShowQuietModeBubble() {
Shell::GetPrimaryRootWindow(),
internal::kShellWindowId_SettingBubbleContainer);
quiet_mode_bubble_.reset(new message_center::QuietModeBubble(
- button_, parent, message_center_->notification_list()));
- quiet_mode_bubble_->GetBubbleWidget()->StackAtTop();
- quiet_mode_bubble_->GetBubbleWidget()->AddObserver(this);
+ button_,
+ parent,
+ message_center_tray_->message_center()->notification_list()));
+ quiet_mode_bubble()->GetBubbleWidget()->StackAtTop();
+ quiet_mode_bubble()->GetBubbleWidget()->AddObserver(this);
}
void WebNotificationTray::UpdateAfterLoginStatusChange(
user::LoginStatus login_status) {
+ // The status icon should be always visible except for lock screen / login
+ // screen, to allow quiet mode and settings.
+ SetVisible((login_status != user::LOGGED_IN_NONE) &&
+ (login_status != user::LOGGED_IN_LOCKED));
+
if (login_status == user::LOGGED_IN_LOCKED) {
- if (message_center_bubble()) {
- message_center_bubble_.reset();
- show_message_center_on_unlock_ = true;
- }
- HidePopupBubble();
+ show_message_center_on_unlock_ =
+ message_center_tray_->HideMessageCenterBubble();
+ message_center_tray_->HidePopupBubble();
} else {
+ // Only try once to show the message center bubble on login status change,
+ // so always set |show_message_center_on_unlock_| to false.
if (show_message_center_on_unlock_)
- ShowMessageCenterBubble();
+ message_center_tray_->ShowMessageCenterBubble();
show_message_center_on_unlock_ = false;
}
- // The status icon should be always visible except for lock screen / login
- // screen, to allow quiet mode and settings.
- SetVisible((login_status != user::LOGGED_IN_NONE) &&
- (login_status != user::LOGGED_IN_LOCKED));
- UpdateTray();
}
bool WebNotificationTray::ShouldBlockLauncherAutoHide() const {
@@ -226,13 +237,13 @@ bool WebNotificationTray::ShouldBlockLauncherAutoHide() const {
bool WebNotificationTray::IsMessageCenterBubbleVisible() const {
return (message_center_bubble() &&
- message_center_bubble_->bubble()->IsVisible());
+ message_center_bubble()->bubble()->IsVisible());
}
bool WebNotificationTray::IsMouseInNotificationBubble() const {
if (!popup_bubble())
return false;
- return popup_bubble_->bubble_view()->GetBoundsInScreen().Contains(
+ return popup_bubble()->bubble_view()->GetBoundsInScreen().Contains(
Shell::GetScreen()->GetCursorScreenPoint());
}
@@ -241,24 +252,24 @@ void WebNotificationTray::SetShelfAlignment(ShelfAlignment alignment) {
return;
internal::TrayBackgroundView::SetShelfAlignment(alignment);
// Destroy any existing bubble so that it will be rebuilt correctly.
- HideMessageCenterBubble();
- HidePopupBubble();
+ message_center_tray_->HideMessageCenterBubble();
+ message_center_tray_->HidePopupBubble();
}
void WebNotificationTray::AnchorUpdated() {
- if (popup_bubble_.get()) {
- popup_bubble_->bubble_view()->UpdateBubble();
+ if (popup_bubble()) {
+ popup_bubble()->bubble_view()->UpdateBubble();
// Ensure that the notification buble is above the launcher/status area.
- popup_bubble_->bubble_view()->GetWidget()->StackAtTop();
- UpdateBubbleViewArrow(popup_bubble_->bubble_view());
+ popup_bubble()->bubble_view()->GetWidget()->StackAtTop();
+ UpdateBubbleViewArrow(popup_bubble()->bubble_view());
}
- if (message_center_bubble_.get()) {
- message_center_bubble_->bubble_view()->UpdateBubble();
- UpdateBubbleViewArrow(message_center_bubble_->bubble_view());
+ if (message_center_bubble()) {
+ message_center_bubble()->bubble_view()->UpdateBubble();
+ UpdateBubbleViewArrow(message_center_bubble()->bubble_view());
}
// Quiet mode settings bubble has to be on top.
- if (quiet_mode_bubble() && quiet_mode_bubble_->GetBubbleWidget())
- quiet_mode_bubble_->GetBubbleWidget()->StackAtTop();
+ if (quiet_mode_bubble() && quiet_mode_bubble()->GetBubbleWidget())
+ quiet_mode_bubble()->GetBubbleWidget()->StackAtTop();
}
string16 WebNotificationTray::GetAccessibleNameForTray() {
@@ -270,9 +281,9 @@ void WebNotificationTray::HideBubbleWithView(
const views::TrayBubbleView* bubble_view) {
if (message_center_bubble() &&
bubble_view == message_center_bubble()->bubble_view()) {
- HideMessageCenterBubble();
+ message_center_tray_->HideMessageCenterBubble();
} else if (popup_bubble() && bubble_view == popup_bubble()->bubble_view()) {
- HidePopupBubble();
+ message_center_tray_->HidePopupBubble();
}
}
@@ -282,7 +293,10 @@ bool WebNotificationTray::PerformAction(const ui::Event& event) {
return true;
}
quiet_mode_bubble_.reset();
- ToggleMessageCenterBubble();
+ if (message_center_bubble())
+ message_center_tray_->HideMessageCenterBubble();
+ else
+ message_center_tray_->ShowMessageCenterBubble();
return true;
}
@@ -307,9 +321,10 @@ string16 WebNotificationTray::GetAccessibleNameForBubble() {
return GetAccessibleNameForTray();
}
-gfx::Rect WebNotificationTray::GetAnchorRect(views::Widget* anchor_widget,
- AnchorType anchor_type,
- AnchorAlignment anchor_alignment) {
+gfx::Rect WebNotificationTray::GetAnchorRect(
+ views::Widget* anchor_widget,
+ views::TrayBubbleView::AnchorType anchor_type,
+ views::TrayBubbleView::AnchorAlignment anchor_alignment) {
return GetBubbleAnchorRect(anchor_widget, anchor_type, anchor_alignment);
}
@@ -317,24 +332,6 @@ void WebNotificationTray::HideBubble(const views::TrayBubbleView* bubble_view) {
HideBubbleWithView(bubble_view);
}
-void WebNotificationTray::OnMessageCenterChanged(bool new_notification) {
- if (message_center_bubble()) {
- if (message_center_->NotificationCount() == 0)
- HideMessageCenterBubble();
- else
- message_center_bubble()->bubble()->ScheduleUpdate();
- }
- if (popup_bubble()) {
- if (message_center_->NotificationCount() == 0)
- HidePopupBubble();
- else
- popup_bubble()->bubble()->ScheduleUpdate();
- }
- UpdateTray();
- if (new_notification)
- ShowPopupBubble();
-}
-
void WebNotificationTray::ButtonPressed(views::Button* sender,
const ui::Event& event) {
DCHECK_EQ(button_, sender);
@@ -342,25 +339,17 @@ void WebNotificationTray::ButtonPressed(views::Button* sender,
}
void WebNotificationTray::OnWidgetClosing(views::Widget* widget) {
- if (quiet_mode_bubble() && quiet_mode_bubble_->GetBubbleWidget() == widget) {
+ if (quiet_mode_bubble() && quiet_mode_bubble()->GetBubbleWidget() == widget) {
widget->RemoveObserver(this);
}
quiet_mode_bubble_.reset();
}
-// Private methods
-
-void WebNotificationTray::ToggleMessageCenterBubble() {
- if (message_center_bubble())
- HideMessageCenterBubble();
- else
- ShowMessageCenterBubble();
- UpdateTray();
-}
-
-void WebNotificationTray::UpdateTray() {
+void WebNotificationTray::OnMessageCenterTrayChanged() {
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
- if (message_center_->UnreadNotificationCount() > 0) {
+ message_center::MessageCenter* message_center =
+ message_center_tray_->message_center();
+ if (message_center->UnreadNotificationCount() > 0) {
button_->SetImage(views::CustomButton::STATE_NORMAL, rb.GetImageSkiaNamed(
IDR_AURA_UBER_TRAY_NOTIFY_BUTTON_ACTIVE_NORMAL));
button_->SetImage(views::CustomButton::STATE_HOVERED, rb.GetImageSkiaNamed(
@@ -375,7 +364,7 @@ void WebNotificationTray::UpdateTray() {
button_->SetImage(views::CustomButton::STATE_PRESSED, rb.GetImageSkiaNamed(
IDR_AURA_UBER_TRAY_NOTIFY_BUTTON_INACTIVE_PRESSED));
}
- if (message_center_bubble())
+ if (IsMessageCenterBubbleVisible())
button_->SetState(views::CustomButton::STATE_PRESSED);
else
button_->SetState(views::CustomButton::STATE_NORMAL);
@@ -388,26 +377,34 @@ bool WebNotificationTray::ClickedOutsideBubble() {
if (!message_center_bubble() && !quiet_mode_bubble())
return false;
quiet_mode_bubble_.reset();
- HideMessageCenterBubble();
+ message_center_tray_->HideMessageCenterBubble();
return true;
}
+message_center::MessageCenter* WebNotificationTray::message_center() {
+ return message_center_tray_->message_center();
+}
+
// Methods for testing
+bool WebNotificationTray::IsPopupVisible() const {
+ return message_center_tray_->popups_visible();
+}
+
message_center::MessageCenterBubble*
WebNotificationTray::GetMessageCenterBubbleForTest() {
- if (!message_center_bubble_.get())
+ if (!message_center_bubble())
return NULL;
return static_cast<message_center::MessageCenterBubble*>(
- message_center_bubble_->bubble());
+ message_center_bubble()->bubble());
}
message_center::MessagePopupBubble*
WebNotificationTray::GetPopupBubbleForTest() {
- if (!popup_bubble_.get())
+ if (!popup_bubble())
return NULL;
return static_cast<message_center::MessagePopupBubble*>(
- popup_bubble_->bubble());
+ popup_bubble()->bubble());
}
} // namespace ash
diff --git a/ash/system/web_notification/web_notification_tray.h b/ash/system/web_notification/web_notification_tray.h
index bd9e026..585f457 100644
--- a/ash/system/web_notification/web_notification_tray.h
+++ b/ash/system/web_notification/web_notification_tray.h
@@ -7,10 +7,12 @@
#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/message_center/message_center.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/message_center/message_center_tray.h"
+#include "ui/message_center/message_center_tray_delegate.h"
+#include "ui/views/bubble/tray_bubble_view.h"
#include "ui/views/widget/widget_observer.h"
// Status area tray for showing browser and app notifications. This hosts
@@ -21,24 +23,11 @@
// generated by SystemTrayItem). Visibility of one notification type or other
// is controlled by StatusAreaWidget.
-namespace aura {
-class LocatedEvent;
-}
-
-namespace gfx {
-class ImageSkia;
-}
-
-namespace views {
-class ImageButton;
-class TrayBubbleView;
-class Widget;
-}
-
namespace message_center {
+class MessageBubbleBase;
+class MessageCenter;
class MessageCenterBubble;
class MessagePopupBubble;
-class QuietModeBubble;
}
namespace ash {
@@ -51,11 +40,12 @@ class WebNotificationBubbleWrapper;
class ASH_EXPORT WebNotificationTray
: public internal::TrayBackgroundView,
public views::TrayBubbleView::Delegate,
- public message_center::MessageCenter::Observer,
+ public message_center::MessageCenterTrayDelegate,
public views::ButtonListener,
public views::WidgetObserver {
public:
- explicit WebNotificationTray(internal::StatusAreaWidget* status_area_widget);
+ explicit WebNotificationTray(
+ internal::StatusAreaWidget* status_area_widget);
virtual ~WebNotificationTray();
// Set whether or not the popup notifications should be hidden.
@@ -73,10 +63,6 @@ class ASH_EXPORT WebNotificationTray
// Returns true if the mouse is inside the notification bubble.
bool IsMouseInNotificationBubble() const;
- message_center::MessageCenter* message_center() {
- return message_center_;
- }
-
// Overridden from TrayBackgroundView.
virtual void SetShelfAlignment(ShelfAlignment alignment) OVERRIDE;
virtual void AnchorUpdated() OVERRIDE;
@@ -98,9 +84,6 @@ class ASH_EXPORT WebNotificationTray
AnchorAlignment anchor_alignment) OVERRIDE;
virtual void HideBubble(const views::TrayBubbleView* bubble_view) OVERRIDE;
- // Overridden from message_center::MessageCenter::Observer.
- virtual void OnMessageCenterChanged(bool new_notification) OVERRIDE;
-
// Overridden from ButtonListener.
virtual void ButtonPressed(views::Button* sender,
const ui::Event& event) OVERRIDE;
@@ -108,6 +91,17 @@ class ASH_EXPORT WebNotificationTray
// Overridden from WidgetObserver.
virtual void OnWidgetClosing(views::Widget* widget) OVERRIDE;
+ // Overridden from MessageCenterTrayDelegate.
+ virtual void OnMessageCenterTrayChanged() OVERRIDE;
+ virtual bool ShowMessageCenter() OVERRIDE;
+ virtual void UpdateMessageCenter() OVERRIDE;
+ virtual void HideMessageCenter() OVERRIDE;
+ virtual bool ShowPopups() OVERRIDE;
+ virtual void UpdatePopups() OVERRIDE;
+ virtual void HidePopups() OVERRIDE;
+
+ message_center::MessageCenter* message_center();
+
private:
FRIEND_TEST_ALL_PREFIXES(WebNotificationTrayTest, WebNotifications);
FRIEND_TEST_ALL_PREFIXES(WebNotificationTrayTest, WebNotificationPopupBubble);
@@ -115,29 +109,16 @@ class ASH_EXPORT WebNotificationTray
ManyMessageCenterNotifications);
FRIEND_TEST_ALL_PREFIXES(WebNotificationTrayTest, ManyPopupNotifications);
- // Shows or hides the message center bubble.
- void ToggleMessageCenterBubble();
-
- // Shows or updates the message center bubble and hides the popup bubble.
- void ShowMessageCenterBubble();
+ // Queries login status and the status area widget to determine visibility of
+ // the message center.
+ bool ShouldShowMessageCenter();
- // Hides the message center bubble if visible.
- void HideMessageCenterBubble();
-
- // Shows or updates the popup notification bubble if appropriate.
- void ShowPopupBubble();
-
- // Hides the notification bubble if visible.
- void HidePopupBubble();
-
- // Returns true if it should show the quiet mode bubble.
+ // Returns true if it should show the quiet mode bubble.
bool ShouldShowQuietModeBubble(const ui::Event& event);
// Shows the quiet mode bubble.
void ShowQuietModeBubble();
- // Updates the tray icon and visibility.
- void UpdateTray();
internal::WebNotificationBubbleWrapper* message_center_bubble() const {
return message_center_bubble_.get();
@@ -152,14 +133,16 @@ class ASH_EXPORT WebNotificationTray
}
// Testing accessors.
+ bool IsPopupVisible() const;
message_center::MessageCenterBubble* GetMessageCenterBubbleForTest();
message_center::MessagePopupBubble* GetPopupBubbleForTest();
- message_center::MessageCenter* message_center_; // Weak, owned by ash::Shell
+ scoped_ptr<message_center::MessageCenterTray> message_center_tray_;
scoped_ptr<internal::WebNotificationBubbleWrapper> message_center_bubble_;
scoped_ptr<internal::WebNotificationBubbleWrapper> popup_bubble_;
scoped_ptr<message_center::QuietModeBubble> quiet_mode_bubble_;
views::ImageButton* button_;
+
bool show_message_center_on_unlock_;
DISALLOW_COPY_AND_ASSIGN(WebNotificationTray);
diff --git a/ash/system/web_notification/web_notification_tray_unittest.cc b/ash/system/web_notification/web_notification_tray_unittest.cc
index a5a3833..1523a51 100644
--- a/ash/system/web_notification/web_notification_tray_unittest.cc
+++ b/ash/system/web_notification/web_notification_tray_unittest.cc
@@ -9,13 +9,14 @@
#include "ash/root_window_controller.h"
#include "ash/system/status_area_widget.h"
#include "ash/system/tray/system_tray_item.h"
+#include "ash/test/ash_test_base.h"
+#include "base/stringprintf.h"
+#include "base/utf_string_conversions.h"
#include "ui/message_center/message_center_bubble.h"
+#include "ui/message_center/message_center_tray.h"
#include "ui/message_center/message_popup_bubble.h"
#include "ui/message_center/notification_list.h"
#include "ui/notifications/notification_types.h"
-#include "ash/test/ash_test_base.h"
-#include "base/stringprintf.h"
-#include "base/utf_string_conversions.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/view.h"
@@ -34,6 +35,10 @@ WebNotificationTray* GetWebNotificationTray() {
web_notification_tray();
}
+message_center::MessageCenter* get_message_center() {
+ return GetWebNotificationTray()->message_center();
+}
+
class TestDelegate : public message_center::MessageCenter::Delegate {
public:
TestDelegate(message_center::MessageCenter* message_center)
@@ -68,7 +73,7 @@ class TestDelegate : public message_center::MessageCenter::Delegate {
void AddNotification(WebNotificationTray* tray, const std::string& id) {
notification_ids_.insert(id);
- tray->message_center()->AddNotification(
+ get_message_center()->AddNotification(
ui::notifications::NOTIFICATION_TYPE_SIMPLE,
id,
ASCIIToUTF16("Test Web Notification"),
@@ -83,7 +88,7 @@ class TestDelegate : public message_center::MessageCenter::Delegate {
const std::string& new_id) {
notification_ids_.erase(old_id);
notification_ids_.insert(new_id);
- tray->message_center()->UpdateNotification(
+ get_message_center()->UpdateNotification(
old_id, new_id,
ASCIIToUTF16("Updated Web Notification"),
ASCIIToUTF16("Updated message body."),
@@ -91,7 +96,7 @@ class TestDelegate : public message_center::MessageCenter::Delegate {
}
void RemoveNotification(WebNotificationTray* tray, const std::string& id) {
- tray->message_center()->RemoveNotification(id);
+ get_message_center()->RemoveNotification(id);
notification_ids_.erase(id);
}
@@ -118,17 +123,17 @@ TEST_F(WebNotificationTrayTest, WebNotifications) {
// Add a notification.
delegate->AddNotification(tray, "test_id1");
- EXPECT_EQ(1u, tray->message_center()->NotificationCount());
+ EXPECT_EQ(1u, get_message_center()->NotificationCount());
EXPECT_TRUE(message_center->notification_list()->HasNotification("test_id1"));
delegate->AddNotification(tray, "test_id2");
delegate->AddNotification(tray, "test_id2");
- EXPECT_EQ(2u, tray->message_center()->NotificationCount());
+ EXPECT_EQ(2u, get_message_center()->NotificationCount());
EXPECT_TRUE(message_center->notification_list()->HasNotification("test_id2"));
// Ensure that updating a notification does not affect the count.
delegate->UpdateNotification(tray, "test_id2", "test_id3");
delegate->UpdateNotification(tray, "test_id3", "test_id3");
- EXPECT_EQ(2u, tray->message_center()->NotificationCount());
+ EXPECT_EQ(2u, get_message_center()->NotificationCount());
EXPECT_FALSE(delegate->HasNotificationId("test_id2"));
EXPECT_FALSE(message_center->notification_list()->HasNotification(
"test_id2"));
@@ -139,37 +144,37 @@ TEST_F(WebNotificationTrayTest, WebNotifications) {
EXPECT_FALSE(delegate->HasNotificationId("test_id1"));
EXPECT_FALSE(message_center->notification_list()->HasNotification(
"test_id1"));
- EXPECT_EQ(1u, tray->message_center()->NotificationCount());
+ EXPECT_EQ(1u, get_message_center()->NotificationCount());
// Remove the remianing notification.
delegate->RemoveNotification(tray, "test_id3");
- EXPECT_EQ(0u, tray->message_center()->NotificationCount());
+ EXPECT_EQ(0u, get_message_center()->NotificationCount());
EXPECT_FALSE(message_center->notification_list()->HasNotification(
"test_id3"));
}
TEST_F(WebNotificationTrayTest, WebNotificationPopupBubble) {
WebNotificationTray* tray = GetWebNotificationTray();
- scoped_ptr<TestDelegate> delegate(new TestDelegate(tray->message_center()));
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(get_message_center()));
ASSERT_TRUE(tray->GetWidget());
// Adding a notification should show the popup bubble.
delegate->AddNotification(tray, "test_id1");
- EXPECT_TRUE(tray->popup_bubble() != NULL);
+ EXPECT_TRUE(tray->IsPopupVisible());
// Updating a notification should not hide the popup bubble.
delegate->AddNotification(tray, "test_id2");
delegate->UpdateNotification(tray, "test_id2", "test_id3");
- EXPECT_TRUE(tray->popup_bubble() != NULL);
+ EXPECT_TRUE(tray->IsPopupVisible());
// Removing the first notification should not hide the popup bubble.
delegate->RemoveNotification(tray, "test_id1");
- EXPECT_TRUE(tray->popup_bubble() != NULL);
+ EXPECT_TRUE(tray->IsPopupVisible());
// Removing the visible notification should hide the popup bubble.
delegate->RemoveNotification(tray, "test_id3");
- EXPECT_TRUE(tray->popup_bubble() == NULL);
+ EXPECT_FALSE(tray->IsPopupVisible());
}
using message_center::NotificationList;
@@ -177,7 +182,7 @@ using message_center::NotificationList;
TEST_F(WebNotificationTrayTest, ManyMessageCenterNotifications) {
WebNotificationTray* tray = GetWebNotificationTray();
- scoped_ptr<TestDelegate> delegate(new TestDelegate(tray->message_center()));
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(get_message_center()));
// Add the max visible notifications +1, ensure the correct visible number.
size_t notifications_to_add =
@@ -186,18 +191,19 @@ TEST_F(WebNotificationTrayTest, ManyMessageCenterNotifications) {
std::string id = StringPrintf("test_id%d", static_cast<int>(i));
delegate->AddNotification(tray, id);
}
- tray->ShowMessageCenterBubble();
+ bool shown = tray->message_center_tray_->ShowMessageCenterBubble();
+ EXPECT_TRUE(shown);
RunAllPendingInMessageLoop();
EXPECT_TRUE(tray->message_center_bubble() != NULL);
EXPECT_EQ(notifications_to_add,
- tray->message_center()->NotificationCount());
+ get_message_center()->NotificationCount());
EXPECT_EQ(NotificationList::kMaxVisibleMessageCenterNotifications,
tray->GetMessageCenterBubbleForTest()->NumMessageViewsForTest());
}
TEST_F(WebNotificationTrayTest, ManyPopupNotifications) {
WebNotificationTray* tray = GetWebNotificationTray();
- scoped_ptr<TestDelegate> delegate(new TestDelegate(tray->message_center()));
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(get_message_center()));
// Add the max visible popup notifications +1, ensure the correct num visible.
size_t notifications_to_add =
@@ -207,13 +213,15 @@ TEST_F(WebNotificationTrayTest, ManyPopupNotifications) {
delegate->AddNotification(tray, id);
}
// Hide and reshow the bubble so that it is updated immediately, not delayed.
- tray->HidePopupBubble();
- tray->ShowPopupBubble();
- EXPECT_TRUE(tray->popup_bubble() != NULL);
+ tray->SetHidePopupBubble(true);
+ tray->SetHidePopupBubble(false);
+ EXPECT_TRUE(tray->IsPopupVisible());
EXPECT_EQ(notifications_to_add,
- tray->message_center()->NotificationCount());
+ get_message_center()->NotificationCount());
EXPECT_EQ(NotificationList::kMaxVisiblePopupNotifications,
tray->GetPopupBubbleForTest()->NumMessageViewsForTest());
+ get_message_center()->SetDelegate(NULL);
+ get_message_center()->notification_list()->RemoveAllNotifications();
}
} // namespace ash
diff --git a/chrome/browser/notifications/message_center_notification_manager.cc b/chrome/browser/notifications/message_center_notification_manager.cc
index c6e3dcc..44f3352 100644
--- a/chrome/browser/notifications/message_center_notification_manager.cc
+++ b/chrome/browser/notifications/message_center_notification_manager.cc
@@ -17,11 +17,18 @@
#include "chrome/browser/ui/host_desktop.h"
#include "chrome/common/extensions/extension_set.h"
#include "chrome/common/pref_names.h"
+#include "ui/message_center/message_center_tray.h"
MessageCenterNotificationManager::MessageCenterNotificationManager(
message_center::MessageCenter* message_center)
: message_center_(message_center) {
message_center_->SetDelegate(this);
+
+#if !defined(OS_CHROMEOS)
+ // On Windows, the notification manager owns the tray icon and views. Other
+ // platforms have global ownership and Create will return NULL.
+ tray_.reset(message_center::CreateMessageCenterTray());
+#endif
}
MessageCenterNotificationManager::~MessageCenterNotificationManager() {
diff --git a/chrome/browser/notifications/message_center_notification_manager.h b/chrome/browser/notifications/message_center_notification_manager.h
index 00d111d..0f4939b 100644
--- a/chrome/browser/notifications/message_center_notification_manager.h
+++ b/chrome/browser/notifications/message_center_notification_manager.h
@@ -13,6 +13,7 @@
#include "chrome/browser/notifications/notification_ui_manager.h"
#include "chrome/browser/notifications/notification_ui_manager_impl.h"
#include "ui/message_center/message_center.h"
+#include "ui/message_center/message_center_tray_delegate.h"
class Notification;
class Profile;
@@ -52,6 +53,7 @@ class MessageCenterNotificationManager
int button_index) OVERRIDE;
private:
+ scoped_ptr<message_center::MessageCenterTrayDelegate> tray_;
message_center::MessageCenter* message_center_; // Weak, global.
// This class keeps a set of original Notification objects and corresponding
diff --git a/chrome/browser/ui/views/message_center/notification_bubble_wrapper_win.cc b/chrome/browser/ui/views/message_center/notification_bubble_wrapper_win.cc
new file mode 100644
index 0000000..434ed58
--- /dev/null
+++ b/chrome/browser/ui/views/message_center/notification_bubble_wrapper_win.cc
@@ -0,0 +1,109 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/message_center/notification_bubble_wrapper_win.h"
+
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/ui/views/message_center/web_notification_tray_win.h"
+#include "ui/gfx/size.h"
+#include "ui/message_center/message_bubble_base.h"
+#include "ui/views/bubble/bubble_delegate.h"
+#include "ui/views/bubble/tray_bubble_view.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_observer.h"
+
+namespace {
+
+const char kAccessibleNameForBubble[] = "Windows Notification Center";
+
+}
+
+namespace message_center {
+
+namespace internal {
+
+NotificationBubbleWrapperWin::NotificationBubbleWrapperWin(
+ WebNotificationTrayWin* tray,
+ scoped_ptr<message_center::MessageBubbleBase> bubble,
+ AnchorType anchor_type)
+ : bubble_(bubble.Pass()),
+ bubble_view_(NULL),
+ bubble_widget_(NULL),
+ tray_(tray) {
+ // Windows-specific initialization.
+ views::TrayBubbleView::AnchorAlignment anchor_alignment =
+ tray->GetAnchorAlignment();
+ views::TrayBubbleView::InitParams init_params =
+ bubble_->GetInitParams(anchor_alignment);
+ init_params.anchor_type = anchor_type;
+ init_params.close_on_deactivate = false;
+ init_params.arrow_alignment =
+ views::BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR;
+ // TODO(dewittj): Show big shadow without blocking clicks.
+ init_params.shadow = views::BubbleBorder::NO_SHADOW;
+
+ bubble_view_ = views::TrayBubbleView::Create(
+ tray->GetBubbleWindowContainer(), NULL, this, &init_params);
+
+ bubble_widget_ = views::BubbleDelegateView::CreateBubble(bubble_view_);
+ bubble_widget_->AddObserver(this);
+ bubble_widget_->StackAtTop();
+ bubble_widget_->SetAlwaysOnTop(true);
+ bubble_widget_->Activate();
+ bubble_view_->InitializeAndShowBubble();
+
+ bubble_view_->set_close_on_deactivate(true);
+ bubble_->InitializeContents(bubble_view_);
+}
+
+NotificationBubbleWrapperWin::~NotificationBubbleWrapperWin() {
+ bubble_.reset();
+ if (bubble_widget_) {
+ bubble_widget_->RemoveObserver(this);
+ bubble_widget_->Close();
+ }
+}
+
+void NotificationBubbleWrapperWin::OnWidgetClosing(views::Widget* widget) {
+ DCHECK_EQ(widget, bubble_widget_);
+ bubble_widget_->RemoveObserver(this);
+ bubble_widget_ = NULL;
+ tray_->HideBubbleWithView(bubble_view_);
+}
+
+void NotificationBubbleWrapperWin::BubbleViewDestroyed() {
+ bubble_->BubbleViewDestroyed();
+}
+
+void NotificationBubbleWrapperWin::OnMouseEnteredView() {
+ bubble_->OnMouseEnteredView();
+}
+
+void NotificationBubbleWrapperWin::OnMouseExitedView() {
+ bubble_->OnMouseExitedView();
+}
+
+string16 NotificationBubbleWrapperWin::GetAccessibleNameForBubble() {
+ // TODO(dewittj): Get a string resource.
+ return ASCIIToUTF16(kAccessibleNameForBubble);
+}
+
+gfx::Rect NotificationBubbleWrapperWin::GetAnchorRect(
+ views::Widget* anchor_widget,
+ AnchorType anchor_type,
+ AnchorAlignment anchor_alignment) {
+ gfx::Size size = bubble_view_->GetPreferredSize();
+ return tray_->GetAnchorRect(size,
+ anchor_type,
+ anchor_alignment);
+}
+
+void NotificationBubbleWrapperWin::HideBubble(
+ const views::TrayBubbleView* bubble_view) {
+ tray_->HideBubbleWithView(bubble_view);
+}
+
+} // namespace internal
+
+} // namespace message_center
diff --git a/chrome/browser/ui/views/message_center/notification_bubble_wrapper_win.h b/chrome/browser/ui/views/message_center/notification_bubble_wrapper_win.h
new file mode 100644
index 0000000..90d533d
--- /dev/null
+++ b/chrome/browser/ui/views/message_center/notification_bubble_wrapper_win.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_MESSAGE_CENTER_NOTIFICATION_BUBBLE_WRAPPER_WIN_H_
+#define CHROME_BROWSER_UI_VIEWS_MESSAGE_CENTER_NOTIFICATION_BUBBLE_WRAPPER_WIN_H_
+
+#include "chrome/browser/ui/views/message_center/web_notification_tray_win.h"
+#include "ui/views/bubble/tray_bubble_view.h"
+#include "ui/views/widget/widget.h"
+
+namespace message_center {
+
+namespace internal {
+
+// NotificationBubbleWrapperWin is a class that manages the views associated
+// with a MessageBubbleBase object and that notifies the WebNotificationTrayWin
+// when the widget closes. Delegates GetAnchorRect to the
+// WebNotificationTrayWin.
+class NotificationBubbleWrapperWin
+ : public views::WidgetObserver,
+ public views::TrayBubbleView::Delegate {
+ public:
+ // Takes ownership of |bubble|.
+ NotificationBubbleWrapperWin(
+ WebNotificationTrayWin* tray,
+ scoped_ptr<message_center::MessageBubbleBase> bubble,
+ AnchorType anchor_type);
+ virtual ~NotificationBubbleWrapperWin();
+
+ // Overridden from views::WidgetObserver.
+ void OnWidgetClosing(views::Widget* widget) OVERRIDE;
+
+ // TrayBubbleView::Delegate implementation.
+ virtual void BubbleViewDestroyed() OVERRIDE;
+ virtual void OnMouseEnteredView() OVERRIDE;
+ virtual void OnMouseExitedView() OVERRIDE;
+ virtual string16 GetAccessibleNameForBubble() OVERRIDE;
+ // GetAnchorRect passes responsibility for BubbleDelegateView::GetAnchorRect
+ // to the delegate.
+ virtual gfx::Rect GetAnchorRect(views::Widget* anchor_widget,
+ AnchorType anchor_type,
+ AnchorAlignment anchor_alignment) OVERRIDE;
+ virtual void HideBubble(const views::TrayBubbleView* bubble_view) OVERRIDE;
+
+ // Convenience accessors.
+ views::TrayBubbleView* bubble_view() { return bubble_view_; }
+ views::Widget* bubble_widget() { return bubble_widget_; }
+ message_center::MessageBubbleBase* bubble() { return bubble_.get(); }
+
+ private:
+ scoped_ptr<message_center::MessageBubbleBase> bubble_;
+ // |bubble_view_| is owned by its Widget.
+ views::TrayBubbleView* bubble_view_;
+ views::Widget* bubble_widget_;
+ WebNotificationTrayWin* tray_;
+
+ DISALLOW_COPY_AND_ASSIGN(NotificationBubbleWrapperWin);
+};
+
+} // namespace internal
+
+} // namespace message_center
+
+#endif // CHROME_BROWSER_UI_VIEWS_MESSAGE_CENTER_NOTIFICATION_BUBBLE_WRAPPER_WIN_H_
diff --git a/chrome/browser/ui/views/message_center/web_notification_tray_win.cc b/chrome/browser/ui/views/message_center/web_notification_tray_win.cc
new file mode 100644
index 0000000..9b1ebf60
--- /dev/null
+++ b/chrome/browser/ui/views/message_center/web_notification_tray_win.cc
@@ -0,0 +1,237 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/message_center/web_notification_tray_win.h"
+
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/status_icons/status_icon.h"
+#include "chrome/browser/status_icons/status_tray.h"
+#include "chrome/browser/ui/views/message_center/notification_bubble_wrapper_win.h"
+#include "chrome/browser/ui/views/status_icons/status_icon_win.h"
+#include "grit/theme_resources.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/win/hwnd_util.h"
+#include "ui/gfx/image/image_skia_operations.h"
+#include "ui/gfx/screen.h"
+#include "ui/message_center/message_bubble_base.h"
+#include "ui/message_center/message_center_bubble.h"
+#include "ui/message_center/message_center_tray.h"
+#include "ui/message_center/message_center_tray_delegate.h"
+#include "ui/message_center/message_popup_bubble.h"
+#include "ui/views/widget/widget.h"
+
+namespace {
+
+// Tray constants
+const int kPaddingFromLeftEdgeOfSystemTrayBottomAlignment = 8;
+
+gfx::Rect GetCornerAnchorRect(gfx::Size preferred_size) {
+ // TODO(dewittj): Use the preference to determine which corner to anchor from.
+ gfx::Screen* screen = gfx::Screen::GetNativeScreen();
+ gfx::Rect rect = screen->GetPrimaryDisplay().work_area();
+ rect.Inset(10, 5);
+ gfx::Point bottom_right(
+ rect.bottom_right().x() - preferred_size.width() / 2,
+ rect.bottom_right().y());
+ return gfx::Rect(bottom_right, gfx::Size());
+}
+
+// GetMouseAnchorRect returns a rectangle that is near the cursor point, but
+// whose behavior depends on where the Windows taskbar is. If it is on the
+// top or bottom of the screen, we want the arrow to touch the edge of the
+// taskbar directly above or below the mouse pointer and within the work area.
+// Otherwise, position the anchor on the mouse cursor directly.
+gfx::Rect GetMouseAnchorRect() {
+ gfx::Screen* screen = gfx::Screen::GetNativeScreen();
+ gfx::Rect usable_area = screen->GetPrimaryDisplay().bounds();
+ gfx::Rect work_area = screen->GetPrimaryDisplay().work_area();
+
+ // Inset the rectangle by the taskbar width if it is on top or bottom.
+ usable_area.set_y(work_area.y());
+ usable_area.set_height(work_area.height());
+
+ // Keep the anchor from being too close to the edge of the screen.
+ usable_area.Inset(kPaddingFromLeftEdgeOfSystemTrayBottomAlignment, 0);
+
+ // Use a mouse point that is on the mouse cursor, unless the mouse is over the
+ // start menu and the start menu is on the top or bottom.
+ gfx::Point cursor = screen->GetCursorScreenPoint();
+ gfx::Rect mouse_anchor_rect(
+ gfx::BoundingRect(cursor, usable_area.bottom_right()));
+ mouse_anchor_rect.set_height(0);
+ if (!usable_area.Contains(cursor))
+ mouse_anchor_rect.AdjustToFit(usable_area);
+ mouse_anchor_rect.set_width(0);
+ return mouse_anchor_rect;
+}
+
+gfx::ImageSkia GetIcon(bool has_unread_notifications) {
+ // TODO(dewittj): Use an icon resource for both unread and read notifications.
+ ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+ gfx::ImageSkia* icon =
+ rb.GetImageSkiaNamed(IDR_ALLOWED_NOTIFICATION);
+ DCHECK(icon);
+ if (has_unread_notifications)
+ return *icon;
+ return gfx::ImageSkiaOperations::CreateTransparentImage(*icon, .5);
+}
+
+} // namespace
+
+namespace message_center {
+
+MessageCenterTrayDelegate* CreateMessageCenterTray() {
+ return new WebNotificationTrayWin();
+}
+
+WebNotificationTrayWin::WebNotificationTrayWin()
+ : status_icon_(NULL),
+ message_center_visible_(false) {
+ message_center_tray_.reset(new MessageCenterTray(
+ this, g_browser_process->message_center()));
+ StatusTray* status_tray = g_browser_process->status_tray();
+ status_icon_ = status_tray->CreateStatusIcon();
+ status_icon_->AddObserver(this);
+ status_icon_->SetImage(
+ GetIcon(message_center()->UnreadNotificationCount() > 0));
+}
+
+WebNotificationTrayWin::~WebNotificationTrayWin() {
+ // Reset this early so that delegated events during destruction don't cause
+ // problems.
+ message_center_tray_.reset();
+ status_icon_->RemoveObserver(this);
+ StatusTray* status_tray = g_browser_process->status_tray();
+ status_tray->RemoveStatusIcon(status_icon_);
+ status_icon_ = NULL;
+}
+
+message_center::MessageCenter* WebNotificationTrayWin::message_center() {
+ return message_center_tray_->message_center();
+}
+
+bool WebNotificationTrayWin::ShowPopups() {
+ scoped_ptr<message_center::MessagePopupBubble> bubble(
+ new message_center::MessagePopupBubble(message_center()));
+ popup_bubble_.reset(new internal::NotificationBubbleWrapperWin(
+ this, bubble.Pass(), views::TrayBubbleView::ANCHOR_TYPE_BUBBLE));
+ return true;
+}
+
+void WebNotificationTrayWin::HidePopups() {
+ popup_bubble_.reset();
+}
+
+bool WebNotificationTrayWin::ShowMessageCenter() {
+ // Calculate the maximum height of the message center, given its anchor.
+ scoped_ptr<message_center::MessageCenterBubble> bubble(
+ new message_center::MessageCenterBubble(message_center()));
+ gfx::Point anchor_center = message_center_anchor_rect_.CenterPoint();
+ gfx::Screen* screen = gfx::Screen::GetNativeScreen();
+ gfx::Rect work_area = screen->GetPrimaryDisplay().work_area();
+ gfx::Point work_area_center = work_area.CenterPoint();
+ const int zMarginFromEdgeOfWorkArea = 10;
+ int max_height = 0;
+ if (work_area_center < anchor_center)
+ max_height = anchor_center.y() - work_area.origin().y();
+ else
+ max_height = work_area.bottom() - message_center_anchor_rect_.bottom();
+ bubble->SetMaxHeight(max_height - zMarginFromEdgeOfWorkArea);
+
+ message_center_bubble_.reset(new internal::NotificationBubbleWrapperWin(
+ this,
+ bubble.Pass(),
+ views::TrayBubbleView::ANCHOR_TYPE_TRAY));
+ return true;
+}
+
+void WebNotificationTrayWin::HideMessageCenter() {
+ message_center_bubble_.reset();
+}
+
+void WebNotificationTrayWin::UpdateMessageCenter() {
+ if (message_center_bubble_.get())
+ message_center_bubble_->bubble()->ScheduleUpdate();
+}
+
+void WebNotificationTrayWin::UpdatePopups() {
+ if (popup_bubble_.get())
+ popup_bubble_->bubble()->ScheduleUpdate();
+};
+
+void WebNotificationTrayWin::OnMessageCenterTrayChanged() {
+ bool has_unread_notifications =
+ message_center()->UnreadNotificationCount() > 0;
+ status_icon_->SetImage(GetIcon(has_unread_notifications));
+}
+
+gfx::Rect WebNotificationTrayWin::GetAnchorRect(
+ gfx::Size preferred_size,
+ views::TrayBubbleView::AnchorType anchor_type,
+ views::TrayBubbleView::AnchorAlignment anchor_alignment) {
+ if (anchor_type == views::TrayBubbleView::ANCHOR_TYPE_TRAY)
+ return message_center_anchor_rect_;
+ return GetCornerAnchorRect(preferred_size);
+}
+
+views::TrayBubbleView::AnchorAlignment
+WebNotificationTrayWin::GetAnchorAlignment() {
+ gfx::Screen* screen = gfx::Screen::GetNativeScreen();
+ // TODO(dewittj): It's possible GetPrimaryDisplay is wrong.
+ gfx::Rect screen_bounds = screen->GetPrimaryDisplay().bounds();
+ gfx::Rect work_area = screen->GetPrimaryDisplay().work_area();
+
+ // Comparing the work area to the screen bounds gives us the location of the
+ // taskbar. If the work area is less tall than the screen, assume the taskbar
+ // is on the bottom, and cause the arrow to be displayed on the bottom of the
+ // bubble. Otherwise, cause the arrow to be displayed on the side of the
+ // bubble that the taskbar is on.
+ if (work_area.height() < screen_bounds.height())
+ return views::TrayBubbleView::ANCHOR_ALIGNMENT_BOTTOM;
+ if (work_area.x() > screen_bounds.x())
+ return views::TrayBubbleView::ANCHOR_ALIGNMENT_LEFT;
+ return views::TrayBubbleView::ANCHOR_ALIGNMENT_RIGHT;
+}
+
+gfx::NativeView WebNotificationTrayWin::GetBubbleWindowContainer() {
+ return NULL;
+}
+
+void WebNotificationTrayWin::OnStatusIconClicked() {
+ UpdateAnchorRect();
+ message_center_tray_->ToggleMessageCenterBubble();
+}
+
+void WebNotificationTrayWin::HideBubbleWithView(
+ const views::TrayBubbleView* bubble_view) {
+ if (message_center_bubble_.get() &&
+ bubble_view == message_center_bubble_->bubble_view()) {
+ message_center_tray_->HideMessageCenterBubble();
+ } else if (popup_bubble_.get() &&
+ bubble_view == popup_bubble_->bubble_view()) {
+ message_center_tray_->HidePopupBubble();
+ }
+}
+
+void WebNotificationTrayWin::UpdateAnchorRect() {
+ message_center_anchor_rect_ = GetMouseAnchorRect();
+}
+
+message_center::MessageCenterBubble*
+WebNotificationTrayWin::GetMessageCenterBubbleForTest() {
+ if (!message_center_bubble_.get())
+ return NULL;
+ return static_cast<message_center::MessageCenterBubble*>(
+ message_center_bubble_->bubble());
+}
+
+message_center::MessagePopupBubble*
+WebNotificationTrayWin::GetPopupBubbleForTest() {
+ if (!popup_bubble_.get())
+ return NULL;
+ return static_cast<message_center::MessagePopupBubble*>(
+ popup_bubble_->bubble());
+}
+
+} // namespace message_center
diff --git a/chrome/browser/ui/views/message_center/web_notification_tray_win.h b/chrome/browser/ui/views/message_center/web_notification_tray_win.h
new file mode 100644
index 0000000..67ac6d2
--- /dev/null
+++ b/chrome/browser/ui/views/message_center/web_notification_tray_win.h
@@ -0,0 +1,95 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_MESSAGE_CENTER_WEB_NOTIFICATION_TRAY_WIN_H_
+#define CHROME_BROWSER_UI_VIEWS_MESSAGE_CENTER_WEB_NOTIFICATION_TRAY_WIN_H_
+
+#include "chrome/browser/status_icons/status_icon_observer.h"
+#include "ui/message_center/message_center_tray.h"
+#include "ui/message_center/message_center_tray_delegate.h"
+#include "ui/views/bubble/tray_bubble_view.h"
+
+class StatusIcon;
+
+namespace message_center {
+class MessageCenter;
+class MessageCenterBubble;
+class MessagePopupBubble;
+}
+
+namespace views {
+class Widget;
+}
+
+namespace message_center {
+
+namespace internal {
+class NotificationBubbleWrapperWin;
+}
+
+// A MessageCenterTrayDelegate implementation that exposes the MessageCenterTray
+// via a system tray icon. The notification popups will be displayed in the
+// corner of the screen and the message center will be displayed by the system
+// tray icon on click.
+// TODO(dewittj): Quiet mode.
+class WebNotificationTrayWin
+ : public message_center::MessageCenterTrayDelegate,
+ public StatusIconObserver {
+ public:
+ WebNotificationTrayWin();
+ virtual ~WebNotificationTrayWin();
+
+ message_center::MessageCenter* message_center();
+
+ // MessageCenterTrayDelegate implementation.
+ virtual bool ShowPopups() OVERRIDE;
+ virtual void HidePopups() OVERRIDE;
+ virtual bool ShowMessageCenter() OVERRIDE;
+ virtual void HideMessageCenter() OVERRIDE;
+ virtual void UpdateMessageCenter() OVERRIDE;
+ virtual void UpdatePopups() OVERRIDE;
+ virtual void OnMessageCenterTrayChanged() OVERRIDE;
+
+ // These are forwarded to WebNotificationTrayWin by
+ // NotificationBubbleWrapperWin classes since they don't have enough
+ // context to provide the required data for TrayBubbleView::Delegate.
+ gfx::Rect GetAnchorRect(
+ gfx::Size preferred_size,
+ views::TrayBubbleView::AnchorType anchor_type,
+ views::TrayBubbleView::AnchorAlignment anchor_alignment);
+ gfx::NativeView GetBubbleWindowContainer();
+ views::TrayBubbleView::AnchorAlignment GetAnchorAlignment();
+
+ // StatusIconObserver implementation.
+ virtual void OnStatusIconClicked() OVERRIDE;
+
+ void HideBubbleWithView(const views::TrayBubbleView* bubble_view);
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(WebNotificationTrayWinTest, WebNotifications);
+ FRIEND_TEST_ALL_PREFIXES(WebNotificationTrayWinTest,
+ WebNotificationPopupBubble);
+ FRIEND_TEST_ALL_PREFIXES(WebNotificationTrayWinTest,
+ ManyMessageCenterNotifications);
+ FRIEND_TEST_ALL_PREFIXES(WebNotificationTrayWinTest, ManyPopupNotifications);
+
+ void UpdateAnchorRect();
+ message_center::MessagePopupBubble* GetPopupBubbleForTest();
+ message_center::MessageCenterBubble* GetMessageCenterBubbleForTest();
+
+ scoped_ptr<internal::NotificationBubbleWrapperWin> popup_bubble_;
+ scoped_ptr<internal::NotificationBubbleWrapperWin> message_center_bubble_;
+
+ StatusIcon* status_icon_;
+ bool message_center_visible_;
+ scoped_ptr<MessageCenterTray> message_center_tray_;
+ gfx::Rect message_center_anchor_rect_;
+ gfx::Rect popup_anchor_rect_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebNotificationTrayWin);
+};
+
+} // namespace message_center
+
+#endif // CHROME_BROWSER_UI_VIEWS_MESSAGE_CENTER_WEB_NOTIFICATION_TRAY_WIN_H_
diff --git a/chrome/browser/ui/views/message_center/web_notification_tray_win_browsertest.cc b/chrome/browser/ui/views/message_center/web_notification_tray_win_browsertest.cc
new file mode 100644
index 0000000..447a574
--- /dev/null
+++ b/chrome/browser/ui/views/message_center/web_notification_tray_win_browsertest.cc
@@ -0,0 +1,214 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/message_center/web_notification_tray_win.h"
+
+#include <set>
+
+#include "ash/root_window_controller.h"
+#include "ash/system/status_area_widget.h"
+#include "ash/system/tray/system_tray_item.h"
+#include "base/stringprintf.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "content/public/test/test_utils.h"
+#include "ui/message_center/message_center_bubble.h"
+#include "ui/message_center/message_center_tray.h"
+#include "ui/message_center/message_popup_bubble.h"
+#include "ui/message_center/notification_list.h"
+#include "ui/notifications/notification_types.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/fill_layout.h"
+#include "ui/views/view.h"
+#include "ui/views/widget/widget.h"
+
+namespace message_center {
+
+namespace {
+
+class TestDelegate : public message_center::MessageCenter::Delegate {
+ public:
+ explicit TestDelegate(message_center::MessageCenter* message_center)
+ : message_center_(message_center) {
+ message_center_->SetDelegate(this);
+ }
+
+ virtual ~TestDelegate() {
+ message_center_->SetDelegate(NULL);
+ message_center_->notification_list()->RemoveAllNotifications();
+ }
+
+ // WebNotificationTray::Delegate overrides.
+ virtual void NotificationRemoved(
+ const std::string& notification_id) OVERRIDE {
+ notification_ids_.erase(notification_id);
+ }
+
+ virtual void DisableNotificationsFromSource(
+ const std::string& notification_id) OVERRIDE {
+ }
+
+ virtual void DisableExtension(const std::string& notification_id) OVERRIDE { }
+ virtual void ShowSettings(const std::string& notification_id) OVERRIDE { }
+ virtual void OnClicked(const std::string& notification_id) OVERRIDE { }
+ virtual void ShowSettingsDialog(gfx::NativeView context) OVERRIDE { }
+
+
+ void AddNotification(const std::string& id) {
+ notification_ids_.insert(id);
+ message_center_->AddNotification(
+ ui::notifications::NOTIFICATION_TYPE_SIMPLE,
+ id,
+ ASCIIToUTF16("Test Web Notification"),
+ ASCIIToUTF16("Notification message body."),
+ ASCIIToUTF16("www.test.org"),
+ "" /* extension id */,
+ NULL /* optional_fields */);
+ }
+
+ void UpdateNotification(const std::string& old_id,
+ const std::string& new_id) {
+ notification_ids_.erase(old_id);
+ notification_ids_.insert(new_id);
+ message_center_->UpdateNotification(
+ old_id, new_id,
+ ASCIIToUTF16("Updated Web Notification"),
+ ASCIIToUTF16("Updated message body."),
+ NULL);
+ }
+
+ void RemoveNotification(const std::string& id) {
+ message_center_->RemoveNotification(id);
+ notification_ids_.erase(id);
+ }
+
+ bool HasNotificationId(const std::string& id) {
+ return notification_ids_.find(id) != notification_ids_.end();
+ }
+
+ private:
+ std::set<std::string> notification_ids_;
+ // Weak pointer.
+ message_center::MessageCenter* message_center_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestDelegate);
+};
+
+} // namespace
+
+typedef InProcessBrowserTest WebNotificationTrayWinTest;
+
+// TODO(dewittj): More exhaustive testing.
+IN_PROC_BROWSER_TEST_F(WebNotificationTrayWinTest, WebNotifications) {
+ scoped_ptr<WebNotificationTrayWin> tray(new WebNotificationTrayWin());
+ message_center::MessageCenter* message_center = tray->message_center();
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(message_center));
+
+ // Add a notification.
+ delegate->AddNotification("test_id1");
+ EXPECT_EQ(1u, message_center->NotificationCount());
+ EXPECT_TRUE(message_center->notification_list()->HasNotification("test_id1"));
+ EXPECT_FALSE(
+ message_center->notification_list()->HasNotification("test_id2"));
+ delegate->AddNotification("test_id2");
+ delegate->AddNotification("test_id2");
+ EXPECT_EQ(2u, message_center->NotificationCount());
+ EXPECT_TRUE(message_center->notification_list()->HasNotification("test_id2"));
+
+ // Ensure that updating a notification does not affect the count.
+ delegate->UpdateNotification("test_id2", "test_id3");
+ delegate->UpdateNotification("test_id3", "test_id3");
+ EXPECT_EQ(2u, message_center->NotificationCount());
+ EXPECT_FALSE(delegate->HasNotificationId("test_id2"));
+ EXPECT_FALSE(message_center->notification_list()->HasNotification(
+ "test_id2"));
+ EXPECT_TRUE(delegate->HasNotificationId("test_id3"));
+
+ // Ensure that Removing the first notification removes it from the tray.
+ delegate->RemoveNotification("test_id1");
+ EXPECT_FALSE(delegate->HasNotificationId("test_id1"));
+ EXPECT_FALSE(message_center->notification_list()->HasNotification(
+ "test_id1"));
+ EXPECT_EQ(1u, message_center->NotificationCount());
+
+ // Remove the remaining notification.
+ delegate->RemoveNotification("test_id3");
+ EXPECT_EQ(0u, message_center->NotificationCount());
+ EXPECT_FALSE(message_center->notification_list()->HasNotification(
+ "test_id3"));
+}
+
+IN_PROC_BROWSER_TEST_F(WebNotificationTrayWinTest, WebNotificationPopupBubble) {
+ scoped_ptr<WebNotificationTrayWin> tray(new WebNotificationTrayWin());
+ message_center::MessageCenter* message_center = tray->message_center();
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(message_center));
+
+ // Adding a notification should show the popup bubble.
+ delegate->AddNotification("test_id1");
+ EXPECT_TRUE(tray->message_center_tray_->popups_visible());
+
+ // Updating a notification should not hide the popup bubble.
+ delegate->AddNotification("test_id2");
+ delegate->UpdateNotification("test_id2", "test_id3");
+ EXPECT_TRUE(tray->message_center_tray_->popups_visible());
+
+ // Removing the first notification should not hide the popup bubble.
+ delegate->RemoveNotification("test_id1");
+ EXPECT_TRUE(tray->message_center_tray_->popups_visible());
+
+ // Removing the visible notification should hide the popup bubble.
+ delegate->RemoveNotification("test_id3");
+ EXPECT_FALSE(tray->message_center_tray_->popups_visible());
+}
+
+using message_center::NotificationList;
+
+IN_PROC_BROWSER_TEST_F(WebNotificationTrayWinTest,
+ ManyMessageCenterNotifications) {
+ scoped_ptr<WebNotificationTrayWin> tray(new WebNotificationTrayWin());
+ message_center::MessageCenter* message_center = tray->message_center();
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(message_center));
+
+ // Add the max visible notifications +1, ensure the correct visible number.
+ size_t notifications_to_add =
+ NotificationList::kMaxVisibleMessageCenterNotifications + 1;
+ for (size_t i = 0; i < notifications_to_add; ++i) {
+ std::string id = StringPrintf("test_id%d", static_cast<int>(i));
+ delegate->AddNotification(id);
+ }
+ bool shown = tray->message_center_tray_->ShowMessageCenterBubble();
+ EXPECT_TRUE(shown);
+ content::RunAllPendingInMessageLoop();
+ EXPECT_TRUE(tray->message_center_bubble_.get() != NULL);
+ EXPECT_EQ(notifications_to_add,
+ message_center->NotificationCount());
+ EXPECT_EQ(NotificationList::kMaxVisibleMessageCenterNotifications,
+ tray->GetMessageCenterBubbleForTest()->NumMessageViewsForTest());
+}
+
+IN_PROC_BROWSER_TEST_F(WebNotificationTrayWinTest, ManyPopupNotifications) {
+ scoped_ptr<WebNotificationTrayWin> tray(new WebNotificationTrayWin());
+ message_center::MessageCenter* message_center = tray->message_center();
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(message_center));
+
+ // Add the max visible popup notifications +1, ensure the correct num visible.
+ size_t notifications_to_add =
+ NotificationList::kMaxVisiblePopupNotifications + 1;
+ for (size_t i = 0; i < notifications_to_add; ++i) {
+ std::string id = StringPrintf("test_id%d", static_cast<int>(i));
+ delegate->AddNotification(id);
+ }
+ // Hide and reshow the bubble so that it is updated immediately, not delayed.
+ tray->message_center_tray_->HidePopupBubble();
+ tray->message_center_tray_->ShowPopupBubble();
+ EXPECT_TRUE(tray->message_center_tray_->popups_visible());
+ EXPECT_EQ(notifications_to_add,
+ message_center->NotificationCount());
+ EXPECT_EQ(NotificationList::kMaxVisiblePopupNotifications,
+ tray->GetPopupBubbleForTest()->NumMessageViewsForTest());
+ message_center->SetDelegate(NULL);
+ message_center->notification_list()->RemoveAllNotifications();
+}
+
+} // namespace message_center
diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi
index 5ac9d84..f7ec2f1 100644
--- a/chrome/chrome_browser_ui.gypi
+++ b/chrome/chrome_browser_ui.gypi
@@ -1651,6 +1651,10 @@
'browser/ui/views/login_prompt_views.cc',
'browser/ui/views/login_view.cc',
'browser/ui/views/login_view.h',
+ 'browser/ui/views/message_center/notification_bubble_wrapper_win.h',
+ 'browser/ui/views/message_center/notification_bubble_wrapper_win.cc',
+ 'browser/ui/views/message_center/web_notification_tray_win.h',
+ 'browser/ui/views/message_center/web_notification_tray_win.cc',
'browser/ui/views/missing_system_file_dialog_win.cc',
'browser/ui/views/missing_system_file_dialog_win.h',
'browser/ui/views/native_constrained_window_aura.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 4a6b46c..ee286e0 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1310,6 +1310,7 @@
'browser/ui/views/frame/browser_view_browsertest.cc',
'browser/ui/views/immersive_mode_controller_browsertest.cc',
'browser/ui/views/location_bar/star_view_browsertest.cc',
+ 'browser/ui/views/message_center/web_notification_tray_win_browsertest.cc',
'browser/ui/views/select_file_dialog_extension_browsertest.cc',
'browser/ui/views/sync/one_click_signin_bubble_view_browsertest.cc',
'browser/ui/views/toolbar_view_browsertest.cc',
@@ -1639,6 +1640,7 @@
'app/chrome_version.rc.version',
# TODO(port): http://crbug.com/45770
'browser/printing/printing_layout_browsertest.cc',
+ 'browser/ui/views/message_center/web_notification_tray_win_browsertest.cc',
],
}],
['toolkit_uses_gtk == 1', {
@@ -1736,6 +1738,7 @@
['enable_message_center==0 or use_ash==1', {
'sources!': [
'browser/notifications/message_center_notifications_browsertest.cc',
+ 'browser/ui/views/message_center/web_notification_tray_win_browsertest.cc',
],
}],
], # conditions
diff --git a/ui/message_center/message_center.gyp b/ui/message_center/message_center.gyp
index c2c631a..f778182 100644
--- a/ui/message_center/message_center.gyp
+++ b/ui/message_center/message_center.gyp
@@ -34,6 +34,9 @@
'message_center_constants.cc',
'message_center_constants.h',
'message_center_export.h',
+ 'message_center_tray.cc',
+ 'message_center_tray.h',
+ 'message_center_tray_delegate.h',
'message_center_switches.cc',
'message_center_switches.h',
'message_popup_bubble.cc',
@@ -67,6 +70,7 @@
'message_center',
],
'sources': [
+ 'message_center_tray_unittest.cc',
'notification_list_unittest.cc',
],
},
diff --git a/ui/message_center/message_center.h b/ui/message_center/message_center.h
index 3850489..e502913 100644
--- a/ui/message_center/message_center.h
+++ b/ui/message_center/message_center.h
@@ -14,8 +14,6 @@
#include "ui/message_center/notification_list.h"
#include "ui/notifications/notification_types.h"
-template <typename T> struct DefaultSingletonTraits;
-
namespace base {
class DictionaryValue;
}
diff --git a/ui/message_center/message_center_tray.cc b/ui/message_center/message_center_tray.cc
new file mode 100644
index 0000000..81c3bcd
--- /dev/null
+++ b/ui/message_center/message_center_tray.cc
@@ -0,0 +1,111 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/message_center/message_center_tray.h"
+
+#include "base/observer_list.h"
+#include "base/utf_string_conversions.h"
+#include "ui/message_center/message_center_tray_delegate.h"
+
+namespace message_center {
+
+MessageCenterTray::MessageCenterTray(
+ MessageCenterTrayDelegate* delegate,
+ message_center::MessageCenter* message_center)
+ : message_center_(message_center),
+ message_center_visible_(false),
+ popups_visible_(false),
+ delegate_(delegate) {
+ message_center_->AddObserver(this);
+}
+
+MessageCenterTray::~MessageCenterTray() {
+ message_center_->RemoveObserver(this);
+}
+
+bool MessageCenterTray::ShowMessageCenterBubble() {
+ if (message_center_visible_)
+ return true;
+
+ HidePopupBubble();
+
+ message_center_visible_ = delegate_->ShowMessageCenter();
+ message_center_->SetMessageCenterVisible(message_center_visible_);
+ return message_center_visible_;
+}
+
+bool MessageCenterTray::HideMessageCenterBubble() {
+ if (!message_center_visible_)
+ return false;
+ delegate_->HideMessageCenter();
+ message_center_visible_ = false;
+ message_center_->SetMessageCenterVisible(false);
+ NotifyMessageCenterTrayChanged();
+ return true;
+}
+
+void MessageCenterTray::ToggleMessageCenterBubble() {
+ if (message_center_visible_)
+ HideMessageCenterBubble();
+ else
+ ShowMessageCenterBubble();
+}
+
+void MessageCenterTray::ShowPopupBubble() {
+ if (message_center_visible_) {
+ // We don't want to show popups if the user is already looking at the
+ // message center. Instead, update it.
+ delegate_->UpdateMessageCenter();
+ return;
+ }
+
+ if (popups_visible_) {
+ delegate_->UpdatePopups();
+ NotifyMessageCenterTrayChanged();
+ return;
+ }
+
+ if (!message_center_->HasPopupNotifications())
+ return;
+
+ popups_visible_ = delegate_->ShowPopups();
+ NotifyMessageCenterTrayChanged();
+}
+
+bool MessageCenterTray::HidePopupBubble() {
+ if (!popups_visible_)
+ return false;
+
+ delegate_->HidePopups();
+ popups_visible_ = false;
+ NotifyMessageCenterTrayChanged();
+
+ return true;
+}
+
+void MessageCenterTray::OnMessageCenterChanged(bool new_notification) {
+ if (message_center_visible_) {
+ if (message_center_->NotificationCount() == 0)
+ HideMessageCenterBubble();
+ else
+ delegate_->UpdateMessageCenter();
+ }
+ if (popups_visible_) {
+ if (message_center_->NotificationCount() == 0)
+ HidePopupBubble();
+ else
+ delegate_->UpdatePopups();
+ }
+
+ if (new_notification)
+ ShowPopupBubble();
+
+ NotifyMessageCenterTrayChanged();
+}
+
+void MessageCenterTray::NotifyMessageCenterTrayChanged() {
+ delegate_->OnMessageCenterTrayChanged();
+}
+
+} // namespace message_center
diff --git a/ui/message_center/message_center_tray.h b/ui/message_center/message_center_tray.h
new file mode 100644
index 0000000..4d7f98a0
--- /dev/null
+++ b/ui/message_center/message_center_tray.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_MESSAGE_CENTER_MESSAGE_CENTER_TRAY_H_
+#define UI_MESSAGE_CENTER_MESSAGE_CENTER_TRAY_H_
+
+#include "base/observer_list.h"
+#include "ui/message_center/message_center.h"
+#include "ui/message_center/message_center_export.h"
+#include "ui/message_center/message_center_tray_delegate.h"
+
+namespace message_center {
+
+class MessageBubbleBase;
+class MessagePopupBubble;
+class QuietModeBubble;
+
+// Implementation found with each supported platform's implementation of
+// MessageCenterTrayDelegate.
+MessageCenterTrayDelegate* CreateMessageCenterTray();
+
+// Class that observes a MessageCenter. Manages the popup and message center
+// bubbles. Tells the MessageCenterTrayHost when the tray is changed, as well
+// as when bubbles are shown and hidden.
+class MESSAGE_CENTER_EXPORT MessageCenterTray
+ : public message_center::MessageCenter::Observer {
+ public:
+ MessageCenterTray(MessageCenterTrayDelegate* delegate,
+ message_center::MessageCenter* message_center);
+ virtual ~MessageCenterTray();
+
+ // Shows or updates the message center bubble and hides the popup bubble.
+ // Returns whether the message center is visible after the call, whether or
+ // not it was visible before.
+ bool ShowMessageCenterBubble();
+
+ // Returns whether the message center was visible before.
+ bool HideMessageCenterBubble();
+
+ void ToggleMessageCenterBubble();
+
+ // Causes an update if the popup bubble is already shown.
+ void ShowPopupBubble();
+
+ // Returns whether the popup was visible before.
+ bool HidePopupBubble();
+
+ bool message_center_visible() { return message_center_visible_; }
+ bool popups_visible() { return popups_visible_; }
+ MessageCenterTrayDelegate* delegate() { return delegate_; }
+ message_center::MessageCenter* message_center() { return message_center_; }
+
+ // Overridden from message_center::MessageCenter::Observer.
+ virtual void OnMessageCenterChanged(bool new_notification) OVERRIDE;
+
+ private:
+ void NotifyMessageCenterTrayChanged();
+
+ // |message_center_| is a weak pointer that must live longer than
+ // MessageCenterTray.
+ message_center::MessageCenter* message_center_;
+ bool message_center_visible_;
+ bool popups_visible_;
+ // |delegate_| is a weak pointer that must live longer than MessageCenterTray.
+ MessageCenterTrayDelegate* delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessageCenterTray);
+};
+
+} // namespace message_center
+
+#endif // UI_MESSAGE_CENTER_MESSAGE_CENTER_TRAY_H_
diff --git a/ui/message_center/message_center_tray_delegate.h b/ui/message_center/message_center_tray_delegate.h
new file mode 100644
index 0000000..edd31a3
--- /dev/null
+++ b/ui/message_center/message_center_tray_delegate.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_MESSAGE_CENTER_MESSAGE_CENTER_TRAY_DELEGATE_H_
+#define UI_MESSAGE_CENTER_MESSAGE_CENTER_TRAY_DELEGATE_H_
+
+namespace message_center {
+
+// A MessageCenterTrayDelegate class is responsible for managing the various UI
+// surfaces that should be displayed when the MessageCenter is changed.
+class MESSAGE_CENTER_EXPORT MessageCenterTrayDelegate {
+ public:
+ virtual ~MessageCenterTrayDelegate() {};
+
+ // Called whenever a change to the visible UI has occurred.
+ virtual void OnMessageCenterTrayChanged() = 0;
+
+ // Display the popup bubbles for new notifications to the user. Returns true
+ // if popups were actually displayed to the user.
+ virtual bool ShowPopups() = 0;
+
+ // Remove the popup bubbles from the UI.
+ virtual void HidePopups() = 0;
+
+ // Called when a change to the message center could cause a change to popups
+ // that are currently being displayed.
+ virtual void UpdatePopups() = 0;
+
+ // Display the message center containing all undismissed notifications to the
+ // user. Returns true if the center was actually displayed to the user.
+ virtual bool ShowMessageCenter() = 0;
+
+ // Remove the message center from the UI.
+ virtual void HideMessageCenter() = 0;
+
+ // Called when a change to the message center could cause a change to the
+ // message center widget that is currently being displayed.
+ virtual void UpdateMessageCenter() = 0;
+};
+
+} // namespace message_center
+
+#endif // UI_MESSAGE_CENTER_MESSAGE_CENTER_TRAY_DELEGATE_H_
diff --git a/ui/message_center/message_center_tray_unittest.cc b/ui/message_center/message_center_tray_unittest.cc
new file mode 100644
index 0000000..24a83e5
--- /dev/null
+++ b/ui/message_center/message_center_tray_unittest.cc
@@ -0,0 +1,208 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/message_center/message_center_tray.h"
+
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/message_center/message_center.h"
+#include "ui/notifications/notification_types.h"
+
+namespace message_center {
+namespace {
+
+class MockDelegate : public MessageCenterTrayDelegate {
+ public:
+ MockDelegate()
+ : show_popups_success_(true),
+ show_message_center_success_(true) {}
+ virtual ~MockDelegate() {}
+ virtual void OnMessageCenterTrayChanged() {}
+ virtual bool ShowPopups() {
+ return show_message_center_success_;
+ }
+ virtual void HidePopups() {}
+ virtual void UpdatePopups() {}
+ virtual bool ShowMessageCenter() {
+ return show_popups_success_;
+ }
+ virtual void HideMessageCenter() {}
+ virtual void UpdateMessageCenter() {}
+
+ bool show_popups_success_;
+ bool show_message_center_success_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockDelegate);
+};
+
+class MessageCenterTrayTest : public testing::Test {
+ public:
+ MessageCenterTrayTest() {}
+ virtual ~MessageCenterTrayTest() {}
+
+ virtual void SetUp() {
+ delegate_.reset(new MockDelegate);
+ message_center_.reset(new MessageCenter());
+ message_center_tray_.reset(
+ new MessageCenterTray(delegate_.get(), message_center_.get()));
+ }
+
+ virtual void TearDown() {
+ message_center_tray_.reset();
+ message_center_.reset();
+ delegate_.reset();
+ }
+
+ protected:
+ scoped_ptr<MockDelegate> delegate_;
+ scoped_ptr<MessageCenterTray> message_center_tray_;
+ scoped_ptr<MessageCenter> message_center_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MessageCenterTrayTest);
+};
+
+} // namespace
+
+TEST_F(MessageCenterTrayTest, BasicMessageCenter) {
+ ASSERT_FALSE(message_center_tray_->popups_visible());
+ ASSERT_FALSE(message_center_tray_->message_center_visible());
+
+ bool shown = message_center_tray_->ShowMessageCenterBubble();
+ EXPECT_TRUE(shown);
+
+ ASSERT_FALSE(message_center_tray_->popups_visible());
+ ASSERT_TRUE(message_center_tray_->message_center_visible());
+
+ message_center_tray_->HideMessageCenterBubble();
+
+ ASSERT_FALSE(message_center_tray_->popups_visible());
+ ASSERT_FALSE(message_center_tray_->message_center_visible());
+
+ message_center_tray_->ToggleMessageCenterBubble();
+
+ ASSERT_FALSE(message_center_tray_->popups_visible());
+ ASSERT_TRUE(message_center_tray_->message_center_visible());
+
+ message_center_tray_->ToggleMessageCenterBubble();
+
+ ASSERT_FALSE(message_center_tray_->popups_visible());
+ ASSERT_FALSE(message_center_tray_->message_center_visible());
+}
+
+TEST_F(MessageCenterTrayTest, BasicPopup) {
+ ASSERT_FALSE(message_center_tray_->popups_visible());
+ ASSERT_FALSE(message_center_tray_->message_center_visible());
+
+ message_center_tray_->ShowPopupBubble();
+
+ ASSERT_FALSE(message_center_tray_->popups_visible());
+ ASSERT_FALSE(message_center_tray_->message_center_visible());
+
+ message_center_->AddNotification(
+ ui::notifications::NOTIFICATION_TYPE_SIMPLE,
+ "BasicPopup",
+ ASCIIToUTF16("Test Web Notification"),
+ ASCIIToUTF16("Notification message body."),
+ ASCIIToUTF16("www.test.org"),
+ "" /* extension id */,
+ NULL /* optional_fields */);
+
+ ASSERT_TRUE(message_center_tray_->popups_visible());
+ ASSERT_FALSE(message_center_tray_->message_center_visible());
+
+ message_center_tray_->HidePopupBubble();
+
+ ASSERT_FALSE(message_center_tray_->popups_visible());
+ ASSERT_FALSE(message_center_tray_->message_center_visible());
+}
+
+TEST_F(MessageCenterTrayTest, MessageCenterClosesPopups) {
+ ASSERT_FALSE(message_center_tray_->popups_visible());
+ ASSERT_FALSE(message_center_tray_->message_center_visible());
+
+ message_center_->AddNotification(
+ ui::notifications::NOTIFICATION_TYPE_SIMPLE,
+ "MessageCenterClosesPopups",
+ ASCIIToUTF16("Test Web Notification"),
+ ASCIIToUTF16("Notification message body."),
+ ASCIIToUTF16("www.test.org"),
+ "" /* extension id */,
+ NULL /* optional_fields */);
+
+ ASSERT_TRUE(message_center_tray_->popups_visible());
+ ASSERT_FALSE(message_center_tray_->message_center_visible());
+
+ bool shown = message_center_tray_->ShowMessageCenterBubble();
+ EXPECT_TRUE(shown);
+
+ ASSERT_FALSE(message_center_tray_->popups_visible());
+ ASSERT_TRUE(message_center_tray_->message_center_visible());
+
+ message_center_->AddNotification(
+ ui::notifications::NOTIFICATION_TYPE_SIMPLE,
+ "MessageCenterClosesPopups2",
+ ASCIIToUTF16("Test Web Notification"),
+ ASCIIToUTF16("Notification message body."),
+ ASCIIToUTF16("www.test.org"),
+ "" /* extension id */,
+ NULL /* optional_fields */);
+
+ message_center_tray_->ShowPopupBubble();
+
+ ASSERT_FALSE(message_center_tray_->popups_visible());
+ ASSERT_TRUE(message_center_tray_->message_center_visible());
+
+ message_center_tray_->HideMessageCenterBubble();
+
+ ASSERT_FALSE(message_center_tray_->popups_visible());
+ ASSERT_FALSE(message_center_tray_->message_center_visible());
+}
+
+TEST_F(MessageCenterTrayTest, ShowBubbleFails) {
+ // Now the delegate will signal that it was unable to show a bubble.
+ delegate_->show_popups_success_ = false;
+ delegate_->show_message_center_success_ = false;
+
+ ASSERT_FALSE(message_center_tray_->popups_visible());
+ ASSERT_FALSE(message_center_tray_->message_center_visible());
+
+ message_center_->AddNotification(
+ ui::notifications::NOTIFICATION_TYPE_SIMPLE,
+ "ShowBubbleFails",
+ ASCIIToUTF16("Test Web Notification"),
+ ASCIIToUTF16("Notification message body."),
+ ASCIIToUTF16("www.test.org"),
+ "" /* extension id */,
+ NULL /* optional_fields */);
+
+ message_center_tray_->ShowPopupBubble();
+
+ ASSERT_FALSE(message_center_tray_->popups_visible());
+ ASSERT_FALSE(message_center_tray_->message_center_visible());
+
+ bool shown = message_center_tray_->ShowMessageCenterBubble();
+ EXPECT_FALSE(shown);
+
+ ASSERT_FALSE(message_center_tray_->popups_visible());
+ ASSERT_FALSE(message_center_tray_->message_center_visible());
+
+ message_center_tray_->HideMessageCenterBubble();
+
+ ASSERT_FALSE(message_center_tray_->popups_visible());
+ ASSERT_FALSE(message_center_tray_->message_center_visible());
+
+ message_center_tray_->ToggleMessageCenterBubble();
+
+ ASSERT_FALSE(message_center_tray_->popups_visible());
+ ASSERT_FALSE(message_center_tray_->message_center_visible());
+
+ message_center_tray_->HidePopupBubble();
+
+ ASSERT_FALSE(message_center_tray_->popups_visible());
+ ASSERT_FALSE(message_center_tray_->message_center_visible());
+}
+
+} // namespace message_center
diff --git a/ui/views/bubble/tray_bubble_view.cc b/ui/views/bubble/tray_bubble_view.cc
index 3b13f2b..c81752d 100644
--- a/ui/views/bubble/tray_bubble_view.cc
+++ b/ui/views/bubble/tray_bubble_view.cc
@@ -48,7 +48,7 @@ class TrayBubbleBorder : public views::BubbleBorder {
owner_(owner),
anchor_(anchor),
tray_arrow_offset_(params.arrow_offset) {
- set_alignment(views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE);
+ set_alignment(params.arrow_alignment);
set_background_color(params.arrow_color);
set_paint_arrow(!params.hide_arrow);
}
@@ -60,7 +60,7 @@ class TrayBubbleBorder : public views::BubbleBorder {
// it has no arrow.
virtual gfx::Rect GetBounds(const gfx::Rect& position_relative_to,
const gfx::Size& contents_size) const OVERRIDE {
- if (arrow_location() != NONE) {
+ if (has_arrow(arrow_location())) {
return views::BubbleBorder::GetBounds(position_relative_to,
contents_size);
}
@@ -235,7 +235,8 @@ TrayBubbleView::InitParams::InitParams(AnchorType anchor_type,
arrow_location(views::BubbleBorder::NONE),
arrow_offset(kArrowDefaultOffset),
hide_arrow(false),
- shadow(views::BubbleBorder::BIG_SHADOW) {
+ shadow(views::BubbleBorder::BIG_SHADOW),
+ arrow_alignment(views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE) {
}
// static
@@ -295,7 +296,7 @@ TrayBubbleView::~TrayBubbleView() {
void TrayBubbleView::InitializeAndShowBubble() {
// Must occur after call to BubbleDelegateView::CreateBubble().
- SetAlignment(views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE);
+ SetAlignment(params_.arrow_alignment);
bubble_border_->UpdateArrowOffset();
if (get_use_acceleration_when_possible())
diff --git a/ui/views/bubble/tray_bubble_view.h b/ui/views/bubble/tray_bubble_view.h
index 218d885..353473a 100644
--- a/ui/views/bubble/tray_bubble_view.h
+++ b/ui/views/bubble/tray_bubble_view.h
@@ -32,11 +32,16 @@ class TrayBubbleContentMask;
class VIEWS_EXPORT TrayBubbleView : public views::BubbleDelegateView {
public:
+ // AnchorType differentiates between bubbles that are anchored on a tray
+ // element (ANCHOR_TYPE_TRAY) and display an arrow, or that are floating on
+ // the screen away from the tray (ANCHOR_TYPE_BUBBLE).
enum AnchorType {
ANCHOR_TYPE_TRAY,
- ANCHOR_TYPE_BUBBLE
+ ANCHOR_TYPE_BUBBLE,
};
+ // AnchorAlignment determines to which side of the anchor the bubble will
+ // align itself.
enum AnchorAlignment {
ANCHOR_ALIGNMENT_BOTTOM,
ANCHOR_ALIGNMENT_LEFT,
@@ -97,6 +102,7 @@ class VIEWS_EXPORT TrayBubbleView : public views::BubbleDelegateView {
int arrow_offset;
bool hide_arrow; // hides arrow, but may still use "arrow geometry"
views::BubbleBorder::Shadow shadow;
+ views::BubbleBorder::BubbleAlignment arrow_alignment;
};
// Constructs and returns a TrayBubbleView. init_params may be modified.