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.
5 #include "ash/system/tray/system_tray.h"
7 #include "ash/ash_switches.h"
8 #include "ash/metrics/user_metrics_recorder.h"
9 #include "ash/shelf/shelf_layout_manager.h"
10 #include "ash/shell.h"
11 #include "ash/shell_window_ids.h"
12 #include "ash/system/audio/tray_audio.h"
13 #include "ash/system/bluetooth/tray_bluetooth.h"
14 #include "ash/system/cast/tray_cast.h"
15 #include "ash/system/date/tray_date.h"
16 #include "ash/system/status_area_widget.h"
17 #include "ash/system/tray/system_tray_delegate.h"
18 #include "ash/system/tray/system_tray_item.h"
19 #include "ash/system/tray/tray_bubble_wrapper.h"
20 #include "ash/system/tray/tray_constants.h"
21 #include "ash/system/tray_accessibility.h"
22 #include "ash/system/tray_update.h"
23 #include "ash/system/user/login_status.h"
24 #include "ash/system/user/tray_user.h"
25 #include "ash/system/user/tray_user_separator.h"
26 #include "ash/system/web_notification/web_notification_tray.h"
27 #include "base/logging.h"
28 #include "base/strings/utf_string_conversions.h"
29 #include "base/timer/timer.h"
30 #include "grit/ash_strings.h"
31 #include "ui/aura/window_event_dispatcher.h"
32 #include "ui/base/l10n/l10n_util.h"
33 #include "ui/compositor/layer.h"
34 #include "ui/events/event_constants.h"
35 #include "ui/gfx/canvas.h"
36 #include "ui/gfx/screen.h"
37 #include "ui/gfx/skia_util.h"
38 #include "ui/views/border.h"
39 #include "ui/views/controls/label.h"
40 #include "ui/views/layout/box_layout.h"
41 #include "ui/views/layout/fill_layout.h"
42 #include "ui/views/view.h"
44 #if defined(OS_CHROMEOS)
45 #include "ash/system/chromeos/audio/tray_audio_chromeos.h"
46 #include "ash/system/chromeos/brightness/tray_brightness.h"
47 #include "ash/system/chromeos/enterprise/tray_enterprise.h"
48 #include "ash/system/chromeos/network/tray_network.h"
49 #include "ash/system/chromeos/network/tray_sms.h"
50 #include "ash/system/chromeos/network/tray_vpn.h"
51 #include "ash/system/chromeos/power/power_status.h"
52 #include "ash/system/chromeos/power/tray_power.h"
53 #include "ash/system/chromeos/rotation/tray_rotation_lock.h"
54 #include "ash/system/chromeos/screen_security/screen_capture_tray_item.h"
55 #include "ash/system/chromeos/screen_security/screen_share_tray_item.h"
56 #include "ash/system/chromeos/session/tray_session_length_limit.h"
57 #include "ash/system/chromeos/settings/tray_settings.h"
58 #include "ash/system/chromeos/supervised/tray_supervised_user.h"
59 #include "ash/system/chromeos/tray_caps_lock.h"
60 #include "ash/system/chromeos/tray_display.h"
61 #include "ash/system/chromeos/tray_tracing.h"
62 #include "ash/system/ime/tray_ime_chromeos.h"
63 #include "ash/system/tray/media_security/multi_profile_media_tray_item.h"
64 #include "ui/message_center/message_center.h"
66 #include "ash/system/win/audio/tray_audio_win.h"
67 #include "media/audio/win/core_audio_util_win.h"
70 using views::TrayBubbleView
;
74 // The minimum width of the system tray menu width.
75 const int kMinimumSystemTrayMenuWidth
= 300;
77 // Class to initialize and manage the SystemTrayBubble and TrayBubbleWrapper
78 // instances for a bubble.
80 class SystemBubbleWrapper
{
82 // Takes ownership of |bubble|.
83 explicit SystemBubbleWrapper(SystemTrayBubble
* bubble
)
84 : bubble_(bubble
), is_persistent_(false) {}
86 // Initializes the bubble view and creates |bubble_wrapper_|.
87 void InitView(TrayBackgroundView
* tray
,
89 TrayBubbleView::InitParams
* init_params
,
92 user::LoginStatus login_status
=
93 Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus();
94 bubble_
->InitView(anchor
, login_status
, init_params
);
95 bubble_wrapper_
.reset(new TrayBubbleWrapper(tray
, bubble_
->bubble_view()));
96 // The system bubble should not have an arrow.
97 bubble_
->bubble_view()->SetArrowPaintType(
98 views::BubbleBorder::PAINT_NONE
);
99 is_persistent_
= is_persistent
;
101 // If ChromeVox is enabled, focus the default item if no item is focused.
102 if (Shell::GetInstance()->accessibility_delegate()->
103 IsSpokenFeedbackEnabled()) {
104 bubble_
->FocusDefaultIfNeeded();
108 // Convenience accessors:
109 SystemTrayBubble
* bubble() const { return bubble_
.get(); }
110 SystemTrayBubble::BubbleType
bubble_type() const {
111 return bubble_
->bubble_type();
113 TrayBubbleView
* bubble_view() const { return bubble_
->bubble_view(); }
114 bool is_persistent() const { return is_persistent_
; }
117 scoped_ptr
<SystemTrayBubble
> bubble_
;
118 scoped_ptr
<TrayBubbleWrapper
> bubble_wrapper_
;
121 DISALLOW_COPY_AND_ASSIGN(SystemBubbleWrapper
);
127 SystemTray::SystemTray(StatusAreaWidget
* status_area_widget
)
128 : TrayBackgroundView(status_area_widget
),
130 default_bubble_height_(0),
131 hide_notifications_(false),
132 full_system_tray_menu_(false),
133 tray_accessibility_(NULL
),
135 SetContentsBackground();
138 SystemTray::~SystemTray() {
139 // Destroy any child views that might have back pointers before ~View().
140 system_bubble_
.reset();
141 notification_bubble_
.reset();
142 for (std::vector
<SystemTrayItem
*>::iterator it
= items_
.begin();
145 (*it
)->DestroyTrayView();
149 void SystemTray::InitializeTrayItems(SystemTrayDelegate
* delegate
) {
150 TrayBackgroundView::Initialize();
151 CreateItems(delegate
);
154 void SystemTray::CreateItems(SystemTrayDelegate
* delegate
) {
156 // Create user items for each possible user.
157 ash::Shell
* shell
= ash::Shell::GetInstance();
158 int maximum_user_profiles
=
159 shell
->session_state_delegate()->GetMaximumNumberOfLoggedInUsers();
160 for (int i
= 0; i
< maximum_user_profiles
; i
++)
161 AddTrayItem(new TrayUser(this, i
));
163 if (maximum_user_profiles
> 1) {
164 // Add a special double line separator between users and the rest of the
165 // menu if more then one user is logged in.
166 AddTrayItem(new TrayUserSeparator(this));
170 tray_accessibility_
= new TrayAccessibility(this);
171 tray_date_
= new TrayDate(this);
173 #if defined(OS_CHROMEOS)
174 AddTrayItem(new TraySessionLengthLimit(this));
175 AddTrayItem(new TrayEnterprise(this));
176 AddTrayItem(new TraySupervisedUser(this));
177 AddTrayItem(new TrayIME(this));
178 AddTrayItem(tray_accessibility_
);
179 AddTrayItem(new TrayTracing(this));
180 AddTrayItem(new TrayPower(this, message_center::MessageCenter::Get()));
181 AddTrayItem(new TrayNetwork(this));
182 AddTrayItem(new TrayVPN(this));
183 AddTrayItem(new TraySms(this));
184 AddTrayItem(new TrayBluetooth(this));
185 tray_cast_
= new TrayCast(this);
186 AddTrayItem(tray_cast_
);
187 AddTrayItem(new TrayDisplay(this));
188 screen_capture_tray_item_
= new ScreenCaptureTrayItem(this);
189 AddTrayItem(screen_capture_tray_item_
);
190 screen_share_tray_item_
= new ScreenShareTrayItem(this);
191 AddTrayItem(screen_share_tray_item_
);
192 AddTrayItem(new MultiProfileMediaTrayItem(this));
193 AddTrayItem(new TrayAudioChromeOs(this));
194 AddTrayItem(new TrayBrightness(this));
195 AddTrayItem(new TrayCapsLock(this));
196 AddTrayItem(new TrayRotationLock(this));
197 AddTrayItem(new TraySettings(this));
198 AddTrayItem(new TrayUpdate(this));
199 AddTrayItem(tray_date_
);
200 #elif defined(OS_WIN)
201 AddTrayItem(tray_accessibility_
);
202 if (media::CoreAudioUtil::IsSupported())
203 AddTrayItem(new TrayAudioWin(this));
204 AddTrayItem(new TrayUpdate(this));
205 AddTrayItem(tray_date_
);
206 #elif defined(OS_LINUX)
207 AddTrayItem(tray_accessibility_
);
208 AddTrayItem(new TrayBluetooth(this));
209 AddTrayItem(new TrayUpdate(this));
210 AddTrayItem(tray_date_
);
213 SetVisible(ash::Shell::GetInstance()->system_tray_delegate()->
214 GetTrayVisibilityOnStartup());
217 void SystemTray::AddTrayItem(SystemTrayItem
* item
) {
218 items_
.push_back(item
);
220 SystemTrayDelegate
* delegate
= Shell::GetInstance()->system_tray_delegate();
221 views::View
* tray_item
= item
->CreateTrayView(delegate
->GetUserLoginStatus());
222 item
->UpdateAfterShelfAlignmentChange(shelf_alignment());
225 tray_container()->AddChildViewAt(tray_item
, 0);
226 PreferredSizeChanged();
227 tray_item_map_
[item
] = tray_item
;
231 void SystemTray::RemoveTrayItem(SystemTrayItem
* item
) {
235 const std::vector
<SystemTrayItem
*>& SystemTray::GetTrayItems() const {
239 void SystemTray::ShowDefaultView(BubbleCreationType creation_type
) {
240 ShowDefaultViewWithOffset(
242 TrayBubbleView::InitParams::kArrowDefaultOffset
,
246 void SystemTray::ShowPersistentDefaultView() {
247 ShowItems(items_
.get(),
251 TrayBubbleView::InitParams::kArrowDefaultOffset
,
255 void SystemTray::ShowDetailedView(SystemTrayItem
* item
,
258 BubbleCreationType creation_type
) {
259 std::vector
<SystemTrayItem
*> items
;
260 // The detailed view with timeout means a UI to show the current system state,
261 // like the audio level or brightness. Such UI should behave as persistent and
262 // keep its own logic for the appearance.
264 !activate
&& close_delay
> 0 && creation_type
== BUBBLE_CREATE_NEW
);
265 items
.push_back(item
);
267 items
, true, activate
, creation_type
, GetTrayXOffset(item
), persistent
);
269 system_bubble_
->bubble()->StartAutoCloseTimer(close_delay
);
272 void SystemTray::SetDetailedViewCloseDelay(int close_delay
) {
273 if (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DETAILED
))
274 system_bubble_
->bubble()->StartAutoCloseTimer(close_delay
);
277 void SystemTray::HideDetailedView(SystemTrayItem
* item
) {
278 if (item
!= detailed_item_
)
280 DestroySystemBubble();
281 UpdateNotificationBubble();
284 void SystemTray::ShowNotificationView(SystemTrayItem
* item
) {
285 if (std::find(notification_items_
.begin(), notification_items_
.end(), item
)
286 != notification_items_
.end())
288 notification_items_
.push_back(item
);
289 UpdateNotificationBubble();
292 void SystemTray::HideNotificationView(SystemTrayItem
* item
) {
293 std::vector
<SystemTrayItem
*>::iterator found_iter
=
294 std::find(notification_items_
.begin(), notification_items_
.end(), item
);
295 if (found_iter
== notification_items_
.end())
297 notification_items_
.erase(found_iter
);
298 // Only update the notification bubble if visible (i.e. don't create one).
299 if (notification_bubble_
)
300 UpdateNotificationBubble();
303 void SystemTray::UpdateAfterLoginStatusChange(user::LoginStatus login_status
) {
304 DestroySystemBubble();
305 UpdateNotificationBubble();
307 for (std::vector
<SystemTrayItem
*>::iterator it
= items_
.begin();
310 (*it
)->UpdateAfterLoginStatusChange(login_status
);
313 // Items default to SHELF_ALIGNMENT_BOTTOM. Update them if the initial
314 // position of the shelf differs.
315 if (shelf_alignment() != SHELF_ALIGNMENT_BOTTOM
)
316 UpdateAfterShelfAlignmentChange(shelf_alignment());
319 PreferredSizeChanged();
322 void SystemTray::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment
) {
323 for (std::vector
<SystemTrayItem
*>::iterator it
= items_
.begin();
326 (*it
)->UpdateAfterShelfAlignmentChange(alignment
);
330 void SystemTray::SetHideNotifications(bool hide_notifications
) {
331 if (notification_bubble_
)
332 notification_bubble_
->bubble()->SetVisible(!hide_notifications
);
333 hide_notifications_
= hide_notifications
;
336 bool SystemTray::ShouldShowShelf() const {
337 return system_bubble_
.get() && system_bubble_
->bubble()->ShouldShowShelf();
340 bool SystemTray::HasSystemBubble() const {
341 return system_bubble_
.get() != NULL
;
344 bool SystemTray::HasNotificationBubble() const {
345 return notification_bubble_
.get() != NULL
;
348 SystemTrayBubble
* SystemTray::GetSystemBubble() {
351 return system_bubble_
->bubble();
354 bool SystemTray::IsAnyBubbleVisible() const {
355 return ((system_bubble_
.get() &&
356 system_bubble_
->bubble()->IsVisible()) ||
357 (notification_bubble_
.get() &&
358 notification_bubble_
->bubble()->IsVisible()));
361 bool SystemTray::IsMouseInNotificationBubble() const {
362 if (!notification_bubble_
)
364 return notification_bubble_
->bubble_view()->GetBoundsInScreen().Contains(
365 Shell::GetScreen()->GetCursorScreenPoint());
368 bool SystemTray::CloseSystemBubble() const {
371 system_bubble_
->bubble()->Close();
375 views::View
* SystemTray::GetHelpButtonView() const {
376 return tray_date_
->GetHelpButtonView();
379 bool SystemTray::CloseNotificationBubbleForTest() const {
380 if (!notification_bubble_
)
382 notification_bubble_
->bubble()->Close();
388 bool SystemTray::HasSystemBubbleType(SystemTrayBubble::BubbleType type
) {
389 DCHECK(type
!= SystemTrayBubble::BUBBLE_TYPE_NOTIFICATION
);
390 return system_bubble_
.get() && system_bubble_
->bubble_type() == type
;
393 void SystemTray::DestroySystemBubble() {
394 CloseSystemBubbleAndDeactivateSystemTray();
395 detailed_item_
= NULL
;
396 UpdateWebNotifications();
399 void SystemTray::DestroyNotificationBubble() {
400 if (notification_bubble_
) {
401 notification_bubble_
.reset();
402 UpdateWebNotifications();
406 base::string16
SystemTray::GetAccessibleNameForTray() {
407 base::string16 time
= GetAccessibleTimeString(base::Time::Now());
408 base::string16 battery
= base::ASCIIToUTF16("");
409 #if defined(OS_CHROMEOS)
410 battery
= PowerStatus::Get()->GetAccessibleNameString(false);
412 return l10n_util::GetStringFUTF16(
413 IDS_ASH_STATUS_TRAY_ACCESSIBLE_DESCRIPTION
, time
, battery
);
416 int SystemTray::GetTrayXOffset(SystemTrayItem
* item
) const {
417 // Don't attempt to align the arrow if the shelf is on the left or right.
418 if (shelf_alignment() != SHELF_ALIGNMENT_BOTTOM
&&
419 shelf_alignment() != SHELF_ALIGNMENT_TOP
)
420 return TrayBubbleView::InitParams::kArrowDefaultOffset
;
422 std::map
<SystemTrayItem
*, views::View
*>::const_iterator it
=
423 tray_item_map_
.find(item
);
424 if (it
== tray_item_map_
.end())
425 return TrayBubbleView::InitParams::kArrowDefaultOffset
;
427 const views::View
* item_view
= it
->second
;
428 if (item_view
->bounds().IsEmpty()) {
429 // The bounds of item could be still empty if it does not have a visible
430 // tray view. In that case, use the default (minimum) offset.
431 return TrayBubbleView::InitParams::kArrowDefaultOffset
;
434 gfx::Point
point(item_view
->width() / 2, 0);
435 ConvertPointToWidget(item_view
, &point
);
439 void SystemTray::ShowDefaultViewWithOffset(BubbleCreationType creation_type
,
442 if (creation_type
!= BUBBLE_USE_EXISTING
) {
443 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
444 ash::UMA_STATUS_AREA_MENU_OPENED
);
446 ShowItems(items_
.get(), false, true, creation_type
, arrow_offset
, persistent
);
449 void SystemTray::ShowItems(const std::vector
<SystemTrayItem
*>& items
,
452 BubbleCreationType creation_type
,
455 // No system tray bubbles in kiosk mode.
456 if (Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus() ==
457 ash::user::LOGGED_IN_KIOSK_APP
) {
461 // Destroy any existing bubble and create a new one.
462 SystemTrayBubble::BubbleType bubble_type
= detailed
?
463 SystemTrayBubble::BUBBLE_TYPE_DETAILED
:
464 SystemTrayBubble::BUBBLE_TYPE_DEFAULT
;
466 // Destroy the notification bubble here so that it doesn't get rebuilt
467 // while we add items to the main bubble_ (e.g. in HideNotificationView).
468 notification_bubble_
.reset();
469 if (system_bubble_
.get() && creation_type
== BUBBLE_USE_EXISTING
) {
470 system_bubble_
->bubble()->UpdateView(items
, bubble_type
);
471 // If ChromeVox is enabled, focus the default item if no item is focused.
472 if (Shell::GetInstance()->accessibility_delegate()->
473 IsSpokenFeedbackEnabled()) {
474 system_bubble_
->bubble()->FocusDefaultIfNeeded();
477 // Remember if the menu is a single property (like e.g. volume) or the
478 // full tray menu. Note that in case of the |BUBBLE_USE_EXISTING| case
479 // above, |full_system_tray_menu_| does not get changed since the fact that
480 // the menu is full (or not) doesn't change even if a "single property"
481 // (like network) replaces most of the menu.
482 full_system_tray_menu_
= items
.size() > 1;
483 // The menu width is fixed, and it is a per language setting.
484 int menu_width
= std::max(kMinimumSystemTrayMenuWidth
,
485 Shell::GetInstance()->system_tray_delegate()->GetSystemTrayMenuWidth());
487 TrayBubbleView::InitParams
init_params(TrayBubbleView::ANCHOR_TYPE_TRAY
,
488 GetAnchorAlignment(),
491 init_params
.can_activate
= can_activate
;
492 init_params
.first_item_has_no_margin
= true;
494 // This is the case where a volume control or brightness control bubble
496 init_params
.max_height
= default_bubble_height_
;
497 init_params
.arrow_color
= kBackgroundColor
;
499 init_params
.arrow_color
= kHeaderBackgroundColor
;
501 init_params
.arrow_offset
= arrow_offset
;
502 if (bubble_type
== SystemTrayBubble::BUBBLE_TYPE_DEFAULT
)
503 init_params
.close_on_deactivate
= !persistent
;
504 // For Volume and Brightness we don't want to show an arrow when
505 // they are shown in a bubble by themselves.
506 init_params
.arrow_paint_type
= views::BubbleBorder::PAINT_NORMAL
;
507 if (items
.size() == 1 && items
[0]->ShouldHideArrow())
508 init_params
.arrow_paint_type
= views::BubbleBorder::PAINT_TRANSPARENT
;
509 SystemTrayBubble
* bubble
= new SystemTrayBubble(this, items
, bubble_type
);
510 system_bubble_
.reset(new SystemBubbleWrapper(bubble
));
511 system_bubble_
->InitView(this, tray_container(), &init_params
, persistent
);
513 // Save height of default view for creating detailed views directly.
515 default_bubble_height_
= system_bubble_
->bubble_view()->height();
517 if (detailed
&& items
.size() > 0)
518 detailed_item_
= items
[0];
520 detailed_item_
= NULL
;
522 UpdateNotificationBubble(); // State changed, re-create notifications.
523 if (!notification_bubble_
)
524 UpdateWebNotifications();
525 GetShelfLayoutManager()->UpdateAutoHideState();
527 // When we show the system menu in our alternate shelf layout, we need to
528 // tint the background.
529 if (full_system_tray_menu_
)
530 SetDrawBackgroundAsActive(true);
533 void SystemTray::UpdateNotificationBubble() {
534 // Only show the notification bubble if we have notifications.
535 if (notification_items_
.empty()) {
536 DestroyNotificationBubble();
539 // Destroy the existing bubble before constructing a new one.
540 notification_bubble_
.reset();
541 SystemTrayBubble
* notification_bubble
;
542 notification_bubble
= new SystemTrayBubble(
543 this, notification_items_
, SystemTrayBubble::BUBBLE_TYPE_NOTIFICATION
);
545 TrayBubbleView::AnchorType anchor_type
;
546 // Tray items might want to show notifications while we are creating and
547 // initializing the |system_bubble_| - but it might not be fully initialized
548 // when coming here - this would produce a crashed like crbug.com/247416.
549 // As such we check the existence of the widget here.
550 if (system_bubble_
.get() &&
551 system_bubble_
->bubble_view() &&
552 system_bubble_
->bubble_view()->GetWidget()) {
553 anchor
= system_bubble_
->bubble_view();
554 anchor_type
= TrayBubbleView::ANCHOR_TYPE_BUBBLE
;
556 anchor
= tray_container();
557 anchor_type
= TrayBubbleView::ANCHOR_TYPE_TRAY
;
559 TrayBubbleView::InitParams
init_params(anchor_type
,
560 GetAnchorAlignment(),
563 init_params
.first_item_has_no_margin
= true;
564 init_params
.arrow_color
= kBackgroundColor
;
565 init_params
.arrow_offset
= GetTrayXOffset(notification_items_
[0]);
566 notification_bubble_
.reset(new SystemBubbleWrapper(notification_bubble
));
567 notification_bubble_
->InitView(this, anchor
, &init_params
, false);
569 if (notification_bubble
->bubble_view()->child_count() == 0) {
570 // It is possible that none of the items generated actual notifications.
571 DestroyNotificationBubble();
574 if (hide_notifications_
)
575 notification_bubble
->SetVisible(false);
577 UpdateWebNotifications();
580 void SystemTray::UpdateWebNotifications() {
581 TrayBubbleView
* bubble_view
= NULL
;
582 if (notification_bubble_
)
583 bubble_view
= notification_bubble_
->bubble_view();
584 else if (system_bubble_
)
585 bubble_view
= system_bubble_
->bubble_view();
589 gfx::Rect work_area
= Shell::GetScreen()->GetDisplayNearestWindow(
590 bubble_view
->GetWidget()->GetNativeView()).work_area();
591 if (GetShelfLayoutManager()->GetAlignment() != SHELF_ALIGNMENT_TOP
) {
593 0, work_area
.height() - bubble_view
->GetBoundsInScreen().y());
596 0, bubble_view
->GetBoundsInScreen().bottom() - work_area
.y());
599 status_area_widget()->web_notification_tray()->SetSystemTrayHeight(height
);
602 base::string16
SystemTray::GetAccessibleTimeString(
603 const base::Time
& now
) const {
604 base::HourClockType hour_type
=
605 ash::Shell::GetInstance()->system_tray_delegate()->GetHourClockType();
606 return base::TimeFormatTimeOfDayWithHourClockType(
607 now
, hour_type
, base::kKeepAmPm
);
610 void SystemTray::SetShelfAlignment(ShelfAlignment alignment
) {
611 if (alignment
== shelf_alignment())
613 TrayBackgroundView::SetShelfAlignment(alignment
);
614 UpdateAfterShelfAlignmentChange(alignment
);
615 // Destroy any existing bubble so that it is rebuilt correctly.
616 CloseSystemBubbleAndDeactivateSystemTray();
617 // Rebuild any notification bubble.
618 if (notification_bubble_
) {
619 notification_bubble_
.reset();
620 UpdateNotificationBubble();
624 void SystemTray::AnchorUpdated() {
625 if (notification_bubble_
) {
626 notification_bubble_
->bubble_view()->UpdateBubble();
627 // Ensure that the notification buble is above the shelf/status area.
628 notification_bubble_
->bubble_view()->GetWidget()->StackAtTop();
629 UpdateBubbleViewArrow(notification_bubble_
->bubble_view());
631 if (system_bubble_
) {
632 system_bubble_
->bubble_view()->UpdateBubble();
633 UpdateBubbleViewArrow(system_bubble_
->bubble_view());
637 void SystemTray::BubbleResized(const TrayBubbleView
* bubble_view
) {
638 UpdateWebNotifications();
641 void SystemTray::HideBubbleWithView(const TrayBubbleView
* bubble_view
) {
642 if (system_bubble_
.get() && bubble_view
== system_bubble_
->bubble_view()) {
643 DestroySystemBubble();
644 UpdateNotificationBubble(); // State changed, re-create notifications.
645 GetShelfLayoutManager()->UpdateAutoHideState();
646 } else if (notification_bubble_
.get() &&
647 bubble_view
== notification_bubble_
->bubble_view()) {
648 DestroyNotificationBubble();
652 bool SystemTray::ClickedOutsideBubble() {
653 if (!system_bubble_
|| system_bubble_
->is_persistent())
655 HideBubbleWithView(system_bubble_
->bubble_view());
659 void SystemTray::BubbleViewDestroyed() {
660 if (system_bubble_
) {
661 system_bubble_
->bubble()->DestroyItemViews();
662 system_bubble_
->bubble()->BubbleViewDestroyed();
666 void SystemTray::OnMouseEnteredView() {
668 system_bubble_
->bubble()->StopAutoCloseTimer();
671 void SystemTray::OnMouseExitedView() {
673 system_bubble_
->bubble()->RestartAutoCloseTimer();
676 base::string16
SystemTray::GetAccessibleNameForBubble() {
677 return GetAccessibleNameForTray();
680 gfx::Rect
SystemTray::GetAnchorRect(
681 views::Widget
* anchor_widget
,
682 TrayBubbleView::AnchorType anchor_type
,
683 TrayBubbleView::AnchorAlignment anchor_alignment
) const {
684 return GetBubbleAnchorRect(anchor_widget
, anchor_type
, anchor_alignment
);
687 void SystemTray::HideBubble(const TrayBubbleView
* bubble_view
) {
688 HideBubbleWithView(bubble_view
);
691 views::View
* SystemTray::GetTrayItemViewForTest(SystemTrayItem
* item
) {
692 std::map
<SystemTrayItem
*, views::View
*>::iterator it
=
693 tray_item_map_
.find(item
);
694 return it
== tray_item_map_
.end() ? NULL
: it
->second
;
697 TrayCast
* SystemTray::GetTrayCastForTesting() const { return tray_cast_
; }
699 TrayDate
* SystemTray::GetTrayDateForTesting() const { return tray_date_
; }
701 bool SystemTray::PerformAction(const ui::Event
& event
) {
702 // If we're already showing the default view, hide it; otherwise, show it
703 // (and hide any popup that's currently shown).
704 if (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DEFAULT
)) {
705 system_bubble_
->bubble()->Close();
707 int arrow_offset
= TrayBubbleView::InitParams::kArrowDefaultOffset
;
708 if (event
.IsMouseEvent() || event
.type() == ui::ET_GESTURE_TAP
) {
709 const ui::LocatedEvent
& located_event
=
710 static_cast<const ui::LocatedEvent
&>(event
);
711 if (shelf_alignment() == SHELF_ALIGNMENT_BOTTOM
||
712 shelf_alignment() == SHELF_ALIGNMENT_TOP
) {
713 gfx::Point
point(located_event
.x(), 0);
714 ConvertPointToWidget(this, &point
);
715 arrow_offset
= point
.x();
718 ShowDefaultViewWithOffset(BUBBLE_CREATE_NEW
, arrow_offset
, false);
723 void SystemTray::CloseSystemBubbleAndDeactivateSystemTray() {
724 system_bubble_
.reset();
725 // When closing a system bubble with the alternate shelf layout, we need to
726 // turn off the active tinting of the shelf.
727 if (full_system_tray_menu_
) {
728 SetDrawBackgroundAsActive(false);
729 full_system_tray_menu_
= false;