Chromium Code Reviews| 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 |