| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/ui/views/message_center/web_notification_tray.h" | 5 #include "chrome/browser/ui/views/message_center/web_notification_tray.h" |
| 6 | 6 |
| 7 #include "base/i18n/number_formatting.h" | 7 #include "base/i18n/number_formatting.h" |
| 8 #include "base/strings/string16.h" | 8 #include "base/strings/string16.h" |
| 9 #include "base/strings/utf_string_conversions.h" | 9 #include "base/strings/utf_string_conversions.h" |
| 10 #include "chrome/browser/browser_process.h" | 10 #include "chrome/browser/browser_process.h" |
| 11 #include "chrome/browser/status_icons/status_icon.h" | 11 #include "chrome/browser/status_icons/status_icon.h" |
| 12 #include "chrome/browser/status_icons/status_tray.h" | 12 #include "chrome/browser/status_icons/status_tray.h" |
| 13 #include "chrome/browser/ui/views/message_center/notification_bubble_wrapper.h" | |
| 14 #include "content/public/browser/user_metrics.h" | 13 #include "content/public/browser/user_metrics.h" |
| 15 #include "grit/chromium_strings.h" | 14 #include "grit/chromium_strings.h" |
| 16 #include "grit/theme_resources.h" | 15 #include "grit/theme_resources.h" |
| 17 #include "grit/ui_strings.h" | 16 #include "grit/ui_strings.h" |
| 18 #include "ui/base/l10n/l10n_util.h" | 17 #include "ui/base/l10n/l10n_util.h" |
| 19 #include "ui/base/models/simple_menu_model.h" | |
| 20 #include "ui/base/resource/resource_bundle.h" | 18 #include "ui/base/resource/resource_bundle.h" |
| 21 #include "ui/gfx/canvas.h" | 19 #include "ui/gfx/canvas.h" |
| 22 #include "ui/gfx/image/image_skia_operations.h" | 20 #include "ui/gfx/image/image_skia_operations.h" |
| 23 #include "ui/gfx/rect.h" | 21 #include "ui/gfx/rect.h" |
| 24 #include "ui/gfx/screen.h" | 22 #include "ui/gfx/screen.h" |
| 25 #include "ui/gfx/size.h" | 23 #include "ui/gfx/size.h" |
| 26 #include "ui/message_center/message_center_tray.h" | 24 #include "ui/message_center/message_center_tray.h" |
| 27 #include "ui/message_center/message_center_tray_delegate.h" | 25 #include "ui/message_center/message_center_tray_delegate.h" |
| 28 #include "ui/message_center/views/message_bubble_base.h" | |
| 29 #include "ui/message_center/views/message_center_bubble.h" | |
| 30 #include "ui/message_center/views/message_popup_collection.h" | 26 #include "ui/message_center/views/message_popup_collection.h" |
| 31 #include "ui/views/widget/widget.h" | 27 #include "ui/views/widget/widget.h" |
| 32 | 28 |
| 33 namespace { | 29 namespace { |
| 34 | 30 |
| 35 // Tray constants | 31 // Tray constants |
| 36 const int kScreenEdgePadding = 2; | 32 const int kScreenEdgePadding = 2; |
| 37 | 33 |
| 38 const int kSystemTrayWidth = 16; | 34 const int kSystemTrayWidth = 16; |
| 39 const int kSystemTrayHeight = 16; | 35 const int kSystemTrayHeight = 16; |
| 40 const int kNumberOfSystemTraySprites = 10; | 36 const int kNumberOfSystemTraySprites = 10; |
| 41 | 37 |
| 42 gfx::Rect GetCornerAnchorRect() { | |
| 43 // TODO(dewittj): Use the preference to determine which corner to anchor from. | |
| 44 gfx::Screen* screen = gfx::Screen::GetNativeScreen(); | |
| 45 gfx::Rect rect = screen->GetPrimaryDisplay().work_area(); | |
| 46 rect.Inset(kScreenEdgePadding, kScreenEdgePadding); | |
| 47 return gfx::Rect(rect.bottom_right(), gfx::Size()); | |
| 48 } | |
| 49 | |
| 50 gfx::Point GetClosestCorner(gfx::Rect rect, gfx::Point query) { | |
| 51 gfx::Point center_point = rect.CenterPoint(); | |
| 52 gfx::Point rv; | |
| 53 | |
| 54 if (query.x() > center_point.x()) | |
| 55 rv.set_x(rect.right()); | |
| 56 else | |
| 57 rv.set_x(rect.x()); | |
| 58 | |
| 59 if (query.y() > center_point.y()) | |
| 60 rv.set_y(rect.bottom()); | |
| 61 else | |
| 62 rv.set_y(rect.y()); | |
| 63 | |
| 64 return rv; | |
| 65 } | |
| 66 | |
| 67 // GetMouseAnchorRect returns a rectangle that has one corner where the mouse | |
| 68 // clicked, and the opposite corner at the closest corner of the work area | |
| 69 // (inset by an appropriate margin.) | |
| 70 gfx::Rect GetMouseAnchorRect(gfx::Point cursor) { | |
| 71 // TODO(dewittj): GetNativeScreen could be wrong for Aura. | |
| 72 gfx::Screen* screen = gfx::Screen::GetNativeScreen(); | |
| 73 gfx::Rect work_area = screen->GetPrimaryDisplay().work_area(); | |
| 74 work_area.Inset(kScreenEdgePadding, kScreenEdgePadding); | |
| 75 gfx::Point corner = GetClosestCorner(work_area, cursor); | |
| 76 | |
| 77 gfx::Rect mouse_anchor_rect(gfx::BoundingRect(cursor, corner)); | |
| 78 return mouse_anchor_rect; | |
| 79 } | |
| 80 | |
| 81 gfx::ImageSkia GetIcon(int unread_count) { | 38 gfx::ImageSkia GetIcon(int unread_count) { |
| 82 bool has_unread = unread_count > 0; | 39 bool has_unread = unread_count > 0; |
| 83 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | 40 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
| 84 if (!has_unread) | 41 if (!has_unread) |
| 85 return *rb.GetImageSkiaNamed(IDR_NOTIFICATION_TRAY_EMPTY); | 42 return *rb.GetImageSkiaNamed(IDR_NOTIFICATION_TRAY_EMPTY); |
| 86 | 43 |
| 87 // TODO(dewittj): Use scale factors other than 100P. | 44 // TODO(dewittj): Use scale factors other than 100P. |
| 88 scoped_ptr<gfx::Canvas> canvas( | 45 scoped_ptr<gfx::Canvas> canvas( |
| 89 new gfx::Canvas(gfx::Size(kSystemTrayWidth, kSystemTrayHeight), | 46 new gfx::Canvas(gfx::Size(kSystemTrayWidth, kSystemTrayHeight), |
| 90 ui::SCALE_FACTOR_100P, | 47 ui::SCALE_FACTOR_100P, |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 144 popup_collection_.reset(new message_center::MessagePopupCollection( | 101 popup_collection_.reset(new message_center::MessagePopupCollection( |
| 145 NULL, message_center(), message_center_tray_.get())); | 102 NULL, message_center(), message_center_tray_.get())); |
| 146 return true; | 103 return true; |
| 147 } | 104 } |
| 148 | 105 |
| 149 void WebNotificationTray::HidePopups() { popup_collection_.reset(); } | 106 void WebNotificationTray::HidePopups() { popup_collection_.reset(); } |
| 150 | 107 |
| 151 bool WebNotificationTray::ShowMessageCenterInternal(bool show_settings) { | 108 bool WebNotificationTray::ShowMessageCenterInternal(bool show_settings) { |
| 152 content::RecordAction(UserMetricsAction("Notifications.ShowMessageCenter")); | 109 content::RecordAction(UserMetricsAction("Notifications.ShowMessageCenter")); |
| 153 | 110 |
| 154 // Using MessageBubbleBase instead of MessageCenterBubble to | 111 message_center_delegate_ = new MessageCenterWidgetDelegate( |
| 155 // remove dependence on implicit type conversion | 112 this, message_center_tray_.get(), show_settings /* settings initally |
| 156 scoped_ptr<message_center::MessageCenterBubble> bubble( | 113 invisible */); |
| 157 new message_center::MessageCenterBubble(message_center(), | |
| 158 message_center_tray_.get())); | |
| 159 | 114 |
| 160 gfx::Screen* screen = gfx::Screen::GetNativeScreen(); | |
| 161 gfx::Rect work_area = screen->GetPrimaryDisplay().work_area(); | |
| 162 views::TrayBubbleView::AnchorAlignment alignment = GetAnchorAlignment(); | |
| 163 | |
| 164 int max_height = work_area.height(); | |
| 165 | |
| 166 // If the alignment is left- or right-oriented, the bubble can fill up the | |
| 167 // entire vertical height of the screen since the bubble is rendered to the | |
| 168 // side of the clicked icon. Otherwise we have to adjust for the arrow's | |
| 169 // height. | |
| 170 if (alignment == views::TrayBubbleView::ANCHOR_ALIGNMENT_BOTTOM || | |
| 171 alignment == views::TrayBubbleView::ANCHOR_ALIGNMENT_TOP) { | |
| 172 max_height -= 2 * kScreenEdgePadding; | |
| 173 | |
| 174 // If the work area contains the click point, then we know that the icon is | |
| 175 // not in the taskbar. Then we need to subtract the distance of the click | |
| 176 // point from the edge of the work area so we can see the whole bubble. | |
| 177 if (work_area.Contains(mouse_click_point_)) { | |
| 178 max_height -= std::min(mouse_click_point_.y() - work_area.y(), | |
| 179 work_area.bottom() - mouse_click_point_.y()); | |
| 180 } | |
| 181 } | |
| 182 bubble->SetMaxHeight(max_height); | |
| 183 if (show_settings) | |
| 184 bubble->SetSettingsVisible(); | |
| 185 | |
| 186 message_center_bubble_.reset(new internal::NotificationBubbleWrapper( | |
| 187 this, | |
| 188 bubble.PassAs<message_center::MessageBubbleBase>(), | |
| 189 internal::NotificationBubbleWrapper::BUBBLE_TYPE_MESSAGE_CENTER)); | |
| 190 return true; | 115 return true; |
| 191 } | 116 } |
| 192 | 117 |
| 193 bool WebNotificationTray::ShowMessageCenter() { | 118 bool WebNotificationTray::ShowMessageCenter() { |
| 194 return ShowMessageCenterInternal(false /* show_settings */); | 119 return ShowMessageCenterInternal(false /* show_settings */); |
| 195 } | 120 } |
| 196 | 121 |
| 197 void WebNotificationTray::HideMessageCenter() { | 122 void WebNotificationTray::HideMessageCenter() { |
| 198 message_center_bubble_.reset(); | 123 if (message_center_delegate_) { |
| 124 message_center_delegate_->CloseWidget(); |
| 125 } |
| 199 } | 126 } |
| 200 | 127 |
| 201 void WebNotificationTray::UpdatePopups() { | 128 void WebNotificationTray::UpdatePopups() { |
| 202 // |popup_collection_| receives notification add/remove events and updates | 129 // |popup_collection_| receives notification add/remove events and updates |
| 203 // itself, so this method doesn't need to do anything. | 130 // itself, so this method doesn't need to do anything. |
| 204 // TODO(mukai): remove this method (currently this is used by | 131 // TODO(mukai): remove this method (currently this is used by |
| 205 // non-rich-notifications in ChromeOS). | 132 // non-rich-notifications in ChromeOS). |
| 206 }; | 133 }; |
| 207 | 134 |
| 208 bool WebNotificationTray::ShowNotifierSettings() { | 135 bool WebNotificationTray::ShowNotifierSettings() { |
| 209 if (message_center_bubble_) { | 136 if (message_center_delegate_) { |
| 210 static_cast<MessageCenterBubble*>( | 137 message_center_delegate_->SetSettingsVisible(true); |
| 211 message_center_bubble_->bubble())->SetSettingsVisible(); | |
| 212 return true; | 138 return true; |
| 213 } | 139 } |
| 214 return ShowMessageCenterInternal(true /* show_settings */); | 140 return ShowMessageCenterInternal(true /* show_settings */); |
| 215 } | 141 } |
| 216 | 142 |
| 217 void WebNotificationTray::OnMessageCenterTrayChanged() { | 143 void WebNotificationTray::OnMessageCenterTrayChanged() { |
| 218 // See the comments in ash/system/web_notification/web_notification_tray.cc | 144 // See the comments in ash/system/web_notification/web_notification_tray.cc |
| 219 // for why PostTask. | 145 // for why PostTask. |
| 220 should_update_tray_content_ = true; | 146 should_update_tray_content_ = true; |
| 221 base::MessageLoop::current()->PostTask( | 147 base::MessageLoop::current()->PostTask( |
| 222 FROM_HERE, | 148 FROM_HERE, |
| 223 base::Bind(&WebNotificationTray::UpdateStatusIcon, AsWeakPtr())); | 149 base::Bind(&WebNotificationTray::UpdateStatusIcon, AsWeakPtr())); |
| 224 } | 150 } |
| 225 | 151 |
| 226 gfx::Rect WebNotificationTray::GetMessageCenterAnchor() { | 152 void WebNotificationTray::OnStatusIconClicked() { |
| 227 return GetMouseAnchorRect(mouse_click_point_); | 153 // TODO(dewittj): It's possible GetNativeScreen is wrong for win-aura. |
| 154 gfx::Screen* screen = gfx::Screen::GetNativeScreen(); |
| 155 mouse_click_point_ = screen->GetCursorScreenPoint(); |
| 156 message_center_tray_->ToggleMessageCenterBubble(); |
| 228 } | 157 } |
| 229 | 158 |
| 230 gfx::Rect WebNotificationTray::GetPopupAnchor() { | |
| 231 return GetCornerAnchorRect(); | |
| 232 } | |
| 233 | |
| 234 views::TrayBubbleView::AnchorAlignment | |
| 235 WebNotificationTray::GetAnchorAlignment() { | |
| 236 gfx::Screen* screen = gfx::Screen::GetNativeScreen(); | |
| 237 // TODO(dewittj): It's possible GetPrimaryDisplay is wrong. | |
| 238 gfx::Rect screen_bounds = screen->GetPrimaryDisplay().bounds(); | |
| 239 gfx::Rect work_area = screen->GetPrimaryDisplay().work_area(); | |
| 240 | |
| 241 // Comparing the work area to the screen bounds gives us the location of the | |
| 242 // taskbar. If the work area is less tall than the screen, assume the taskbar | |
| 243 // is on the bottom, and cause the arrow to be displayed on the bottom of the | |
| 244 // bubble. Otherwise, cause the arrow to be displayed on the side of the | |
| 245 // bubble that the taskbar is on. | |
| 246 if (work_area.width() < screen_bounds.width()) { | |
| 247 if (work_area.x() > screen_bounds.x()) | |
| 248 return views::TrayBubbleView::ANCHOR_ALIGNMENT_LEFT; | |
| 249 return views::TrayBubbleView::ANCHOR_ALIGNMENT_RIGHT; | |
| 250 } | |
| 251 return views::TrayBubbleView::ANCHOR_ALIGNMENT_BOTTOM; | |
| 252 } | |
| 253 | |
| 254 gfx::NativeView WebNotificationTray::GetBubbleWindowContainer() { return NULL; } | |
| 255 | |
| 256 void WebNotificationTray::UpdateStatusIcon() { | 159 void WebNotificationTray::UpdateStatusIcon() { |
| 257 if (!should_update_tray_content_) | 160 if (!should_update_tray_content_) |
| 258 return; | 161 return; |
| 259 should_update_tray_content_ = false; | 162 should_update_tray_content_ = false; |
| 260 | 163 |
| 261 int total_notifications = message_center()->NotificationCount(); | 164 int total_notifications = message_center()->NotificationCount(); |
| 262 if (total_notifications == 0) { | 165 if (total_notifications == 0) { |
| 263 DestroyStatusIcon(); | 166 DestroyStatusIcon(); |
| 264 return; | 167 return; |
| 265 } | 168 } |
| 266 | 169 |
| 267 int unread_notifications = message_center()->UnreadNotificationCount(); | 170 int unread_notifications = message_center()->UnreadNotificationCount(); |
| 268 StatusIcon* status_icon = GetStatusIcon(); | 171 StatusIcon* status_icon = GetStatusIcon(); |
| 269 if (!status_icon) | 172 if (!status_icon) |
| 270 return; | 173 return; |
| 271 | 174 |
| 272 status_icon->SetImage(GetIcon(unread_notifications)); | 175 status_icon->SetImage(GetIcon(unread_notifications)); |
| 273 | 176 |
| 274 string16 product_name(l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)); | 177 string16 product_name(l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)); |
| 275 if (unread_notifications > 0) { | 178 if (unread_notifications > 0) { |
| 276 string16 str_unread_count = base::FormatNumber(unread_notifications); | 179 string16 str_unread_count = base::FormatNumber(unread_notifications); |
| 277 status_icon->SetToolTip(l10n_util::GetStringFUTF16( | 180 status_icon->SetToolTip(l10n_util::GetStringFUTF16( |
| 278 IDS_MESSAGE_CENTER_TOOLTIP_UNREAD, product_name, str_unread_count)); | 181 IDS_MESSAGE_CENTER_TOOLTIP_UNREAD, product_name, str_unread_count)); |
| 279 } else { | 182 } else { |
| 280 status_icon->SetToolTip( | 183 status_icon->SetToolTip( |
| 281 l10n_util::GetStringFUTF16(IDS_MESSAGE_CENTER_TOOLTIP, product_name)); | 184 l10n_util::GetStringFUTF16(IDS_MESSAGE_CENTER_TOOLTIP, product_name)); |
| 282 } | 185 } |
| 283 } | 186 } |
| 284 | 187 |
| 285 void WebNotificationTray::OnStatusIconClicked() { | 188 void WebNotificationTray::HideBubble() { |
| 286 // TODO(dewittj): It's possible GetNativeScreen is wrong for win-aura. | 189 if (message_center_delegate_) { |
| 287 gfx::Screen* screen = gfx::Screen::GetNativeScreen(); | |
| 288 mouse_click_point_ = screen->GetCursorScreenPoint(); | |
| 289 message_center_tray_->ToggleMessageCenterBubble(); | |
| 290 } | |
| 291 | |
| 292 void WebNotificationTray::HideBubbleWithView( | |
| 293 const views::TrayBubbleView* bubble_view) { | |
| 294 if (message_center_bubble_.get() && | |
| 295 bubble_view == message_center_bubble_->bubble_view()) { | |
| 296 message_center_tray_->HideMessageCenterBubble(); | 190 message_center_tray_->HideMessageCenterBubble(); |
| 297 } | 191 } |
| 298 } | 192 } |
| 299 | 193 |
| 300 StatusIcon* WebNotificationTray::GetStatusIcon() { | 194 StatusIcon* WebNotificationTray::GetStatusIcon() { |
| 301 if (status_icon_) | 195 if (status_icon_) |
| 302 return status_icon_; | 196 return status_icon_; |
| 303 | 197 |
| 304 StatusTray* status_tray = g_browser_process->status_tray(); | 198 StatusTray* status_tray = g_browser_process->status_tray(); |
| 305 if (!status_tray) | 199 if (!status_tray) |
| (...skipping 19 matching lines...) Expand all Loading... |
| 325 if (status_tray) | 219 if (status_tray) |
| 326 status_tray->RemoveStatusIcon(status_icon_); | 220 status_tray->RemoveStatusIcon(status_icon_); |
| 327 status_icon_ = NULL; | 221 status_icon_ = NULL; |
| 328 } | 222 } |
| 329 | 223 |
| 330 void WebNotificationTray::AddQuietModeMenu(StatusIcon* status_icon) { | 224 void WebNotificationTray::AddQuietModeMenu(StatusIcon* status_icon) { |
| 331 DCHECK(status_icon); | 225 DCHECK(status_icon); |
| 332 status_icon->SetContextMenu(message_center_tray_->CreateQuietModeMenu()); | 226 status_icon->SetContextMenu(message_center_tray_->CreateQuietModeMenu()); |
| 333 } | 227 } |
| 334 | 228 |
| 335 message_center::MessageCenterBubble* | 229 MessageCenterWidgetDelegate* |
| 336 WebNotificationTray::GetMessageCenterBubbleForTest() { | 230 WebNotificationTray::GetMessageCenterWidgetDelegateForTest() { |
| 337 if (!message_center_bubble_.get()) | 231 return message_center_delegate_; |
| 338 return NULL; | |
| 339 return static_cast<message_center::MessageCenterBubble*>( | |
| 340 message_center_bubble_->bubble()); | |
| 341 } | 232 } |
| 342 | 233 |
| 343 } // namespace message_center | 234 } // namespace message_center |
| OLD | NEW |