Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(333)

Side by Side Diff: chrome/browser/ui/views/message_center/web_notification_tray.cc

Issue 18003003: Message center re-organized (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Comments applied Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 18 matching lines...) Expand all
109 false); 66 false);
110 return gfx::ImageSkia(canvas->ExtractImageRep()); 67 return gfx::ImageSkia(canvas->ExtractImageRep());
111 } 68 }
112 69
113 } // namespace 70 } // namespace
114 71
115 using content::UserMetricsAction; 72 using content::UserMetricsAction;
116 73
117 namespace message_center { 74 namespace message_center {
118 75
76 namespace internal {
77
78 // Gets the position of the systray (same as position of taskbar) from the
79 // work area bounds. Returns ALIGNMENT_NONE if position cannot be found.
80 Alignment GetSystrayAlignment() {
81 gfx::Screen* screen = gfx::Screen::GetNativeScreen();
82 // TODO(dewittj): It's possible GetPrimaryDisplay is wrong.
83 gfx::Rect screen_bounds = screen->GetPrimaryDisplay().bounds();
84 gfx::Rect work_area = screen->GetPrimaryDisplay().work_area();
85 work_area.Inset(kScreenEdgePadding, kScreenEdgePadding);
86
87 // Comparing the work area to the screen bounds gives us the location of the
88 // taskbar. If the work area is exactly the same as the screen bounds,
89 // we are unable to locate the systray so we say we don't know it's alignment.
90 if (work_area.height() < screen_bounds.height()) {
91 if (work_area.y() > screen_bounds.y())
92 return ALIGNMENT_TOP;
93 return ALIGNMENT_BOTTOM;
94 }
95 if (work_area.width() < screen_bounds.width()) {
96 if (work_area.x() > screen_bounds.x())
97 return ALIGNMENT_LEFT;
98 return ALIGNMENT_RIGHT;
99 }
100
101 return ALIGNMENT_NONE;
102 }
103
104 gfx::Point GetClosestCorner(const gfx::Rect& rect, const gfx::Point& query) {
105 gfx::Point center_point = rect.CenterPoint();
106 gfx::Point rv;
107
108 if (query.x() > center_point.x())
109 rv.set_x(rect.right());
110 else
111 rv.set_x(rect.x());
112
113 if (query.y() > center_point.y())
114 rv.set_y(rect.bottom());
115 else
116 rv.set_y(rect.y());
117
118 return rv;
119 }
120
121 // Gets the corner of the screen where the message center should pop up.
122 Alignment GetAnchorAlignment(const gfx::Rect& work_area, gfx::Point corner) {
123 gfx::Point center = work_area.CenterPoint();
124
125 Alignment anchor_alignment =
126 center.y() > corner.y() ? ALIGNMENT_TOP : ALIGNMENT_BOTTOM;
127 anchor_alignment =
128 (Alignment)(anchor_alignment |
129 (center.x() > corner.x() ? ALIGNMENT_LEFT : ALIGNMENT_RIGHT));
130
131 return anchor_alignment;
132 }
133
134 } // namespace internal
135
119 MessageCenterTrayDelegate* CreateMessageCenterTray() { 136 MessageCenterTrayDelegate* CreateMessageCenterTray() {
120 return new WebNotificationTray(); 137 return new WebNotificationTray();
121 } 138 }
122 139
123 WebNotificationTray::WebNotificationTray() 140 WebNotificationTray::WebNotificationTray()
124 : status_icon_(NULL), 141 : status_icon_(NULL),
125 message_center_visible_(false), 142 message_center_visible_(false),
126 should_update_tray_content_(true) { 143 should_update_tray_content_(true) {
127 message_center_tray_.reset( 144 message_center_tray_.reset(
128 new MessageCenterTray(this, g_browser_process->message_center())); 145 new MessageCenterTray(this, g_browser_process->message_center()));
(...skipping 15 matching lines...) Expand all
144 popup_collection_.reset(new message_center::MessagePopupCollection( 161 popup_collection_.reset(new message_center::MessagePopupCollection(
145 NULL, message_center(), message_center_tray_.get())); 162 NULL, message_center(), message_center_tray_.get()));
146 return true; 163 return true;
147 } 164 }
148 165
149 void WebNotificationTray::HidePopups() { popup_collection_.reset(); } 166 void WebNotificationTray::HidePopups() { popup_collection_.reset(); }
150 167
151 bool WebNotificationTray::ShowMessageCenterInternal(bool show_settings) { 168 bool WebNotificationTray::ShowMessageCenterInternal(bool show_settings) {
152 content::RecordAction(UserMetricsAction("Notifications.ShowMessageCenter")); 169 content::RecordAction(UserMetricsAction("Notifications.ShowMessageCenter"));
153 170
154 // Using MessageBubbleBase instead of MessageCenterBubble to 171 message_center_delegate_ = new MessageCenterWidgetDelegate(
155 // remove dependence on implicit type conversion 172 this,
156 scoped_ptr<message_center::MessageCenterBubble> bubble( 173 message_center_tray_.get(),
157 new message_center::MessageCenterBubble(message_center(), 174 show_settings, /* settings initally invisible */
158 message_center_tray_.get())); 175 GetPositionInfo());
159 176
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; 177 return true;
191 } 178 }
192 179
193 bool WebNotificationTray::ShowMessageCenter() { 180 bool WebNotificationTray::ShowMessageCenter() {
194 return ShowMessageCenterInternal(false /* show_settings */); 181 return ShowMessageCenterInternal(false /* show_settings */);
195 } 182 }
196 183
197 void WebNotificationTray::HideMessageCenter() { 184 void WebNotificationTray::HideMessageCenter() {
198 message_center_bubble_.reset(); 185 if (message_center_delegate_) {
186 message_center_delegate_->CloseWidget();
187 }
199 } 188 }
200 189
201 void WebNotificationTray::UpdatePopups() { 190 void WebNotificationTray::UpdatePopups() {
202 // |popup_collection_| receives notification add/remove events and updates 191 // |popup_collection_| receives notification add/remove events and updates
203 // itself, so this method doesn't need to do anything. 192 // itself, so this method doesn't need to do anything.
204 // TODO(mukai): remove this method (currently this is used by 193 // TODO(mukai): remove this method (currently this is used by
205 // non-rich-notifications in ChromeOS). 194 // non-rich-notifications in ChromeOS).
206 }; 195 };
207 196
208 bool WebNotificationTray::ShowNotifierSettings() { 197 bool WebNotificationTray::ShowNotifierSettings() {
209 if (message_center_bubble_) { 198 if (message_center_delegate_) {
210 static_cast<MessageCenterBubble*>( 199 message_center_delegate_->SetSettingsVisible(true);
211 message_center_bubble_->bubble())->SetSettingsVisible();
212 return true; 200 return true;
213 } 201 }
214 return ShowMessageCenterInternal(true /* show_settings */); 202 return ShowMessageCenterInternal(true /* show_settings */);
215 } 203 }
216 204
217 void WebNotificationTray::OnMessageCenterTrayChanged() { 205 void WebNotificationTray::OnMessageCenterTrayChanged() {
218 // See the comments in ash/system/web_notification/web_notification_tray.cc 206 // See the comments in ash/system/web_notification/web_notification_tray.cc
219 // for why PostTask. 207 // for why PostTask.
220 should_update_tray_content_ = true; 208 should_update_tray_content_ = true;
221 base::MessageLoop::current()->PostTask( 209 base::MessageLoop::current()->PostTask(
222 FROM_HERE, 210 FROM_HERE,
223 base::Bind(&WebNotificationTray::UpdateStatusIcon, AsWeakPtr())); 211 base::Bind(&WebNotificationTray::UpdateStatusIcon, AsWeakPtr()));
224 } 212 }
225 213
226 gfx::Rect WebNotificationTray::GetMessageCenterAnchor() { 214 void WebNotificationTray::OnStatusIconClicked() {
227 return GetMouseAnchorRect(mouse_click_point_); 215 // TODO(dewittj): It's possible GetNativeScreen is wrong for win-aura.
216 gfx::Screen* screen = gfx::Screen::GetNativeScreen();
217 mouse_click_point_ = screen->GetCursorScreenPoint();
218 message_center_tray_->ToggleMessageCenterBubble();
228 } 219 }
229 220
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() { 221 void WebNotificationTray::UpdateStatusIcon() {
257 if (!should_update_tray_content_) 222 if (!should_update_tray_content_)
258 return; 223 return;
259 should_update_tray_content_ = false; 224 should_update_tray_content_ = false;
260 225
261 int total_notifications = message_center()->NotificationCount(); 226 int total_notifications = message_center()->NotificationCount();
262 if (total_notifications == 0) { 227 if (total_notifications == 0) {
263 DestroyStatusIcon(); 228 DestroyStatusIcon();
264 return; 229 return;
265 } 230 }
266 231
267 int unread_notifications = message_center()->UnreadNotificationCount(); 232 int unread_notifications = message_center()->UnreadNotificationCount();
268 StatusIcon* status_icon = GetStatusIcon(); 233 StatusIcon* status_icon = GetStatusIcon();
269 if (!status_icon) 234 if (!status_icon)
270 return; 235 return;
271 236
272 status_icon->SetImage(GetIcon(unread_notifications)); 237 status_icon->SetImage(GetIcon(unread_notifications));
273 238
274 string16 product_name(l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)); 239 string16 product_name(l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME));
275 if (unread_notifications > 0) { 240 if (unread_notifications > 0) {
276 string16 str_unread_count = base::FormatNumber(unread_notifications); 241 string16 str_unread_count = base::FormatNumber(unread_notifications);
277 status_icon->SetToolTip(l10n_util::GetStringFUTF16( 242 status_icon->SetToolTip(l10n_util::GetStringFUTF16(
278 IDS_MESSAGE_CENTER_TOOLTIP_UNREAD, product_name, str_unread_count)); 243 IDS_MESSAGE_CENTER_TOOLTIP_UNREAD, product_name, str_unread_count));
279 } else { 244 } else {
280 status_icon->SetToolTip( 245 status_icon->SetToolTip(
281 l10n_util::GetStringFUTF16(IDS_MESSAGE_CENTER_TOOLTIP, product_name)); 246 l10n_util::GetStringFUTF16(IDS_MESSAGE_CENTER_TOOLTIP, product_name));
282 } 247 }
283 } 248 }
284 249
285 void WebNotificationTray::OnStatusIconClicked() { 250 void WebNotificationTray::HideBubble() {
286 // TODO(dewittj): It's possible GetNativeScreen is wrong for win-aura. 251 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(); 252 message_center_tray_->HideMessageCenterBubble();
297 } 253 }
298 } 254 }
299 255
256 PositionInfo WebNotificationTray::GetPositionInfo() {
257 PositionInfo pos_info;
258
259 gfx::Screen* screen = gfx::Screen::GetNativeScreen();
260 gfx::Rect work_area = screen->GetPrimaryDisplay().work_area();
261 work_area.Inset(kScreenEdgePadding, kScreenEdgePadding);
262
263 gfx::Point corner = internal::GetClosestCorner(work_area, mouse_click_point_);
264
265 pos_info.systray_alignment = internal::GetSystrayAlignment();
266
267 // We assume the systray and taskbar are either at the top or at the bottom
268 // if we are not able to find it.
269 if (pos_info.systray_alignment == ALIGNMENT_NONE) {
270 if (mouse_click_point_.y() > corner.y())
271 pos_info.systray_alignment = ALIGNMENT_TOP;
272 else
273 pos_info.systray_alignment = ALIGNMENT_BOTTOM;
274 }
275
276 pos_info.bubble_alignment = internal::GetAnchorAlignment(work_area, corner);
277
278 pos_info.inital_anchor_point = corner;
279 pos_info.max_height = work_area.height();
280
281 if (work_area.Contains(mouse_click_point_)) {
282 pos_info.inital_anchor_point.set_y(mouse_click_point_.y());
283 pos_info.max_height -= std::abs(mouse_click_point_.y() - corner.y());
284 }
285 return pos_info;
286 }
287
300 StatusIcon* WebNotificationTray::GetStatusIcon() { 288 StatusIcon* WebNotificationTray::GetStatusIcon() {
301 if (status_icon_) 289 if (status_icon_)
302 return status_icon_; 290 return status_icon_;
303 291
304 StatusTray* status_tray = g_browser_process->status_tray(); 292 StatusTray* status_tray = g_browser_process->status_tray();
305 if (!status_tray) 293 if (!status_tray)
306 return NULL; 294 return NULL;
307 295
308 StatusIcon* status_icon = status_tray->CreateStatusIcon(); 296 StatusIcon* status_icon = status_tray->CreateStatusIcon();
309 if (!status_icon) 297 if (!status_icon)
(...skipping 15 matching lines...) Expand all
325 if (status_tray) 313 if (status_tray)
326 status_tray->RemoveStatusIcon(status_icon_); 314 status_tray->RemoveStatusIcon(status_icon_);
327 status_icon_ = NULL; 315 status_icon_ = NULL;
328 } 316 }
329 317
330 void WebNotificationTray::AddQuietModeMenu(StatusIcon* status_icon) { 318 void WebNotificationTray::AddQuietModeMenu(StatusIcon* status_icon) {
331 DCHECK(status_icon); 319 DCHECK(status_icon);
332 status_icon->SetContextMenu(message_center_tray_->CreateQuietModeMenu()); 320 status_icon->SetContextMenu(message_center_tray_->CreateQuietModeMenu());
333 } 321 }
334 322
335 message_center::MessageCenterBubble* 323 MessageCenterWidgetDelegate*
336 WebNotificationTray::GetMessageCenterBubbleForTest() { 324 WebNotificationTray::GetMessageCenterWidgetDelegateForTest() {
337 if (!message_center_bubble_.get()) 325 return message_center_delegate_;
338 return NULL;
339 return static_cast<message_center::MessageCenterBubble*>(
340 message_center_bubble_->bubble());
341 } 326 }
342 327
343 } // namespace message_center 328 } // namespace message_center
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698