// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ash/system/tray/system_tray.h" #include "ash/ash_switches.h" #include "ash/metrics/user_metrics_recorder.h" #include "ash/shelf/shelf_layout_manager.h" #include "ash/shell.h" #include "ash/shell/panel_window.h" #include "ash/shell_window_ids.h" #include "ash/system/bluetooth/tray_bluetooth.h" #include "ash/system/date/tray_date.h" #include "ash/system/drive/tray_drive.h" #include "ash/system/ime/tray_ime.h" #include "ash/system/monitor/tray_monitor.h" #include "ash/system/session_length_limit/tray_session_length_limit.h" #include "ash/system/status_area_widget.h" #include "ash/system/tray/system_tray_delegate.h" #include "ash/system/tray/system_tray_item.h" #include "ash/system/tray/tray_bubble_wrapper.h" #include "ash/system/tray/tray_constants.h" #include "ash/system/tray_accessibility.h" #include "ash/system/tray_caps_lock.h" #include "ash/system/tray_update.h" #include "ash/system/user/login_status.h" #include "ash/system/user/tray_user.h" #include "ash/system/user/tray_user_separator.h" #include "ash/system/web_notification/web_notification_tray.h" #include "base/command_line.h" #include "base/logging.h" #include "base/strings/utf_string_conversions.h" #include "base/timer/timer.h" #include "grit/ash_strings.h" #include "ui/aura/root_window.h" #include "ui/base/l10n/l10n_util.h" #include "ui/compositor/layer.h" #include "ui/events/event_constants.h" #include "ui/gfx/canvas.h" #include "ui/gfx/screen.h" #include "ui/gfx/skia_util.h" #include "ui/views/border.h" #include "ui/views/controls/label.h" #include "ui/views/layout/box_layout.h" #include "ui/views/layout/fill_layout.h" #include "ui/views/view.h" #if defined(OS_CHROMEOS) #include "ash/system/chromeos/audio/tray_audio.h" #include "ash/system/chromeos/brightness/tray_brightness.h" #include "ash/system/chromeos/enterprise/tray_enterprise.h" #include "ash/system/chromeos/managed/tray_locally_managed_user.h" #include "ash/system/chromeos/network/tray_network.h" #include "ash/system/chromeos/network/tray_sms.h" #include "ash/system/chromeos/network/tray_vpn.h" #include "ash/system/chromeos/power/tray_power.h" #include "ash/system/chromeos/screen_security/screen_capture_tray_item.h" #include "ash/system/chromeos/screen_security/screen_share_tray_item.h" #include "ash/system/chromeos/settings/tray_settings.h" #include "ash/system/chromeos/tray_display.h" #include "ash/system/chromeos/tray_tracing.h" #include "ui/message_center/message_center.h" #endif using views::TrayBubbleView; namespace ash { // The minimum width of the system tray menu width. const int kMinimumSystemTrayMenuWidth = 300; namespace internal { // Class to initialize and manage the SystemTrayBubble and TrayBubbleWrapper // instances for a bubble. class SystemBubbleWrapper { public: // Takes ownership of |bubble|. explicit SystemBubbleWrapper(internal::SystemTrayBubble* bubble) : bubble_(bubble), is_persistent_(false) { } // Initializes the bubble view and creates |bubble_wrapper_|. void InitView(TrayBackgroundView* tray, views::View* anchor, TrayBubbleView::InitParams* init_params, bool is_persistent) { DCHECK(anchor); user::LoginStatus login_status = Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus(); bubble_->InitView(anchor, login_status, init_params); bubble_wrapper_.reset( new internal::TrayBubbleWrapper(tray, bubble_->bubble_view())); if (ash::switches::UseAlternateShelfLayout()) { // The system bubble should not have an arrow. bubble_->bubble_view()->SetArrowPaintType( views::BubbleBorder::PAINT_NONE); } is_persistent_ = is_persistent; // If ChromeVox is enabled, focus the default item if no item is focused. if (Shell::GetInstance()->accessibility_delegate()-> IsSpokenFeedbackEnabled()) { bubble_->FocusDefaultIfNeeded(); } } // Convenience accessors: SystemTrayBubble* bubble() const { return bubble_.get(); } SystemTrayBubble::BubbleType bubble_type() const { return bubble_->bubble_type(); } TrayBubbleView* bubble_view() const { return bubble_->bubble_view(); } bool is_persistent() const { return is_persistent_; } private: scoped_ptr<internal::SystemTrayBubble> bubble_; scoped_ptr<internal::TrayBubbleWrapper> bubble_wrapper_; bool is_persistent_; DISALLOW_COPY_AND_ASSIGN(SystemBubbleWrapper); }; } // namespace internal // SystemTray using internal::SystemTrayBubble; SystemTray::SystemTray(internal::StatusAreaWidget* status_area_widget) : internal::TrayBackgroundView(status_area_widget), items_(), default_bubble_height_(0), hide_notifications_(false), full_system_tray_menu_(false), tray_accessibility_(NULL), tray_date_(NULL) { SetContentsBackground(); } SystemTray::~SystemTray() { // Destroy any child views that might have back pointers before ~View(). system_bubble_.reset(); notification_bubble_.reset(); for (std::vector<SystemTrayItem*>::iterator it = items_.begin(); it != items_.end(); ++it) { (*it)->DestroyTrayView(); } } void SystemTray::InitializeTrayItems(SystemTrayDelegate* delegate) { internal::TrayBackgroundView::Initialize(); CreateItems(delegate); } void SystemTray::CreateItems(SystemTrayDelegate* delegate) { #if !defined(OS_WIN) AddTrayItem(new internal::TraySessionLengthLimit(this)); // Create user items for each possible user. ash::Shell* shell = ash::Shell::GetInstance(); int maximum_user_profiles = shell->session_state_delegate()->GetMaximumNumberOfLoggedInUsers(); for (int i = 0; i < maximum_user_profiles; i++) { internal::TrayUser* tray_user = new internal::TrayUser(this, i); AddTrayItem(tray_user); user_items_.push_back(tray_user); } if (maximum_user_profiles > 1) { // Add a special double line separator between users and the rest of the // menu if more then one user is logged in. AddTrayItem(new internal::TrayUserSeparator(this)); } #endif tray_accessibility_ = new internal::TrayAccessibility(this); tray_date_ = new internal::TrayDate(this); #if defined(OS_CHROMEOS) AddTrayItem(new internal::TrayEnterprise(this)); AddTrayItem(new internal::TrayLocallyManagedUser(this)); AddTrayItem(new internal::TrayIME(this)); AddTrayItem(tray_accessibility_); AddTrayItem(new internal::TrayTracing(this)); AddTrayItem( new internal::TrayPower(this, message_center::MessageCenter::Get())); AddTrayItem(new internal::TrayNetwork(this)); AddTrayItem(new internal::TrayVPN(this)); AddTrayItem(new internal::TraySms(this)); AddTrayItem(new internal::TrayBluetooth(this)); AddTrayItem(new internal::TrayDrive(this)); AddTrayItem(new internal::TrayDisplay(this)); AddTrayItem(new internal::ScreenCaptureTrayItem(this)); AddTrayItem(new internal::ScreenShareTrayItem(this)); AddTrayItem(new internal::TrayAudio(this)); AddTrayItem(new internal::TrayBrightness(this)); AddTrayItem(new internal::TrayCapsLock(this)); AddTrayItem(new internal::TraySettings(this)); AddTrayItem(new internal::TrayUpdate(this)); AddTrayItem(tray_date_); #elif defined(OS_WIN) AddTrayItem(tray_accessibility_); AddTrayItem(new internal::TrayUpdate(this)); AddTrayItem(tray_date_); #elif defined(OS_LINUX) AddTrayItem(new internal::TrayIME(this)); AddTrayItem(tray_accessibility_); AddTrayItem(new internal::TrayBluetooth(this)); AddTrayItem(new internal::TrayDrive(this)); AddTrayItem(new internal::TrayCapsLock(this)); AddTrayItem(new internal::TrayUpdate(this)); AddTrayItem(tray_date_); #endif #if defined(OS_LINUX) CommandLine* cmd = CommandLine::ForCurrentProcess(); if (cmd->HasSwitch(ash::switches::kAshEnableMemoryMonitor)) AddTrayItem(new internal::TrayMonitor(this)); #endif SetVisible(ash::Shell::GetInstance()->system_tray_delegate()-> GetTrayVisibilityOnStartup()); } void SystemTray::AddTrayItem(SystemTrayItem* item) { items_.push_back(item); SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate(); views::View* tray_item = item->CreateTrayView(delegate->GetUserLoginStatus()); item->UpdateAfterShelfAlignmentChange(shelf_alignment()); if (tray_item) { tray_container()->AddChildViewAt(tray_item, 0); PreferredSizeChanged(); tray_item_map_[item] = tray_item; } } void SystemTray::RemoveTrayItem(SystemTrayItem* item) { NOTIMPLEMENTED(); } const std::vector<SystemTrayItem*>& SystemTray::GetTrayItems() const { return items_.get(); } const std::vector<internal::TrayUser*>& SystemTray::GetTrayUserItems() const { return user_items_; } void SystemTray::ShowDefaultView(BubbleCreationType creation_type) { ShowDefaultViewWithOffset( creation_type, TrayBubbleView::InitParams::kArrowDefaultOffset, false); } void SystemTray::ShowPersistentDefaultView() { ShowItems(items_.get(), false, false, BUBBLE_CREATE_NEW, TrayBubbleView::InitParams::kArrowDefaultOffset, true); } void SystemTray::ShowDetailedView(SystemTrayItem* item, int close_delay, bool activate, BubbleCreationType creation_type) { std::vector<SystemTrayItem*> items; items.push_back(item); ShowItems(items, true, activate, creation_type, GetTrayXOffset(item), false); if (system_bubble_) system_bubble_->bubble()->StartAutoCloseTimer(close_delay); } void SystemTray::SetDetailedViewCloseDelay(int close_delay) { if (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DETAILED)) system_bubble_->bubble()->StartAutoCloseTimer(close_delay); } void SystemTray::HideDetailedView(SystemTrayItem* item) { if (item != detailed_item_) return; DestroySystemBubble(); UpdateNotificationBubble(); } void SystemTray::ShowNotificationView(SystemTrayItem* item) { if (std::find(notification_items_.begin(), notification_items_.end(), item) != notification_items_.end()) return; notification_items_.push_back(item); UpdateNotificationBubble(); } void SystemTray::HideNotificationView(SystemTrayItem* item) { std::vector<SystemTrayItem*>::iterator found_iter = std::find(notification_items_.begin(), notification_items_.end(), item); if (found_iter == notification_items_.end()) return; notification_items_.erase(found_iter); // Only update the notification bubble if visible (i.e. don't create one). if (notification_bubble_) UpdateNotificationBubble(); } void SystemTray::UpdateAfterLoginStatusChange(user::LoginStatus login_status) { DestroySystemBubble(); UpdateNotificationBubble(); for (std::vector<SystemTrayItem*>::iterator it = items_.begin(); it != items_.end(); ++it) { (*it)->UpdateAfterLoginStatusChange(login_status); } // Items default to SHELF_ALIGNMENT_BOTTOM. Update them if the initial // position of the shelf differs. if (shelf_alignment() != SHELF_ALIGNMENT_BOTTOM) UpdateAfterShelfAlignmentChange(shelf_alignment()); SetVisible(true); PreferredSizeChanged(); } void SystemTray::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) { for (std::vector<SystemTrayItem*>::iterator it = items_.begin(); it != items_.end(); ++it) { (*it)->UpdateAfterShelfAlignmentChange(alignment); } } void SystemTray::SetHideNotifications(bool hide_notifications) { if (notification_bubble_) notification_bubble_->bubble()->SetVisible(!hide_notifications); hide_notifications_ = hide_notifications; } bool SystemTray::ShouldShowLauncher() const { return system_bubble_.get() && system_bubble_->bubble()->ShouldShowLauncher(); } bool SystemTray::HasSystemBubble() const { return system_bubble_.get() != NULL; } bool SystemTray::HasNotificationBubble() const { return notification_bubble_.get() != NULL; } internal::SystemTrayBubble* SystemTray::GetSystemBubble() { if (!system_bubble_) return NULL; return system_bubble_->bubble(); } bool SystemTray::IsAnyBubbleVisible() const { return ((system_bubble_.get() && system_bubble_->bubble()->IsVisible()) || (notification_bubble_.get() && notification_bubble_->bubble()->IsVisible())); } bool SystemTray::IsMouseInNotificationBubble() const { if (!notification_bubble_) return false; return notification_bubble_->bubble_view()->GetBoundsInScreen().Contains( Shell::GetScreen()->GetCursorScreenPoint()); } bool SystemTray::CloseSystemBubble() const { if (!system_bubble_) return false; system_bubble_->bubble()->Close(); return true; } views::View* SystemTray::GetHelpButtonView() const { return tray_date_->GetHelpButtonView(); } bool SystemTray::CloseNotificationBubbleForTest() const { if (!notification_bubble_) return false; notification_bubble_->bubble()->Close(); return true; } // Private methods. bool SystemTray::HasSystemBubbleType(SystemTrayBubble::BubbleType type) { DCHECK(type != SystemTrayBubble::BUBBLE_TYPE_NOTIFICATION); return system_bubble_.get() && system_bubble_->bubble_type() == type; } void SystemTray::DestroySystemBubble() { CloseSystemBubbleAndDeactivateSystemTray(); detailed_item_ = NULL; UpdateWebNotifications(); } void SystemTray::DestroyNotificationBubble() { if (notification_bubble_) { notification_bubble_.reset(); UpdateWebNotifications(); } } int SystemTray::GetTrayXOffset(SystemTrayItem* item) const { // Don't attempt to align the arrow if the shelf is on the left or right. if (shelf_alignment() != SHELF_ALIGNMENT_BOTTOM && shelf_alignment() != SHELF_ALIGNMENT_TOP) return TrayBubbleView::InitParams::kArrowDefaultOffset; std::map<SystemTrayItem*, views::View*>::const_iterator it = tray_item_map_.find(item); if (it == tray_item_map_.end()) return TrayBubbleView::InitParams::kArrowDefaultOffset; const views::View* item_view = it->second; if (item_view->bounds().IsEmpty()) { // The bounds of item could be still empty if it does not have a visible // tray view. In that case, use the default (minimum) offset. return TrayBubbleView::InitParams::kArrowDefaultOffset; } gfx::Point point(item_view->width() / 2, 0); ConvertPointToWidget(item_view, &point); return point.x(); } void SystemTray::ShowDefaultViewWithOffset(BubbleCreationType creation_type, int arrow_offset, bool persistent) { if (creation_type != BUBBLE_USE_EXISTING) { Shell::GetInstance()->metrics()->RecordUserMetricsAction( ash::UMA_STATUS_AREA_MENU_OPENED); } ShowItems(items_.get(), false, true, creation_type, arrow_offset, persistent); } void SystemTray::ShowItems(const std::vector<SystemTrayItem*>& items, bool detailed, bool can_activate, BubbleCreationType creation_type, int arrow_offset, bool persistent) { // No system tray bubbles in kiosk mode. if (Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus() == ash::user::LOGGED_IN_KIOSK_APP) { return; } // Destroy any existing bubble and create a new one. SystemTrayBubble::BubbleType bubble_type = detailed ? SystemTrayBubble::BUBBLE_TYPE_DETAILED : SystemTrayBubble::BUBBLE_TYPE_DEFAULT; // Destroy the notification bubble here so that it doesn't get rebuilt // while we add items to the main bubble_ (e.g. in HideNotificationView). notification_bubble_.reset(); if (system_bubble_.get() && creation_type == BUBBLE_USE_EXISTING) { system_bubble_->bubble()->UpdateView(items, bubble_type); // If ChromeVox is enabled, focus the default item if no item is focused. if (Shell::GetInstance()->accessibility_delegate()-> IsSpokenFeedbackEnabled()) { system_bubble_->bubble()->FocusDefaultIfNeeded(); } } else { // Remember if the menu is a single property (like e.g. volume) or the // full tray menu. Note that in case of the |BUBBLE_USE_EXISTING| case // above, |full_system_tray_menu_| does not get changed since the fact that // the menu is full (or not) doesn't change even if a "single property" // (like network) replaces most of the menu. full_system_tray_menu_ = items.size() > 1; // The menu width is fixed, and it is a per language setting. int menu_width = std::max(kMinimumSystemTrayMenuWidth, Shell::GetInstance()->system_tray_delegate()->GetSystemTrayMenuWidth()); TrayBubbleView::InitParams init_params(TrayBubbleView::ANCHOR_TYPE_TRAY, GetAnchorAlignment(), menu_width, kTrayPopupMaxWidth); init_params.can_activate = can_activate; init_params.first_item_has_no_margin = ash::switches::UseAlternateShelfLayout(); if (detailed) { // This is the case where a volume control or brightness control bubble // is created. init_params.max_height = default_bubble_height_; init_params.arrow_color = kBackgroundColor; } else { init_params.arrow_color = kHeaderBackgroundColor; } init_params.arrow_offset = arrow_offset; if (bubble_type == SystemTrayBubble::BUBBLE_TYPE_DEFAULT) init_params.close_on_deactivate = !persistent; // For Volume and Brightness we don't want to show an arrow when // they are shown in a bubble by themselves. init_params.arrow_paint_type = views::BubbleBorder::PAINT_NORMAL; if (items.size() == 1 && items[0]->ShouldHideArrow()) init_params.arrow_paint_type = views::BubbleBorder::PAINT_TRANSPARENT; SystemTrayBubble* bubble = new SystemTrayBubble(this, items, bubble_type); system_bubble_.reset(new internal::SystemBubbleWrapper(bubble)); system_bubble_->InitView(this, tray_container(), &init_params, persistent); } // Save height of default view for creating detailed views directly. if (!detailed) default_bubble_height_ = system_bubble_->bubble_view()->height(); if (detailed && items.size() > 0) detailed_item_ = items[0]; else detailed_item_ = NULL; UpdateNotificationBubble(); // State changed, re-create notifications. if (!notification_bubble_) UpdateWebNotifications(); GetShelfLayoutManager()->UpdateAutoHideState(); // When we show the system menu in our alternate shelf layout, we need to // tint the background. if (full_system_tray_menu_) SetDrawBackgroundAsActive(true); } void SystemTray::UpdateNotificationBubble() { // Only show the notification bubble if we have notifications. if (notification_items_.empty()) { DestroyNotificationBubble(); return; } // Destroy the existing bubble before constructing a new one. notification_bubble_.reset(); SystemTrayBubble* notification_bubble; notification_bubble = new SystemTrayBubble( this, notification_items_, SystemTrayBubble::BUBBLE_TYPE_NOTIFICATION); views::View* anchor; TrayBubbleView::AnchorType anchor_type; // Tray items might want to show notifications while we are creating and // initializing the |system_bubble_| - but it might not be fully initialized // when coming here - this would produce a crashed like crbug.com/247416. // As such we check the existence of the widget here. if (system_bubble_.get() && system_bubble_->bubble_view() && system_bubble_->bubble_view()->GetWidget()) { anchor = system_bubble_->bubble_view(); anchor_type = TrayBubbleView::ANCHOR_TYPE_BUBBLE; } else { anchor = tray_container(); anchor_type = TrayBubbleView::ANCHOR_TYPE_TRAY; } TrayBubbleView::InitParams init_params(anchor_type, GetAnchorAlignment(), kTrayPopupMinWidth, kTrayPopupMaxWidth); init_params.first_item_has_no_margin = ash::switches::UseAlternateShelfLayout(); init_params.arrow_color = kBackgroundColor; init_params.arrow_offset = GetTrayXOffset(notification_items_[0]); notification_bubble_.reset( new internal::SystemBubbleWrapper(notification_bubble)); notification_bubble_->InitView(this, anchor, &init_params, false); if (notification_bubble->bubble_view()->child_count() == 0) { // It is possible that none of the items generated actual notifications. DestroyNotificationBubble(); return; } if (hide_notifications_) notification_bubble->SetVisible(false); else UpdateWebNotifications(); } void SystemTray::UpdateWebNotifications() { TrayBubbleView* bubble_view = NULL; if (notification_bubble_) bubble_view = notification_bubble_->bubble_view(); else if (system_bubble_) bubble_view = system_bubble_->bubble_view(); int height = 0; if (bubble_view) { gfx::Rect work_area = Shell::GetScreen()->GetDisplayNearestWindow( bubble_view->GetWidget()->GetNativeView()).work_area(); if (GetShelfLayoutManager()->GetAlignment() != SHELF_ALIGNMENT_TOP) { height = std::max( 0, work_area.height() - bubble_view->GetBoundsInScreen().y()); } else { height = std::max( 0, bubble_view->GetBoundsInScreen().bottom() - work_area.y()); } } status_area_widget()->web_notification_tray()->SetSystemTrayHeight(height); } void SystemTray::SetShelfAlignment(ShelfAlignment alignment) { if (alignment == shelf_alignment()) return; internal::TrayBackgroundView::SetShelfAlignment(alignment); UpdateAfterShelfAlignmentChange(alignment); // Destroy any existing bubble so that it is rebuilt correctly. CloseSystemBubbleAndDeactivateSystemTray(); // Rebuild any notification bubble. if (notification_bubble_) { notification_bubble_.reset(); UpdateNotificationBubble(); } } void SystemTray::AnchorUpdated() { if (notification_bubble_) { notification_bubble_->bubble_view()->UpdateBubble(); // Ensure that the notification buble is above the launcher/status area. notification_bubble_->bubble_view()->GetWidget()->StackAtTop(); UpdateBubbleViewArrow(notification_bubble_->bubble_view()); } if (system_bubble_) { system_bubble_->bubble_view()->UpdateBubble(); UpdateBubbleViewArrow(system_bubble_->bubble_view()); } } base::string16 SystemTray::GetAccessibleNameForTray() { return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBLE_NAME); } void SystemTray::BubbleResized(const TrayBubbleView* bubble_view) { UpdateWebNotifications(); } void SystemTray::HideBubbleWithView(const TrayBubbleView* bubble_view) { if (system_bubble_.get() && bubble_view == system_bubble_->bubble_view()) { DestroySystemBubble(); UpdateNotificationBubble(); // State changed, re-create notifications. GetShelfLayoutManager()->UpdateAutoHideState(); } else if (notification_bubble_.get() && bubble_view == notification_bubble_->bubble_view()) { DestroyNotificationBubble(); } } bool SystemTray::ClickedOutsideBubble() { if (!system_bubble_ || system_bubble_->is_persistent()) return false; HideBubbleWithView(system_bubble_->bubble_view()); return true; } void SystemTray::BubbleViewDestroyed() { if (system_bubble_) { system_bubble_->bubble()->DestroyItemViews(); system_bubble_->bubble()->BubbleViewDestroyed(); } } void SystemTray::OnMouseEnteredView() { if (system_bubble_) system_bubble_->bubble()->StopAutoCloseTimer(); } void SystemTray::OnMouseExitedView() { if (system_bubble_) system_bubble_->bubble()->RestartAutoCloseTimer(); } base::string16 SystemTray::GetAccessibleNameForBubble() { return GetAccessibleNameForTray(); } gfx::Rect SystemTray::GetAnchorRect( views::Widget* anchor_widget, TrayBubbleView::AnchorType anchor_type, TrayBubbleView::AnchorAlignment anchor_alignment) { return GetBubbleAnchorRect(anchor_widget, anchor_type, anchor_alignment); } void SystemTray::HideBubble(const TrayBubbleView* bubble_view) { HideBubbleWithView(bubble_view); } views::View* SystemTray::GetTrayItemViewForTest(SystemTrayItem* item) { std::map<SystemTrayItem*, views::View*>::iterator it = tray_item_map_.find(item); return it == tray_item_map_.end() ? NULL : it->second; } void SystemTray::AddTrayUserItemForTest(internal::TrayUser* tray_user) { AddTrayItem(tray_user); user_items_.push_back(tray_user); } bool SystemTray::PerformAction(const ui::Event& event) { // If we're already showing the default view, hide it; otherwise, show it // (and hide any popup that's currently shown). if (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DEFAULT)) { system_bubble_->bubble()->Close(); } else { int arrow_offset = TrayBubbleView::InitParams::kArrowDefaultOffset; if (event.IsMouseEvent() || event.type() == ui::ET_GESTURE_TAP) { const ui::LocatedEvent& located_event = static_cast<const ui::LocatedEvent&>(event); if (shelf_alignment() == SHELF_ALIGNMENT_BOTTOM || shelf_alignment() == SHELF_ALIGNMENT_TOP) { gfx::Point point(located_event.x(), 0); ConvertPointToWidget(this, &point); arrow_offset = point.x(); } } ShowDefaultViewWithOffset(BUBBLE_CREATE_NEW, arrow_offset, false); } return true; } void SystemTray::CloseSystemBubbleAndDeactivateSystemTray() { system_bubble_.reset(); // When closing a system bubble with the alternate shelf layout, we need to // turn off the active tinting of the shelf. if (full_system_tray_menu_) { SetDrawBackgroundAsActive(false); full_system_tray_menu_ = false; } } } // namespace ash