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

Side by Side Diff: ash/system/web_notification/web_notification_tray.cc

Issue 2103603002: mash: Remove StatusAreaWidget references from system tray classes (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: review comments, rename SetSystemTrayHeight to SetTrayBubbleHeight Created 4 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
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ash/system/web_notification/web_notification_tray.h"
6
7 #include "ash/common/ash_switches.h"
8 #include "ash/common/material_design/material_design_controller.h"
9 #include "ash/common/session/session_state_delegate.h"
10 #include "ash/common/shelf/shelf_constants.h"
11 #include "ash/common/shelf/wm_shelf.h"
12 #include "ash/common/shelf/wm_shelf_util.h"
13 #include "ash/common/shell_window_ids.h"
14 #include "ash/common/system/tray/system_tray_delegate.h"
15 #include "ash/common/system/tray/tray_bubble_wrapper.h"
16 #include "ash/common/system/tray/tray_constants.h"
17 #include "ash/common/system/tray/tray_utils.h"
18 #include "ash/common/wm_lookup.h"
19 #include "ash/common/wm_root_window_controller.h"
20 #include "ash/common/wm_shell.h"
21 #include "ash/common/wm_window.h"
22 #include "ash/system/status_area_widget.h"
23 #include "ash/system/tray/system_tray.h"
24 #include "ash/system/web_notification/ash_popup_alignment_delegate.h"
25 #include "base/auto_reset.h"
26 #include "base/i18n/number_formatting.h"
27 #include "base/i18n/rtl.h"
28 #include "base/strings/utf_string_conversions.h"
29 #include "base/threading/thread_task_runner_handle.h"
30 #include "grit/ash_strings.h"
31 #include "ui/base/l10n/l10n_util.h"
32 #include "ui/display/display.h"
33 #include "ui/display/screen.h"
34 #include "ui/gfx/paint_vector_icon.h"
35 #include "ui/gfx/vector_icons_public.h"
36 #include "ui/message_center/message_center_style.h"
37 #include "ui/message_center/message_center_tray_delegate.h"
38 #include "ui/message_center/views/message_bubble_base.h"
39 #include "ui/message_center/views/message_center_bubble.h"
40 #include "ui/message_center/views/message_popup_collection.h"
41 #include "ui/strings/grit/ui_strings.h"
42 #include "ui/views/bubble/tray_bubble_view.h"
43 #include "ui/views/controls/button/custom_button.h"
44 #include "ui/views/controls/image_view.h"
45 #include "ui/views/controls/label.h"
46 #include "ui/views/controls/menu/menu_runner.h"
47 #include "ui/views/layout/fill_layout.h"
48
49 #if defined(OS_CHROMEOS)
50
51 namespace message_center {
52
53 MessageCenterTrayDelegate* CreateMessageCenterTray() {
54 // On Windows+Ash the Tray will not be hosted in ash::Shell.
55 NOTREACHED();
56 return NULL;
57 }
58
59 } // namespace message_center
60
61 #endif // defined(OS_CHROMEOS)
62
63 namespace ash {
64 namespace {
65
66 // Menu commands
67 const int kToggleQuietMode = 0;
68 const int kEnableQuietModeDay = 2;
69 }
70
71 namespace {
72
73 const SkColor kWebNotificationColorNoUnread =
74 SkColorSetARGB(128, 255, 255, 255);
75 const SkColor kWebNotificationColorWithUnread = SK_ColorWHITE;
76 const int kNoUnreadIconSize = 18;
77 }
78
79 // Class to initialize and manage the WebNotificationBubble and
80 // TrayBubbleWrapper instances for a bubble.
81 class WebNotificationBubbleWrapper {
82 public:
83 // Takes ownership of |bubble| and creates |bubble_wrapper_|.
84 WebNotificationBubbleWrapper(WebNotificationTray* tray,
85 message_center::MessageBubbleBase* bubble) {
86 bubble_.reset(bubble);
87 views::TrayBubbleView::AnchorAlignment anchor_alignment =
88 tray->GetAnchorAlignment();
89 views::TrayBubbleView::InitParams init_params =
90 bubble->GetInitParams(anchor_alignment);
91 views::View* anchor = tray->tray_container();
92 if (anchor_alignment == views::TrayBubbleView::ANCHOR_ALIGNMENT_BOTTOM) {
93 gfx::Point bounds(anchor->width() / 2, 0);
94 views::View::ConvertPointToWidget(anchor, &bounds);
95 init_params.arrow_offset = bounds.x();
96 }
97 DCHECK(anchor);
98 // TrayBubbleView uses |anchor| and |tray| to determine the parent
99 // container. See WebNotificationTray::OnBeforeBubbleWidgetInit().
100 views::TrayBubbleView* bubble_view =
101 views::TrayBubbleView::Create(anchor, tray, &init_params);
102 bubble_wrapper_.reset(new TrayBubbleWrapper(tray, bubble_view));
103 bubble_view->SetArrowPaintType(views::BubbleBorder::PAINT_NONE);
104 bubble->InitializeContents(bubble_view);
105 }
106
107 message_center::MessageBubbleBase* bubble() const { return bubble_.get(); }
108
109 // Convenience accessors.
110 views::TrayBubbleView* bubble_view() const { return bubble_->bubble_view(); }
111
112 private:
113 std::unique_ptr<message_center::MessageBubbleBase> bubble_;
114 std::unique_ptr<TrayBubbleWrapper> bubble_wrapper_;
115
116 DISALLOW_COPY_AND_ASSIGN(WebNotificationBubbleWrapper);
117 };
118
119 class WebNotificationButton : public views::CustomButton {
120 public:
121 WebNotificationButton(views::ButtonListener* listener)
122 : views::CustomButton(listener),
123 is_bubble_visible_(false),
124 unread_count_(0) {
125 SetLayoutManager(new views::FillLayout);
126
127 gfx::ImageSkia image;
128 if (MaterialDesignController::IsShelfMaterial()) {
129 image = CreateVectorIcon(gfx::VectorIconId::SHELF_NOTIFICATIONS,
130 kShelfIconColor);
131 } else {
132 image =
133 CreateVectorIcon(gfx::VectorIconId::NOTIFICATIONS, kNoUnreadIconSize,
134 kWebNotificationColorNoUnread);
135 }
136
137 no_unread_icon_.SetImage(image);
138 no_unread_icon_.set_owned_by_client();
139
140 unread_label_.set_owned_by_client();
141 SetupLabelForTray(&unread_label_);
142
143 AddChildView(&unread_label_);
144 }
145
146 void SetBubbleVisible(bool visible) {
147 if (visible == is_bubble_visible_)
148 return;
149
150 is_bubble_visible_ = visible;
151 UpdateIconVisibility();
152 }
153
154 void SetUnreadCount(int unread_count) {
155 // base::FormatNumber doesn't convert to arabic numeric characters.
156 // TODO(mukai): use ICU to support conversion for such locales.
157 unread_count_ = unread_count;
158 UpdateIconVisibility();
159 }
160
161 protected:
162 // Overridden from views::ImageButton:
163 gfx::Size GetPreferredSize() const override {
164 return gfx::Size(kShelfItemHeight, kShelfItemHeight);
165 }
166
167 int GetHeightForWidth(int width) const override {
168 return GetPreferredSize().height();
169 }
170
171 private:
172 void UpdateIconVisibility() {
173 if (unread_count_ == 0) {
174 if (!Contains(&no_unread_icon_)) {
175 RemoveAllChildViews(false /* delete_children */);
176 AddChildView(&no_unread_icon_);
177 }
178 } else {
179 if (!Contains(&unread_label_)) {
180 RemoveAllChildViews(false /* delete_children */);
181 AddChildView(&unread_label_);
182 }
183
184 // TODO(mukai): move NINE_PLUS message to ui_strings, it doesn't need to
185 // be in ash_strings.
186 unread_label_.SetText(
187 (unread_count_ > 9) ? l10n_util::GetStringUTF16(
188 IDS_ASH_NOTIFICATION_UNREAD_COUNT_NINE_PLUS)
189 : base::FormatNumber(unread_count_));
190 unread_label_.SetEnabledColor((unread_count_ > 0)
191 ? kWebNotificationColorWithUnread
192 : kWebNotificationColorNoUnread);
193 }
194 SchedulePaint();
195 }
196
197 bool is_bubble_visible_;
198 int unread_count_;
199
200 views::ImageView no_unread_icon_;
201 views::Label unread_label_;
202
203 DISALLOW_COPY_AND_ASSIGN(WebNotificationButton);
204 };
205
206 WebNotificationTray::WebNotificationTray(StatusAreaWidget* status_area_widget)
207 : TrayBackgroundView(status_area_widget->wm_shelf()),
208 status_area_widget_(status_area_widget),
209 button_(NULL),
210 show_message_center_on_unlock_(false),
211 should_update_tray_content_(false),
212 should_block_shelf_auto_hide_(false) {
213 DCHECK(status_area_widget_);
214 button_ = new WebNotificationButton(this);
215 button_->set_triggerable_event_flags(ui::EF_LEFT_MOUSE_BUTTON |
216 ui::EF_RIGHT_MOUSE_BUTTON);
217 tray_container()->AddChildView(button_);
218 button_->SetFocusBehavior(FocusBehavior::NEVER);
219 SetContentsBackground();
220 tray_container()->SetBorder(views::Border::NullBorder());
221 message_center_tray_.reset(new message_center::MessageCenterTray(
222 this, message_center::MessageCenter::Get()));
223 WmShelf* shelf = WmLookup::Get()
224 ->GetWindowForWidget(status_area_widget)
225 ->GetRootWindowController()
226 ->GetShelf();
227 popup_alignment_delegate_.reset(new AshPopupAlignmentDelegate(shelf));
228 popup_collection_.reset(new message_center::MessagePopupCollection(
229 message_center(), message_center_tray_.get(),
230 popup_alignment_delegate_.get()));
231 const display::Display& display = WmLookup::Get()
232 ->GetWindowForWidget(status_area_widget)
233 ->GetDisplayNearestWindow();
234 popup_alignment_delegate_->StartObserving(display::Screen::GetScreen(),
235 display);
236 OnMessageCenterTrayChanged();
237 }
238
239 WebNotificationTray::~WebNotificationTray() {
240 // Release any child views that might have back pointers before ~View().
241 message_center_bubble_.reset();
242 popup_alignment_delegate_.reset();
243 popup_collection_.reset();
244 }
245
246 // Public methods.
247
248 bool WebNotificationTray::ShowMessageCenterInternal(bool show_settings) {
249 if (!ShouldShowMessageCenter())
250 return false;
251
252 should_block_shelf_auto_hide_ = true;
253 message_center::MessageCenterBubble* message_center_bubble =
254 new message_center::MessageCenterBubble(message_center(),
255 message_center_tray_.get(), true);
256
257 int max_height;
258 if (IsHorizontalAlignment(shelf()->GetAlignment())) {
259 max_height = shelf()->GetIdealBounds().y();
260 } else {
261 // Assume the status area and bubble bottoms are aligned when vertical.
262 WmWindow* status_area_window =
263 WmLookup::Get()->GetWindowForWidget(status_area_widget_);
264 gfx::Rect bounds_in_root =
265 status_area_window->GetRootWindow()->ConvertRectFromScreen(
266 status_area_window->GetBoundsInScreen());
267 max_height = bounds_in_root.bottom();
268 }
269 message_center_bubble->SetMaxHeight(
270 std::max(0, max_height - GetTrayConstant(TRAY_SPACING)));
271 if (show_settings)
272 message_center_bubble->SetSettingsVisible();
273 message_center_bubble_.reset(
274 new WebNotificationBubbleWrapper(this, message_center_bubble));
275
276 status_area_widget_->SetHideSystemNotifications(true);
277 shelf()->UpdateAutoHideState();
278 button_->SetBubbleVisible(true);
279 SetDrawBackgroundAsActive(true);
280 return true;
281 }
282
283 bool WebNotificationTray::ShowMessageCenter() {
284 return ShowMessageCenterInternal(false /* show_settings */);
285 }
286
287 void WebNotificationTray::HideMessageCenter() {
288 if (!message_center_bubble())
289 return;
290 SetDrawBackgroundAsActive(false);
291 message_center_bubble_.reset();
292 should_block_shelf_auto_hide_ = false;
293 show_message_center_on_unlock_ = false;
294 status_area_widget_->SetHideSystemNotifications(false);
295 shelf()->UpdateAutoHideState();
296 button_->SetBubbleVisible(false);
297 }
298
299 void WebNotificationTray::SetSystemTrayHeight(int height) {
300 popup_alignment_delegate_->SetSystemTrayHeight(height);
301 }
302
303 int WebNotificationTray::system_tray_height_for_test() const {
304 return popup_alignment_delegate_->system_tray_height_for_test();
305 }
306
307 bool WebNotificationTray::ShowPopups() {
308 if (message_center_bubble())
309 return false;
310
311 popup_collection_->DoUpdateIfPossible();
312 return true;
313 }
314
315 void WebNotificationTray::HidePopups() {
316 DCHECK(popup_collection_.get());
317 popup_collection_->MarkAllPopupsShown();
318 }
319
320 // Private methods.
321
322 bool WebNotificationTray::ShouldShowMessageCenter() {
323 return !(status_area_widget_->system_tray() &&
324 status_area_widget_->system_tray()->HasNotificationBubble());
325 }
326
327 bool WebNotificationTray::ShouldBlockShelfAutoHide() const {
328 return should_block_shelf_auto_hide_;
329 }
330
331 bool WebNotificationTray::IsMessageCenterBubbleVisible() const {
332 return (message_center_bubble() &&
333 message_center_bubble()->bubble()->IsVisible());
334 }
335
336 bool WebNotificationTray::IsMouseInNotificationBubble() const {
337 return false;
338 }
339
340 void WebNotificationTray::ShowMessageCenterBubble() {
341 if (!IsMessageCenterBubbleVisible())
342 message_center_tray_->ShowMessageCenterBubble();
343 }
344
345 void WebNotificationTray::UpdateAfterLoginStatusChange(
346 LoginStatus login_status) {
347 message_center()->SetLockedState(login_status == LoginStatus::LOCKED);
348 OnMessageCenterTrayChanged();
349 }
350
351 void WebNotificationTray::SetShelfAlignment(ShelfAlignment alignment) {
352 if (alignment == shelf_alignment())
353 return;
354 TrayBackgroundView::SetShelfAlignment(alignment);
355 tray_container()->SetBorder(views::Border::NullBorder());
356 // Destroy any existing bubble so that it will be rebuilt correctly.
357 message_center_tray_->HideMessageCenterBubble();
358 message_center_tray_->HidePopupBubble();
359 }
360
361 void WebNotificationTray::AnchorUpdated() {
362 if (message_center_bubble()) {
363 message_center_bubble()->bubble_view()->UpdateBubble();
364 UpdateBubbleViewArrow(message_center_bubble()->bubble_view());
365 }
366 }
367
368 base::string16 WebNotificationTray::GetAccessibleNameForTray() {
369 return l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_ACCESSIBLE_NAME);
370 }
371
372 void WebNotificationTray::HideBubbleWithView(
373 const views::TrayBubbleView* bubble_view) {
374 if (message_center_bubble() &&
375 bubble_view == message_center_bubble()->bubble_view()) {
376 message_center_tray_->HideMessageCenterBubble();
377 } else if (popup_collection_.get()) {
378 message_center_tray_->HidePopupBubble();
379 }
380 }
381
382 bool WebNotificationTray::PerformAction(const ui::Event& event) {
383 if (message_center_bubble())
384 message_center_tray_->HideMessageCenterBubble();
385 else
386 message_center_tray_->ShowMessageCenterBubble();
387 return true;
388 }
389
390 void WebNotificationTray::BubbleViewDestroyed() {
391 if (message_center_bubble())
392 message_center_bubble()->bubble()->BubbleViewDestroyed();
393 }
394
395 void WebNotificationTray::OnMouseEnteredView() {}
396
397 void WebNotificationTray::OnMouseExitedView() {}
398
399 base::string16 WebNotificationTray::GetAccessibleNameForBubble() {
400 return GetAccessibleNameForTray();
401 }
402
403 gfx::Rect WebNotificationTray::GetAnchorRect(
404 views::Widget* anchor_widget,
405 views::TrayBubbleView::AnchorType anchor_type,
406 views::TrayBubbleView::AnchorAlignment anchor_alignment) const {
407 return GetBubbleAnchorRect(anchor_widget, anchor_type, anchor_alignment);
408 }
409
410 void WebNotificationTray::OnBeforeBubbleWidgetInit(
411 views::Widget* anchor_widget,
412 views::Widget* bubble_widget,
413 views::Widget::InitParams* params) const {
414 // Place the bubble in the same root window as |anchor_widget|.
415 WmLookup::Get()
416 ->GetWindowForWidget(anchor_widget)
417 ->GetRootWindowController()
418 ->ConfigureWidgetInitParamsForContainer(
419 bubble_widget, kShellWindowId_SettingBubbleContainer, params);
420 }
421
422 void WebNotificationTray::HideBubble(const views::TrayBubbleView* bubble_view) {
423 HideBubbleWithView(bubble_view);
424 }
425
426 bool WebNotificationTray::ShowNotifierSettings() {
427 if (message_center_bubble()) {
428 static_cast<message_center::MessageCenterBubble*>(
429 message_center_bubble()->bubble())
430 ->SetSettingsVisible();
431 return true;
432 }
433 return ShowMessageCenterInternal(true /* show_settings */);
434 }
435
436 bool WebNotificationTray::IsContextMenuEnabled() const {
437 return IsLoggedIn();
438 }
439
440 message_center::MessageCenterTray* WebNotificationTray::GetMessageCenterTray() {
441 return message_center_tray_.get();
442 }
443
444 bool WebNotificationTray::IsCommandIdChecked(int command_id) const {
445 if (command_id != kToggleQuietMode)
446 return false;
447 return message_center()->IsQuietMode();
448 }
449
450 bool WebNotificationTray::IsCommandIdEnabled(int command_id) const {
451 return true;
452 }
453
454 bool WebNotificationTray::GetAcceleratorForCommandId(
455 int command_id,
456 ui::Accelerator* accelerator) {
457 return false;
458 }
459
460 void WebNotificationTray::ExecuteCommand(int command_id, int event_flags) {
461 if (command_id == kToggleQuietMode) {
462 bool in_quiet_mode = message_center()->IsQuietMode();
463 message_center()->SetQuietMode(!in_quiet_mode);
464 return;
465 }
466 base::TimeDelta expires_in = command_id == kEnableQuietModeDay
467 ? base::TimeDelta::FromDays(1)
468 : base::TimeDelta::FromHours(1);
469 message_center()->EnterQuietModeWithExpire(expires_in);
470 }
471
472 void WebNotificationTray::ButtonPressed(views::Button* sender,
473 const ui::Event& event) {
474 DCHECK_EQ(button_, sender);
475 PerformAction(event);
476 }
477
478 void WebNotificationTray::OnMessageCenterTrayChanged() {
479 // Do not update the tray contents directly. Multiple change events can happen
480 // consecutively, and calling Update in the middle of those events will show
481 // intermediate unread counts for a moment.
482 should_update_tray_content_ = true;
483 base::ThreadTaskRunnerHandle::Get()->PostTask(
484 FROM_HERE,
485 base::Bind(&WebNotificationTray::UpdateTrayContent, AsWeakPtr()));
486 }
487
488 void WebNotificationTray::UpdateTrayContent() {
489 if (!should_update_tray_content_)
490 return;
491 should_update_tray_content_ = false;
492
493 message_center::MessageCenter* message_center =
494 message_center_tray_->message_center();
495 button_->SetUnreadCount(message_center->UnreadNotificationCount());
496 if (IsMessageCenterBubbleVisible())
497 button_->SetState(views::CustomButton::STATE_PRESSED);
498 else
499 button_->SetState(views::CustomButton::STATE_NORMAL);
500
501 SetVisible(IsLoggedIn());
502 Layout();
503 SchedulePaint();
504 if (IsLoggedIn())
505 status_area_widget_->system_tray()->SetNextFocusableView(this);
506 }
507
508 void WebNotificationTray::ClickedOutsideBubble() {
509 // Only hide the message center
510 if (!message_center_bubble())
511 return;
512
513 message_center_tray_->HideMessageCenterBubble();
514 }
515
516 message_center::MessageCenter* WebNotificationTray::message_center() const {
517 return message_center_tray_->message_center();
518 }
519
520 bool WebNotificationTray::IsLoggedIn() const {
521 WmShell* shell = WmShell::Get();
522 // TODO(jamescook): Should this also check LoginState::LOCKED?
523 return shell->system_tray_delegate()->GetUserLoginStatus() !=
524 LoginStatus::NOT_LOGGED_IN &&
525 !shell->GetSessionStateDelegate()->IsInSecondaryLoginScreen();
526 }
527
528 // Methods for testing
529
530 bool WebNotificationTray::IsPopupVisible() const {
531 return message_center_tray_->popups_visible();
532 }
533
534 message_center::MessageCenterBubble*
535 WebNotificationTray::GetMessageCenterBubbleForTest() {
536 if (!message_center_bubble())
537 return NULL;
538 return static_cast<message_center::MessageCenterBubble*>(
539 message_center_bubble()->bubble());
540 }
541
542 } // namespace ash
OLDNEW
« no previous file with comments | « ash/system/web_notification/web_notification_tray.h ('k') | ash/system/web_notification/web_notification_tray_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698