Index: ash/system/web_notification/web_notification_tray.cc |
diff --git a/ash/system/web_notification/web_notification_tray.cc b/ash/system/web_notification/web_notification_tray.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..fbe4f9433657e787380332f05b0ee2c827e429d5 |
--- /dev/null |
+++ b/ash/system/web_notification/web_notification_tray.cc |
@@ -0,0 +1,828 @@ |
+// 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_tray.h" |
+ |
+#include "ash/shell.h" |
+#include "ash/system/status_area_widget.h" |
+#include "ash/system/status_area_widget.h" |
+#include "ash/system/tray/tray_bubble_view.h" |
+#include "ash/system/tray/tray_constants.h" |
+#include "ash/system/tray/tray_views.h" |
+#include "grit/ash_strings.h" |
+#include "grit/ui_resources.h" |
+#include "grit/ui_resources_standard.h" |
+#include "ui/aura/event.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/views/controls/button/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/fill_layout.h" |
+#include "ui/views/layout/grid_layout.h" |
+#include "ui/views/painter.h" |
+ |
+namespace { |
+ |
+const int kTrayBorder = 4; |
+const int kNotificationIconWidth = 40; |
+const int kNotificationIconHeight = 25; |
+const int kWebNotificationBubbleMinHeight = 80; |
+const int kWebNotificationBubbleMaxHeight = 400; |
+const int kWebNotificationWidth = 400; |
+const int kWebNotificationButtonWidth = 32; |
+ |
+const int kTogglePermissionCommand = 0; |
+const int kToggleExtensionCommand = 1; |
+const int kShowSettingsCommand = 2; |
+ |
+// The image has three icons: 1 notifiaction, 2 notifications, and 3+. |
+SkBitmap GetNotificationImage(int notification_count) { |
+ SkBitmap image; |
+ gfx::Image all = ui::ResourceBundle::GetSharedInstance().GetImageNamed( |
+ IDR_AURA_UBER_TRAY_WEB_NOTIFICATON); |
+ int image_index = notification_count - 1; |
+ image_index = std::max(0, std::min(image_index, 2)); |
+ SkIRect region = SkIRect::MakeXYWH( |
+ 0, image_index * kNotificationIconHeight, |
+ kNotificationIconWidth, kNotificationIconHeight); |
+ all.ToSkBitmap()->extractSubset(&image, region); |
+ return image; |
+} |
+ |
+} // namespace |
+ |
+namespace ash { |
+ |
+namespace internal { |
+ |
+struct WebNotification { |
+ WebNotification(const std::string& i, |
+ const string16& t, |
+ const string16& m, |
+ const string16& s, |
+ const std::string& e) |
+ : id(i), |
+ title(t), |
+ message(m), |
+ display_source(s), |
+ extension_id(e) { |
+ } |
+ |
+ std::string id; |
+ string16 title; |
+ string16 message; |
+ string16 display_source; |
+ std::string extension_id; |
+ gfx::ImageSkia image; |
+}; |
+ |
+// A helper class to manage the list of notifications. |
+class WebNotificationList { |
+ public: |
+ typedef std::list<WebNotification> Notifications; |
+ |
+ WebNotificationList() { |
+ } |
+ |
+ void AddNotification(const std::string& id, |
+ const string16& title, |
+ const string16& message, |
+ const string16& display_source, |
+ const std::string& extension_id) { |
+ Notifications::iterator iter = GetNotification(id); |
+ if (iter != notifications_.end()) { |
+ // Update existing notification. |
+ iter->title = title; |
+ iter->message = message; |
+ iter->display_source = display_source; |
+ iter->extension_id = extension_id; |
+ } else { |
+ notifications_.push_back( |
+ WebNotification(id, title, message, display_source, extension_id)); |
+ } |
+ } |
+ |
+ void UpdateNotificationMessage(const std::string& id, |
+ const string16& title, |
+ const string16& message) { |
+ Notifications::iterator iter = GetNotification(id); |
+ if (iter == notifications_.end()) |
+ return; |
+ iter->title = title; |
+ iter->message = message; |
+ } |
+ |
+ bool RemoveNotification(const std::string& id) { |
+ Notifications::iterator iter = GetNotification(id); |
+ if (iter == notifications_.end()) |
+ return false; |
+ notifications_.erase(iter); |
+ return true; |
+ } |
+ |
+ void RemoveAllNotifications() { |
+ notifications_.clear(); |
+ } |
+ |
+ void RemoveNotificationsBySource(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) |
+ notifications_.erase(curiter); |
+ } |
+ } |
+ |
+ void RemoveNotificationsByExtension(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) |
+ notifications_.erase(curiter); |
+ } |
+ } |
+ |
+ 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; |
+ } |
+ |
+ const Notifications& notifications() const { return notifications_; } |
+ |
+ private: |
+ 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(); |
+ } |
+ |
+ Notifications notifications_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(WebNotificationList); |
+}; |
+ |
+// A simple view for the text (title and message) of a notification. |
+class WebNotificationMessageView : public views::View { |
+ public: |
+ explicit WebNotificationMessageView(const WebNotification& notification) { |
+ 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); |
+ |
+ SetLayoutManager( |
+ new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1)); |
+ AddChildView(title); |
+ AddChildView(message); |
+ } |
+ |
+ virtual ~WebNotificationMessageView() { |
+ } |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(WebNotificationMessageView); |
+}; |
+ |
+// 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 views::MenuButtonListener { |
+ public: |
+ WebNotificationView(WebNotificationTray* tray, |
+ const WebNotification& notification) |
+ : tray_(tray), |
+ notification_(notification), |
+ icon_(NULL), |
+ menu_button_(NULL), |
+ close_button_(NULL) { |
+ InitView(tray, notification); |
+ } |
+ |
+ virtual ~WebNotificationView() { |
+ } |
+ |
+ void InitView(WebNotificationTray* tray, |
+ const WebNotification& notification) { |
+ set_border(views::Border::CreateSolidSidedBorder( |
+ 1, 0, 0, 0, kBorderLightColor)); |
+ set_background(views::Background::CreateSolidBackground(kBackgroundColor)); |
+ |
+ icon_ = new views::ImageView; |
+ icon_->SetImage(notification.image); |
+ |
+ WebNotificationMessageView* message_view |
+ = new WebNotificationMessageView(notification); |
+ |
+ close_button_ = new views::ImageButton(this); |
+ close_button_->SetImage( |
+ views::CustomButton::BS_NORMAL, |
+ ResourceBundle::GetSharedInstance().GetImageSkiaNamed( |
+ IDR_AURA_WINDOW_CLOSE)); |
+ |
+ if (!notification.extension_id.empty() || |
+ !notification.display_source.empty()) { |
+ menu_button_ = new views::MenuButton(NULL, string16(), this, true); |
+ menu_button_->set_border(NULL); |
+ } |
+ |
+ views::GridLayout* layout = new views::GridLayout(this); |
+ SetLayoutManager(layout); |
+ |
+ views::ColumnSet* columns = layout->AddColumnSet(0); |
+ |
+ columns->AddPaddingColumn(0, kTrayPopupPaddingHorizontal/2); |
+ |
+ // Notification Icon. |
+ columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER, |
+ 0, /* resize percent */ |
+ views::GridLayout::FIXED, |
+ kNotificationIconWidth, kNotificationIconWidth); |
+ |
+ columns->AddPaddingColumn(0, kTrayPopupPaddingHorizontal/2); |
+ |
+ // Notification message text. |
+ columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, |
+ 100, /* resize percent */ |
+ views::GridLayout::USE_PREF, 0, 0); |
+ |
+ columns->AddPaddingColumn(0, kTrayPopupPaddingHorizontal/2); |
+ |
+ // Close and menu buttons. |
+ columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER, |
+ 0, /* resize percent */ |
+ views::GridLayout::FIXED, |
+ kWebNotificationButtonWidth, |
+ kWebNotificationButtonWidth); |
+ |
+ columns->AddPaddingColumn(0, kTrayPopupPaddingHorizontal/2); |
+ |
+ // Layout rows |
+ layout->AddPaddingRow(0, kTrayPopupPaddingBetweenItems); |
+ |
+ layout->StartRow(0, 0); |
+ layout->AddView(icon_, 1, 2); |
+ layout->AddView(message_view, 1, 2); |
+ layout->AddView(close_button_); |
+ |
+ layout->StartRow(0, 0); |
+ if (menu_button_) { |
+ layout->SkipColumns(4); |
+ layout->AddView(menu_button_); |
+ } |
+ layout->AddPaddingRow(0, kTrayPopupPaddingBetweenItems); |
+ } |
+ |
+ // view::Views overrodes. |
+ virtual bool OnMousePressed(const views::MouseEvent& event) OVERRIDE { |
+ tray_->OnClicked(notification_.id); |
+ return true; |
+ } |
+ |
+ // Overridden from ButtonListener. |
+ virtual void ButtonPressed(views::Button* sender, |
+ const views::Event& event) OVERRIDE { |
+ if (sender == close_button_) |
+ tray_->RemoveNotification(notification_.id); |
+ } |
+ |
+ // Overridden from MenuButtonListener. |
+ virtual void OnMenuButtonClicked( |
+ View* source, const gfx::Point& point) OVERRIDE { |
+ if (source != menu_button_) |
+ return; |
+ WebNotificationMenuModel menu_model(tray_, notification_); |
+ views::MenuModelAdapter menu_model_adapter(&menu_model); |
+ views::MenuRunner menu_runner(menu_model_adapter.CreateMenu()); |
+ |
+ gfx::Point screen_location; |
+ views::View::ConvertPointToScreen(menu_button_, &screen_location); |
+ ignore_result(menu_runner.RunMenuAt( |
+ source->GetWidget()->GetTopLevelWidget(), |
+ menu_button_, |
+ gfx::Rect(screen_location, menu_button_->size()), |
+ views::MenuItemView::TOPRIGHT, |
+ views::MenuRunner::HAS_MNEMONICS)); |
+ } |
+ |
+ private: |
+ WebNotificationTray* tray_; |
+ WebNotification notification_; |
+ views::ImageView* icon_; |
+ views::MenuButton* menu_button_; |
+ views::ImageButton* close_button_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(WebNotificationView); |
+}; |
+ |
+// The view for the buttons at the bottom of the web notification tray. |
+class WebNotificationButtonView : public TrayPopupTextButtonContainer, |
+ public views::ButtonListener { |
+ public: |
+ explicit WebNotificationButtonView(WebNotificationTray* tray) |
+ : tray_(tray), |
+ settings_button_(NULL), |
+ 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)); |
+ |
+ ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
+ settings_button_ = new TrayPopupTextButton( |
+ this, rb.GetLocalizedString(IDS_ASH_WEB_NOTFICATION_TRAY_SETTINGS)); |
+ AddTextButton(settings_button_); |
+ |
+ close_all_button_ = new TrayPopupTextButton( |
+ this, rb.GetLocalizedString(IDS_ASH_WEB_NOTFICATION_TRAY_CLOSE_ALL)); |
+ AddTextButton(close_all_button_); |
+ } |
+ |
+ virtual ~WebNotificationButtonView() { |
+ } |
+ |
+ // Overridden from ButtonListener. |
+ virtual void ButtonPressed(views::Button* sender, |
+ const views::Event& event) OVERRIDE { |
+ if (sender == settings_button_) |
+ tray_->ShowSettings(""); |
+ else if (sender == close_all_button_) |
+ tray_->RemoveAllNotifications(); |
+ } |
+ |
+ private: |
+ WebNotificationTray* tray_; |
+ TrayPopupTextButton* settings_button_; |
+ TrayPopupTextButton* close_all_button_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(WebNotificationButtonView); |
+}; |
+ |
+} // namespace internal |
+ |
+using internal::WebNotificationList; |
+using internal::WebNotificationView; |
+ |
+class WebNotificationTray::BubbleContentsView : public views::View { |
+ public: |
+ explicit BubbleContentsView(WebNotificationTray* tray) |
+ : tray_(tray) { |
+ SetLayoutManager( |
+ new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1)); |
+ set_background(views::Background::CreateSolidBackground(kBackgroundColor)); |
+ |
+ scroll_content_ = new views::View; |
+ scroll_content_->SetLayoutManager( |
+ new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1)); |
+ scroller_ = new internal::FixedSizedScrollView; |
+ scroller_->SetContentsView(scroll_content_); |
+ AddChildView(scroller_); |
+ |
+ button_view_ = new internal::WebNotificationButtonView(tray); |
+ AddChildView(button_view_); |
+} |
+ |
+ void Update(const WebNotificationList::Notifications& notifications) { |
+ scroll_content_->RemoveAllChildViews(true); |
+ for (WebNotificationList::Notifications::const_iterator iter = |
+ notifications.begin(); iter != notifications.end(); ++iter) { |
+ WebNotificationView* view = new WebNotificationView(tray_, *iter); |
+ scroll_content_->AddChildView(view); |
+ } |
+ SizeScrollContent(); |
+ scroller_->Layout(); |
+ Layout(); |
+ PreferredSizeChanged(); |
+ SchedulePaint(); |
+ } |
+ |
+ private: |
+ void SizeScrollContent() { |
+ gfx::Size scroll_size = scroll_content_->GetPreferredSize(); |
+ int button_height = button_view_->GetPreferredSize().height(); |
+ int scroll_height = std::min( |
+ std::max(scroll_size.height(), |
+ kWebNotificationBubbleMinHeight - button_height), |
+ kWebNotificationBubbleMaxHeight - button_height); |
+ scroll_size.set_height(scroll_height); |
+ scroller_->set_fixed_size(scroll_size); |
+ } |
+ |
+ WebNotificationTray* tray_; |
+ internal::FixedSizedScrollView* scroller_; |
+ views::View* scroll_content_; |
+ internal::WebNotificationButtonView* button_view_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(BubbleContentsView); |
+}; |
+ |
+class WebNotificationTray::Bubble : public internal::TrayBubbleView::Host, |
+ public views::Widget::Observer { |
+ public: |
+ explicit Bubble(WebNotificationTray* tray) |
+ : tray_(tray), |
+ bubble_view_(NULL), |
+ bubble_widget_(NULL), |
+ contents_view_(NULL) { |
+ views::View* anchor = tray->tray_container(); |
+ views::BubbleBorder::ArrowLocation arrow_location; |
+ int arrow_offset = 0; |
+ if (tray_->shelf_alignment() == SHELF_ALIGNMENT_BOTTOM) { |
+ arrow_location = views::BubbleBorder::BOTTOM_RIGHT; |
+ arrow_offset = anchor->GetContentsBounds().width() / 2; |
+ } else if (tray_->shelf_alignment() == SHELF_ALIGNMENT_LEFT) { |
+ arrow_location = views::BubbleBorder::LEFT_BOTTOM; |
+ } else { |
+ arrow_location = views::BubbleBorder::RIGHT_BOTTOM; |
+ } |
+ bubble_view_ = new internal::TrayBubbleView( |
+ anchor, arrow_location, this, false, kWebNotificationWidth); |
+ bubble_view_->SetMaxHeight(kWebNotificationBubbleMaxHeight); |
+ |
+ bubble_widget_ = views::BubbleDelegateView::CreateBubble(bubble_view_); |
+ |
+ bubble_view_->SetAlignment(views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE); |
+ bubble_widget_->non_client_view()->frame_view()->set_background(NULL); |
+ bubble_view_->SetBubbleBorder(arrow_offset); |
+ |
+ bubble_widget_->AddObserver(this); |
+ |
+ contents_view_ = new BubbleContentsView(tray); |
+ bubble_view_->AddChildView(contents_view_); |
+ |
+ Update(); |
+ bubble_view_->Show(); |
+ } |
+ |
+ virtual ~Bubble() { |
+ if (bubble_view_) |
+ bubble_view_->reset_host(); |
+ if (bubble_widget_) { |
+ bubble_widget_->RemoveObserver(this); |
+ bubble_widget_->Close(); |
+ } |
+ } |
+ |
+ void Update() { |
+ contents_view_->Update(tray_->notification_list()->notifications()); |
+ bubble_view_->Layout(); |
+ bubble_view_->SchedulePaint(); |
+ } |
+ |
+ views::Widget* bubble_widget() const { return bubble_widget_; } |
+ |
+ // Overridden from TrayBubbleView::Host. |
+ virtual void BubbleViewDestroyed() OVERRIDE { |
+ bubble_view_ = NULL; |
+ contents_view_ = NULL; |
+ } |
+ |
+ virtual gfx::Rect GetAnchorRect() const OVERRIDE { |
+ gfx::Rect anchor_rect = tray_->tray_container()->GetScreenBounds(); |
+ return anchor_rect; |
+ } |
+ |
+ virtual void OnMouseEnteredView() OVERRIDE { |
+ } |
+ |
+ virtual void OnMouseExitedView() OVERRIDE { |
+ } |
+ |
+ // Overridden from views::Widget::Observer. |
+ virtual void OnWidgetClosing(views::Widget* widget) OVERRIDE { |
stevenjb
2012/06/13 19:05:02
Added this.
|
+ CHECK_EQ(bubble_widget_, widget); |
+ bubble_widget_ = NULL; |
+ tray_->HideBubble(); // Will destroy |this|. |
+ } |
+ |
+ private: |
+ WebNotificationTray* tray_; |
+ internal::TrayBubbleView* bubble_view_; |
+ views::Widget* bubble_widget_; |
+ BubbleContentsView* contents_view_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(Bubble); |
+}; |
+ |
+WebNotificationTray::WebNotificationTray( |
+ internal::StatusAreaWidget* status_area_widget) |
+ : status_area_widget_(status_area_widget), |
+ notification_list_(new WebNotificationList()), |
+ tray_container_(NULL), |
+ icon_(NULL), |
+ delegate_(NULL) { |
+ tray_container_ = new views::View; |
+ tray_container_->set_border(views::Border::CreateEmptyBorder( |
+ kTrayBorder, kTrayBorder, kTrayBorder, kTrayBorder)); |
+ SetShelfAlignment(shelf_alignment()); |
+ |
+ icon_ = new views::ImageView; |
+ tray_container_->AddChildView(icon_); |
+ UpdateIcon(); // Hides the tray initially. |
+ |
+ SetContents(tray_container_); |
+ |
+ Shell::GetInstance()->AddEnvEventFilter(this); |
+} |
+ |
+WebNotificationTray::~WebNotificationTray() { |
+ Shell::GetInstance()->RemoveEnvEventFilter(this); |
+} |
+ |
+void WebNotificationTray::SetDelegate(Delegate* delegate) { |
+ DCHECK(!delegate_); |
+ delegate_ = delegate; |
+} |
+ |
+void WebNotificationTray::AddNotification(const std::string& id, |
+ const string16& title, |
+ const string16& message, |
+ const string16& display_source, |
+ const std::string& extension_id) { |
+ notification_list_->AddNotification( |
+ id, title, message, display_source, extension_id); |
+ UpdateIcon(); |
+ if (bubble()) { |
+ bubble_->Update(); |
+ } else { |
+ status_area_widget_->ShowWebNotificationBubble( |
+ internal::StatusAreaWidget::NON_USER_ACTION); |
+ } |
+} |
+ |
+void WebNotificationTray::UpdateNotification(const std::string& id, |
+ const string16& title, |
+ const string16& message) { |
+ notification_list_->UpdateNotificationMessage(id, title, message); |
+ if (bubble()) |
+ bubble_->Update(); |
+} |
+ |
+void WebNotificationTray::RemoveNotification(const std::string& id) { |
+ if (!notification_list_->RemoveNotification(id)) |
+ return; |
+ if (delegate_) |
+ delegate_->NotificationRemoved(id); |
+ UpdateBubbleAndIcon(); |
+} |
+ |
+void WebNotificationTray::RemoveAllNotifications() { |
+ const WebNotificationList::Notifications& notifications = |
+ notification_list_->notifications(); |
+ if (delegate_) { |
+ for (WebNotificationList::Notifications::const_iterator loopiter = |
+ notifications.begin(); |
+ loopiter != notifications.end(); ) { |
+ WebNotificationList::Notifications::const_iterator curiter = loopiter++; |
+ std::string notification_id = curiter->id; |
+ // May call RemoveNotification and erase curiter. |
+ delegate_->NotificationRemoved(notification_id); |
+ } |
+ } |
+ notification_list_->RemoveAllNotifications(); |
+ UpdateBubbleAndIcon(); |
+} |
+ |
+void WebNotificationTray::SetNotificationImage(const std::string& id, |
+ const gfx::ImageSkia& image) { |
+ if (!notification_list_->SetNotificationImage(id, image)) |
+ return; |
+ if (bubble()) |
+ bubble_->Update(); |
+} |
+ |
+void WebNotificationTray::DisableByExtension(const std::string& id) { |
+ // When we disable notifications, we remove any existing matching |
+ // notifications to avoid adding complicated UI to re-enable the source. |
+ notification_list_->RemoveNotificationsByExtension(id); |
+ UpdateBubbleAndIcon(); |
+ if (delegate_) |
+ delegate_->DisableExtension(id); |
+} |
+ |
+void WebNotificationTray::DisableByUrl(const std::string& id) { |
+ // See comment for DisableByExtension. |
+ notification_list_->RemoveNotificationsBySource(id); |
+ UpdateBubbleAndIcon(); |
+ if (delegate_) |
+ delegate_->DisableNotificationsFromSource(id); |
+} |
+ |
+void WebNotificationTray::ShowBubble() { |
+ if (bubble()) |
+ return; |
+ bubble_.reset(new Bubble(this)); |
+} |
+ |
+void WebNotificationTray::HideBubble() { |
+ bubble_.reset(); |
+} |
+ |
+void WebNotificationTray::ShowSettings(const std::string& id) { |
+ if (delegate_) |
+ delegate_->ShowSettings(id); |
+} |
+ |
+void WebNotificationTray::OnClicked(const std::string& id) { |
+ if (delegate_) |
+ delegate_->OnClicked(id); |
+} |
+ |
+bool WebNotificationTray::PreHandleKeyEvent(aura::Window* target, |
+ aura::KeyEvent* event) { |
+ return false; |
+} |
+ |
+bool WebNotificationTray::PreHandleMouseEvent(aura::Window* target, |
+ aura::MouseEvent* event) { |
+ if (event->type() == ui::ET_MOUSE_PRESSED) |
+ return ProcessLocatedEvent(*event); |
+ return false; |
+} |
+ |
+ui::TouchStatus WebNotificationTray::PreHandleTouchEvent( |
+ aura::Window* target, |
+ aura::TouchEvent* event) { |
+ if (event->type() != ui::ET_TOUCH_PRESSED) |
+ return ui::TOUCH_STATUS_UNKNOWN; |
+ if (ProcessLocatedEvent(*event)) |
+ return ui::TOUCH_STATUS_END; |
+ return ui::TOUCH_STATUS_UNKNOWN; |
+} |
+ |
+ui::GestureStatus WebNotificationTray::PreHandleGestureEvent( |
+ aura::Window* target, |
+ aura::GestureEvent* event) { |
+ return ui::GESTURE_STATUS_UNKNOWN; |
+} |
+ |
+void WebNotificationTray::SetShelfAlignment(ShelfAlignment alignment) { |
+ internal::TrayBackgroundView::SetShelfAlignment(alignment); |
+ tray_container_->SetLayoutManager(new views::BoxLayout( |
+ alignment == SHELF_ALIGNMENT_BOTTOM ? |
+ views::BoxLayout::kHorizontal : views::BoxLayout::kVertical, |
+ 0, 0, 0)); |
+} |
+ |
+bool WebNotificationTray::PerformAction(const views::Event& event) { |
+ if (bubble()) { |
+ status_area_widget_->HideWebNotificationBubble(); |
+ } else { |
+ status_area_widget_->ShowWebNotificationBubble( |
+ internal::StatusAreaWidget::USER_ACTION); |
+ } |
+ return true; |
+} |
+ |
+int WebNotificationTray::GetNotificationCount() const { |
+ return notification_list()->notifications().size(); |
+} |
+ |
+void WebNotificationTray::UpdateIcon() { |
+ int count = GetNotificationCount(); |
+ if (count == 0) { |
+ SetVisible(false); |
+ } else { |
+ icon_->SetImage(gfx::ImageSkia(GetNotificationImage(count))); |
+ SetVisible(true); |
+ } |
+ PreferredSizeChanged(); |
+} |
+ |
+void WebNotificationTray::UpdateBubbleAndIcon() { |
+ UpdateIcon(); |
+ if (!bubble()) |
+ return; |
+ if (GetNotificationCount() == 0) |
+ status_area_widget_->HideWebNotificationBubble(); |
+ else |
+ bubble_->Update(); |
+} |
+ |
+bool WebNotificationTray::ProcessLocatedEvent(const aura::LocatedEvent& event) { |
+ if (!bubble()) |
+ return false; |
+ gfx::Rect bounds = |
+ bubble_->bubble_widget()->GetNativeWindow()->GetBoundsInRootWindow(); |
+ if (bounds.Contains(event.root_location())) |
+ return false; |
+ status_area_widget_->HideWebNotificationBubble(); |
+ // If the event occurred in the tray widget, don't process the click. |
+ bounds = GetWidget()->GetNativeWindow()->GetBoundsInRootWindow(); |
+ if (bounds.Contains(event.root_location())) |
+ return true; |
+ return false; |
+} |
+ |
+} // namespace ash |