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/shelf/shelf_layout_manager.h"
10 #include "ash/shell/panel_window.h"
11 #include "ash/shell_window_ids.h"
12 #include "ash/system/bluetooth/tray_bluetooth.h"
13 #include "ash/system/brightness/tray_brightness.h"
14 #include "ash/system/chromeos/tray_tracing.h"
15 #include "ash/system/date/tray_date.h"
16 #include "ash/system/drive/tray_drive.h"
17 #include "ash/system/ime/tray_ime.h"
18 #include "ash/system/logout_button/tray_logout_button.h"
19 #include "ash/system/monitor/tray_monitor.h"
20 #include "ash/system/session_length_limit/tray_session_length_limit.h"
21 #include "ash/system/status_area_widget.h"
22 #include "ash/system/tray/system_tray_delegate.h"
23 #include "ash/system/tray/system_tray_item.h"
24 #include "ash/system/tray/tray_bubble_wrapper.h"
25 #include "ash/system/tray/tray_constants.h"
26 #include "ash/system/tray_accessibility.h"
27 #include "ash/system/tray_caps_lock.h"
28 #include "ash/system/tray_update.h"
29 #include "ash/system/user/login_status.h"
30 #include "ash/system/user/tray_user.h"
31 #include "ash/system/web_notification/web_notification_tray.h"
32 #include "base/command_line.h"
33 #include "base/logging.h"
34 #include "base/strings/utf_string_conversions.h"
35 #include "base/timer/timer.h"
36 #include "grit/ash_strings.h"
37 #include "ui/aura/root_window.h"
38 #include "ui/base/events/event_constants.h"
39 #include "ui/base/l10n/l10n_util.h"
40 #include "ui/compositor/layer.h"
41 #include "ui/gfx/canvas.h"
42 #include "ui/gfx/screen.h"
43 #include "ui/gfx/skia_util.h"
44 #include "ui/views/border.h"
45 #include "ui/views/controls/label.h"
46 #include "ui/views/layout/box_layout.h"
47 #include "ui/views/layout/fill_layout.h"
48 #include "ui/views/view.h"
50 #if defined(OS_CHROMEOS)
51 #include "ash/system/chromeos/audio/tray_audio.h"
52 #include "ash/system/chromeos/enterprise/tray_enterprise.h"
53 #include "ash/system/chromeos/managed/tray_locally_managed_user.h"
54 #include "ash/system/chromeos/network/tray_network.h"
55 #include "ash/system/chromeos/network/tray_sms.h"
56 #include "ash/system/chromeos/network/tray_vpn.h"
57 #include "ash/system/chromeos/power/tray_power.h"
58 #include "ash/system/chromeos/screen_security/screen_capture_tray_item.h"
59 #include "ash/system/chromeos/screen_security/screen_share_tray_item.h"
60 #include "ash/system/chromeos/settings/tray_settings.h"
61 #include "ash/system/chromeos/tray_display.h"
62 #include "ui/message_center/message_center.h"
65 using views::TrayBubbleView
;
69 // The minimum width of the system tray menu width.
70 const int kMinimumSystemTrayMenuWidth
= 300;
74 // Class to initialize and manage the SystemTrayBubble and TrayBubbleWrapper
75 // instances for a bubble.
77 class SystemBubbleWrapper
{
79 // Takes ownership of |bubble|.
80 explicit SystemBubbleWrapper(internal::SystemTrayBubble
* bubble
)
84 // Initializes the bubble view and creates |bubble_wrapper_|.
85 void InitView(TrayBackgroundView
* tray
,
87 TrayBubbleView::InitParams
* init_params
) {
89 user::LoginStatus login_status
=
90 Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus();
91 bubble_
->InitView(anchor
, login_status
, init_params
);
92 bubble_wrapper_
.reset(
93 new internal::TrayBubbleWrapper(tray
, bubble_
->bubble_view()));
94 if (ash::switches::UseAlternateShelfLayout()) {
95 // The system bubble should not have an arrow.
96 bubble_
->bubble_view()->SetArrowPaintType(
97 views::BubbleBorder::PAINT_NONE
);
101 // Convenience accessors:
102 SystemTrayBubble
* bubble() const { return bubble_
.get(); }
103 SystemTrayBubble::BubbleType
bubble_type() const {
104 return bubble_
->bubble_type();
106 TrayBubbleView
* bubble_view() const { return bubble_
->bubble_view(); }
109 scoped_ptr
<internal::SystemTrayBubble
> bubble_
;
110 scoped_ptr
<internal::TrayBubbleWrapper
> bubble_wrapper_
;
112 DISALLOW_COPY_AND_ASSIGN(SystemBubbleWrapper
);
115 } // namespace internal
119 using internal::SystemTrayBubble
;
121 SystemTray::SystemTray(internal::StatusAreaWidget
* status_area_widget
)
122 : internal::TrayBackgroundView(status_area_widget
),
124 default_bubble_height_(0),
125 hide_notifications_(false),
126 full_system_tray_menu_(false),
127 tray_accessibility_(NULL
) {
128 SetContentsBackground();
131 SystemTray::~SystemTray() {
132 // Destroy any child views that might have back pointers before ~View().
133 system_bubble_
.reset();
134 notification_bubble_
.reset();
135 for (std::vector
<SystemTrayItem
*>::iterator it
= items_
.begin();
138 (*it
)->DestroyTrayView();
142 void SystemTray::InitializeTrayItems(SystemTrayDelegate
* delegate
) {
143 internal::TrayBackgroundView::Initialize();
144 CreateItems(delegate
);
147 void SystemTray::CreateItems(SystemTrayDelegate
* delegate
) {
149 AddTrayItem(new internal::TraySessionLengthLimit(this));
150 AddTrayItem(new internal::TrayLogoutButton(this));
151 // In multi-profile user mode we can have multiple user tiles.
152 ash::Shell
* shell
= ash::Shell::GetInstance();
153 int maximum_user_profiles
=
154 shell
->delegate()->IsMultiProfilesEnabled() ?
155 shell
->session_state_delegate()->GetMaximumNumberOfLoggedInUsers() :
157 // Note: We purposely use one more item then logged in users to account for
158 // the additional separator.
159 for (int i
= 0; i
<= maximum_user_profiles
; i
++)
160 AddTrayItem(new internal::TrayUser(this, i
));
163 #if defined(OS_CHROMEOS)
164 AddTrayItem(new internal::TrayEnterprise(this));
165 AddTrayItem(new internal::TrayLocallyManagedUser(this));
167 AddTrayItem(new internal::TrayIME(this));
168 tray_accessibility_
= new internal::TrayAccessibility(this);
169 AddTrayItem(tray_accessibility_
);
170 #if defined(OS_CHROMEOS)
171 AddTrayItem(new internal::TrayTracing(this));
173 new internal::TrayPower(this, message_center::MessageCenter::Get()));
175 #if defined(OS_CHROMEOS)
176 AddTrayItem(new internal::TrayNetwork(this));
177 AddTrayItem(new internal::TrayVPN(this));
178 AddTrayItem(new internal::TraySms(this));
181 AddTrayItem(new internal::TrayBluetooth(this));
183 AddTrayItem(new internal::TrayDrive(this));
184 #if defined(OS_CHROMEOS)
185 AddTrayItem(new internal::TrayDisplay(this));
186 AddTrayItem(new internal::ScreenCaptureTrayItem(this));
187 AddTrayItem(new internal::ScreenShareTrayItem(this));
188 AddTrayItem(new internal::TrayAudio(this));
191 AddTrayItem(new internal::TrayBrightness(this));
192 AddTrayItem(new internal::TrayCapsLock(this));
194 #if defined(OS_CHROMEOS)
195 AddTrayItem(new internal::TraySettings(this));
197 AddTrayItem(new internal::TrayUpdate(this));
198 AddTrayItem(new internal::TrayDate(this));
200 #if defined(OS_LINUX)
201 // Add memory monitor if enabled.
202 CommandLine
* cmd
= CommandLine::ForCurrentProcess();
203 if (cmd
->HasSwitch(ash::switches::kAshEnableMemoryMonitor
))
204 AddTrayItem(new internal::TrayMonitor(this));
207 SetVisible(ash::Shell::GetInstance()->system_tray_delegate()->
208 GetTrayVisibilityOnStartup());
211 void SystemTray::AddTrayItem(SystemTrayItem
* item
) {
212 items_
.push_back(item
);
214 SystemTrayDelegate
* delegate
= Shell::GetInstance()->system_tray_delegate();
215 views::View
* tray_item
= item
->CreateTrayView(delegate
->GetUserLoginStatus());
216 item
->UpdateAfterShelfAlignmentChange(shelf_alignment());
219 tray_container()->AddChildViewAt(tray_item
, 0);
220 PreferredSizeChanged();
221 tray_item_map_
[item
] = tray_item
;
225 void SystemTray::RemoveTrayItem(SystemTrayItem
* item
) {
229 const std::vector
<SystemTrayItem
*>& SystemTray::GetTrayItems() const {
233 void SystemTray::ShowDefaultView(BubbleCreationType creation_type
) {
234 ShowDefaultViewWithOffset(creation_type
,
235 TrayBubbleView::InitParams::kArrowDefaultOffset
);
238 void SystemTray::ShowDetailedView(SystemTrayItem
* item
,
241 BubbleCreationType creation_type
) {
242 std::vector
<SystemTrayItem
*> items
;
243 items
.push_back(item
);
244 ShowItems(items
, true, activate
, creation_type
, GetTrayXOffset(item
));
246 system_bubble_
->bubble()->StartAutoCloseTimer(close_delay
);
249 void SystemTray::SetDetailedViewCloseDelay(int close_delay
) {
250 if (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DETAILED
))
251 system_bubble_
->bubble()->StartAutoCloseTimer(close_delay
);
254 void SystemTray::HideDetailedView(SystemTrayItem
* item
) {
255 if (item
!= detailed_item_
)
257 DestroySystemBubble();
258 UpdateNotificationBubble();
261 void SystemTray::ShowNotificationView(SystemTrayItem
* item
) {
262 if (std::find(notification_items_
.begin(), notification_items_
.end(), item
)
263 != notification_items_
.end())
265 notification_items_
.push_back(item
);
266 UpdateNotificationBubble();
269 void SystemTray::HideNotificationView(SystemTrayItem
* item
) {
270 std::vector
<SystemTrayItem
*>::iterator found_iter
=
271 std::find(notification_items_
.begin(), notification_items_
.end(), item
);
272 if (found_iter
== notification_items_
.end())
274 notification_items_
.erase(found_iter
);
275 // Only update the notification bubble if visible (i.e. don't create one).
276 if (notification_bubble_
)
277 UpdateNotificationBubble();
280 void SystemTray::UpdateAfterLoginStatusChange(user::LoginStatus login_status
) {
281 DestroySystemBubble();
282 UpdateNotificationBubble();
284 for (std::vector
<SystemTrayItem
*>::iterator it
= items_
.begin();
287 (*it
)->UpdateAfterLoginStatusChange(login_status
);
290 // Items default to SHELF_ALIGNMENT_BOTTOM. Update them if the initial
291 // position of the shelf differs.
292 if (shelf_alignment() != SHELF_ALIGNMENT_BOTTOM
)
293 UpdateAfterShelfAlignmentChange(shelf_alignment());
296 PreferredSizeChanged();
299 void SystemTray::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment
) {
300 for (std::vector
<SystemTrayItem
*>::iterator it
= items_
.begin();
303 (*it
)->UpdateAfterShelfAlignmentChange(alignment
);
307 void SystemTray::SetHideNotifications(bool hide_notifications
) {
308 if (notification_bubble_
)
309 notification_bubble_
->bubble()->SetVisible(!hide_notifications
);
310 hide_notifications_
= hide_notifications
;
313 bool SystemTray::ShouldShowLauncher() const {
314 return system_bubble_
.get() && system_bubble_
->bubble()->ShouldShowLauncher();
317 bool SystemTray::HasSystemBubble() const {
318 return system_bubble_
.get() != NULL
;
321 bool SystemTray::HasNotificationBubble() const {
322 return notification_bubble_
.get() != NULL
;
325 bool SystemTray::IsPressed() {
326 // Only when a full system tray bubble gets shown true will be returned.
327 // Small bubbles (like audio modifications via keyboard) should return false.
328 // Since showing the e.g. network portion of the system tray menu will convert
329 // the |system_bubble_| from type |BUBBLE_TYPE_DEFAULT| into
330 // |BUBBLE_TYPE_DETAILED| the full tray cannot reliably be checked trhough the
331 // type. As such |full_system_tray_menu_| gets checked here.
332 return HasSystemBubble() && full_system_tray_menu_
;
335 internal::SystemTrayBubble
* SystemTray::GetSystemBubble() {
338 return system_bubble_
->bubble();
341 bool SystemTray::IsAnyBubbleVisible() const {
342 return ((system_bubble_
.get() &&
343 system_bubble_
->bubble()->IsVisible()) ||
344 (notification_bubble_
.get() &&
345 notification_bubble_
->bubble()->IsVisible()));
348 bool SystemTray::IsMouseInNotificationBubble() const {
349 if (!notification_bubble_
)
351 return notification_bubble_
->bubble_view()->GetBoundsInScreen().Contains(
352 Shell::GetScreen()->GetCursorScreenPoint());
355 bool SystemTray::CloseSystemBubble() const {
358 system_bubble_
->bubble()->Close();
362 bool SystemTray::CloseNotificationBubbleForTest() const {
363 if (!notification_bubble_
)
365 notification_bubble_
->bubble()->Close();
371 bool SystemTray::HasSystemBubbleType(SystemTrayBubble::BubbleType type
) {
372 DCHECK(type
!= SystemTrayBubble::BUBBLE_TYPE_NOTIFICATION
);
373 return system_bubble_
.get() && system_bubble_
->bubble_type() == type
;
376 void SystemTray::DestroySystemBubble() {
377 system_bubble_
.reset();
378 detailed_item_
= NULL
;
379 UpdateWebNotifications();
382 void SystemTray::DestroyNotificationBubble() {
383 if (notification_bubble_
) {
384 notification_bubble_
.reset();
385 UpdateWebNotifications();
389 int SystemTray::GetTrayXOffset(SystemTrayItem
* item
) const {
390 // Don't attempt to align the arrow if the shelf is on the left or right.
391 if (shelf_alignment() != SHELF_ALIGNMENT_BOTTOM
&&
392 shelf_alignment() != SHELF_ALIGNMENT_TOP
)
393 return TrayBubbleView::InitParams::kArrowDefaultOffset
;
395 std::map
<SystemTrayItem
*, views::View
*>::const_iterator it
=
396 tray_item_map_
.find(item
);
397 if (it
== tray_item_map_
.end())
398 return TrayBubbleView::InitParams::kArrowDefaultOffset
;
400 const views::View
* item_view
= it
->second
;
401 if (item_view
->bounds().IsEmpty()) {
402 // The bounds of item could be still empty if it does not have a visible
403 // tray view. In that case, use the default (minimum) offset.
404 return TrayBubbleView::InitParams::kArrowDefaultOffset
;
407 gfx::Point
point(item_view
->width() / 2, 0);
408 ConvertPointToWidget(item_view
, &point
);
412 void SystemTray::ShowDefaultViewWithOffset(BubbleCreationType creation_type
,
414 ShowItems(items_
.get(), false, true, creation_type
, arrow_offset
);
417 void SystemTray::ShowItems(const std::vector
<SystemTrayItem
*>& items
,
420 BubbleCreationType creation_type
,
422 // No system tray bubbles in kiosk mode.
423 if (Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus() ==
424 ash::user::LOGGED_IN_KIOSK_APP
) {
428 // Destroy any existing bubble and create a new one.
429 SystemTrayBubble::BubbleType bubble_type
= detailed
?
430 SystemTrayBubble::BUBBLE_TYPE_DETAILED
:
431 SystemTrayBubble::BUBBLE_TYPE_DEFAULT
;
433 // Destroy the notification bubble here so that it doesn't get rebuilt
434 // while we add items to the main bubble_ (e.g. in HideNotificationView).
435 notification_bubble_
.reset();
436 if (system_bubble_
.get() && creation_type
== BUBBLE_USE_EXISTING
) {
437 system_bubble_
->bubble()->UpdateView(items
, bubble_type
);
439 // Remember if the menu is a single property (like e.g. volume) or the
440 // full tray menu. Note that in case of the |BUBBLE_USE_EXISTING| case
441 // above, |full_system_tray_menu_| does not get changed since the fact that
442 // the menu is full (or not) doesn't change even if a "single property"
443 // (like network) replaces most of the menu.
444 full_system_tray_menu_
= items
.size() > 1;
445 // The menu width is fixed, and it is a per language setting.
446 int menu_width
= std::max(kMinimumSystemTrayMenuWidth
,
447 Shell::GetInstance()->system_tray_delegate()->GetSystemTrayMenuWidth());
449 TrayBubbleView::InitParams
init_params(TrayBubbleView::ANCHOR_TYPE_TRAY
,
450 GetAnchorAlignment(),
453 init_params
.can_activate
= can_activate
;
454 init_params
.first_item_has_no_margin
=
455 ash::switches::UseAlternateShelfLayout();
457 // This is the case where a volume control or brightness control bubble
459 init_params
.max_height
= default_bubble_height_
;
460 init_params
.arrow_color
= kBackgroundColor
;
462 init_params
.arrow_color
= kHeaderBackgroundColor
;
464 init_params
.arrow_offset
= arrow_offset
;
465 // For Volume and Brightness we don't want to show an arrow when
466 // they are shown in a bubble by themselves.
467 init_params
.arrow_paint_type
= views::BubbleBorder::PAINT_NORMAL
;
468 if (items
.size() == 1 && items
[0]->ShouldHideArrow())
469 init_params
.arrow_paint_type
= views::BubbleBorder::PAINT_TRANSPARENT
;
470 SystemTrayBubble
* bubble
= new SystemTrayBubble(this, items
, bubble_type
);
471 system_bubble_
.reset(new internal::SystemBubbleWrapper(bubble
));
472 system_bubble_
->InitView(this, tray_container(), &init_params
);
474 // Save height of default view for creating detailed views directly.
476 default_bubble_height_
= system_bubble_
->bubble_view()->height();
478 if (detailed
&& items
.size() > 0)
479 detailed_item_
= items
[0];
481 detailed_item_
= NULL
;
483 UpdateNotificationBubble(); // State changed, re-create notifications.
484 if (!notification_bubble_
)
485 UpdateWebNotifications();
486 GetShelfLayoutManager()->UpdateAutoHideState();
489 void SystemTray::UpdateNotificationBubble() {
490 // Only show the notification bubble if we have notifications.
491 if (notification_items_
.empty()) {
492 DestroyNotificationBubble();
495 // Destroy the existing bubble before constructing a new one.
496 notification_bubble_
.reset();
497 SystemTrayBubble
* notification_bubble
;
498 notification_bubble
= new SystemTrayBubble(
499 this, notification_items_
, SystemTrayBubble::BUBBLE_TYPE_NOTIFICATION
);
501 TrayBubbleView::AnchorType anchor_type
;
502 // Tray items might want to show notifications while we are creating and
503 // initializing the |system_bubble_| - but it might not be fully initialized
504 // when coming here - this would produce a crashed like crbug.com/247416.
505 // As such we check the existence of the widget here.
506 if (system_bubble_
.get() &&
507 system_bubble_
->bubble_view() &&
508 system_bubble_
->bubble_view()->GetWidget()) {
509 anchor
= system_bubble_
->bubble_view();
510 anchor_type
= TrayBubbleView::ANCHOR_TYPE_BUBBLE
;
512 anchor
= tray_container();
513 anchor_type
= TrayBubbleView::ANCHOR_TYPE_TRAY
;
515 TrayBubbleView::InitParams
init_params(anchor_type
,
516 GetAnchorAlignment(),
519 init_params
.first_item_has_no_margin
=
520 ash::switches::UseAlternateShelfLayout();
521 init_params
.arrow_color
= kBackgroundColor
;
522 init_params
.arrow_offset
= GetTrayXOffset(notification_items_
[0]);
523 notification_bubble_
.reset(
524 new internal::SystemBubbleWrapper(notification_bubble
));
525 notification_bubble_
->InitView(this, anchor
, &init_params
);
527 if (notification_bubble
->bubble_view()->child_count() == 0) {
528 // It is possible that none of the items generated actual notifications.
529 DestroyNotificationBubble();
532 if (hide_notifications_
)
533 notification_bubble
->SetVisible(false);
535 UpdateWebNotifications();
538 void SystemTray::UpdateWebNotifications() {
539 TrayBubbleView
* bubble_view
= NULL
;
540 if (notification_bubble_
)
541 bubble_view
= notification_bubble_
->bubble_view();
542 else if (system_bubble_
)
543 bubble_view
= system_bubble_
->bubble_view();
547 gfx::Rect work_area
= Shell::GetScreen()->GetDisplayNearestWindow(
548 bubble_view
->GetWidget()->GetNativeView()).work_area();
549 if (GetShelfLayoutManager()->GetAlignment() != SHELF_ALIGNMENT_TOP
) {
551 0, work_area
.height() - bubble_view
->GetBoundsInScreen().y());
554 0, bubble_view
->GetBoundsInScreen().bottom() - work_area
.y());
557 status_area_widget()->web_notification_tray()->SetSystemTrayHeight(height
);
560 void SystemTray::SetShelfAlignment(ShelfAlignment alignment
) {
561 if (alignment
== shelf_alignment())
563 internal::TrayBackgroundView::SetShelfAlignment(alignment
);
564 UpdateAfterShelfAlignmentChange(alignment
);
565 // Destroy any existing bubble so that it is rebuilt correctly.
566 system_bubble_
.reset();
567 // Rebuild any notification bubble.
568 if (notification_bubble_
) {
569 notification_bubble_
.reset();
570 UpdateNotificationBubble();
574 void SystemTray::AnchorUpdated() {
575 if (notification_bubble_
) {
576 notification_bubble_
->bubble_view()->UpdateBubble();
577 // Ensure that the notification buble is above the launcher/status area.
578 notification_bubble_
->bubble_view()->GetWidget()->StackAtTop();
579 UpdateBubbleViewArrow(notification_bubble_
->bubble_view());
581 if (system_bubble_
) {
582 system_bubble_
->bubble_view()->UpdateBubble();
583 UpdateBubbleViewArrow(system_bubble_
->bubble_view());
587 base::string16
SystemTray::GetAccessibleNameForTray() {
588 return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBLE_NAME
);
591 void SystemTray::HideBubbleWithView(const TrayBubbleView
* bubble_view
) {
592 if (system_bubble_
.get() && bubble_view
== system_bubble_
->bubble_view()) {
593 DestroySystemBubble();
594 UpdateNotificationBubble(); // State changed, re-create notifications.
595 GetShelfLayoutManager()->UpdateAutoHideState();
596 } else if (notification_bubble_
.get() &&
597 bubble_view
== notification_bubble_
->bubble_view()) {
598 DestroyNotificationBubble();
602 bool SystemTray::ClickedOutsideBubble() {
605 HideBubbleWithView(system_bubble_
->bubble_view());
609 void SystemTray::BubbleViewDestroyed() {
610 if (system_bubble_
) {
611 system_bubble_
->bubble()->DestroyItemViews();
612 system_bubble_
->bubble()->BubbleViewDestroyed();
616 void SystemTray::OnMouseEnteredView() {
618 system_bubble_
->bubble()->StopAutoCloseTimer();
621 void SystemTray::OnMouseExitedView() {
623 system_bubble_
->bubble()->RestartAutoCloseTimer();
626 base::string16
SystemTray::GetAccessibleNameForBubble() {
627 return GetAccessibleNameForTray();
630 gfx::Rect
SystemTray::GetAnchorRect(
631 views::Widget
* anchor_widget
,
632 TrayBubbleView::AnchorType anchor_type
,
633 TrayBubbleView::AnchorAlignment anchor_alignment
) {
634 return GetBubbleAnchorRect(anchor_widget
, anchor_type
, anchor_alignment
);
637 void SystemTray::HideBubble(const TrayBubbleView
* bubble_view
) {
638 HideBubbleWithView(bubble_view
);
641 bool SystemTray::PerformAction(const ui::Event
& event
) {
642 // If we're already showing the default view, hide it; otherwise, show it
643 // (and hide any popup that's currently shown).
644 if (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DEFAULT
)) {
645 system_bubble_
->bubble()->Close();
647 int arrow_offset
= TrayBubbleView::InitParams::kArrowDefaultOffset
;
648 if (event
.IsMouseEvent() || event
.type() == ui::ET_GESTURE_TAP
) {
649 const ui::LocatedEvent
& located_event
=
650 static_cast<const ui::LocatedEvent
&>(event
);
651 if (shelf_alignment() == SHELF_ALIGNMENT_BOTTOM
||
652 shelf_alignment() == SHELF_ALIGNMENT_TOP
) {
653 gfx::Point
point(located_event
.x(), 0);
654 ConvertPointToWidget(this, &point
);
655 arrow_offset
= point
.x();
658 ShowDefaultViewWithOffset(BUBBLE_CREATE_NEW
, arrow_offset
);