system_tray.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
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/tray/system_tray.h" 6 7#include "ash/ash_switches.h" 8#include "ash/shelf/shelf_layout_manager.h" 9#include "ash/shell.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/date/tray_date.h" 15#include "ash/system/drive/tray_drive.h" 16#include "ash/system/ime/tray_ime.h" 17#include "ash/system/locale/tray_locale.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 "base/command_line.h" 32#include "base/logging.h" 33#include "base/strings/utf_string_conversions.h" 34#include "base/timer/timer.h" 35#include "grit/ash_strings.h" 36#include "ui/aura/root_window.h" 37#include "ui/base/events/event_constants.h" 38#include "ui/base/l10n/l10n_util.h" 39#include "ui/compositor/layer.h" 40#include "ui/gfx/canvas.h" 41#include "ui/gfx/screen.h" 42#include "ui/gfx/skia_util.h" 43#include "ui/views/border.h" 44#include "ui/views/controls/label.h" 45#include "ui/views/layout/box_layout.h" 46#include "ui/views/layout/fill_layout.h" 47#include "ui/views/view.h" 48 49#if defined(OS_CHROMEOS) 50#include "ash/system/chromeos/audio/tray_audio.h" 51#include "ash/system/chromeos/enterprise/tray_enterprise.h" 52#include "ash/system/chromeos/managed/tray_locally_managed_user.h" 53#include "ash/system/chromeos/network/tray_network.h" 54#include "ash/system/chromeos/network/tray_sms.h" 55#include "ash/system/chromeos/network/tray_vpn.h" 56#include "ash/system/chromeos/power/tray_power.h" 57#include "ash/system/chromeos/screen_security/screen_capture_tray_item.h" 58#include "ash/system/chromeos/screen_security/screen_share_tray_item.h" 59#include "ash/system/chromeos/settings/tray_settings.h" 60#include "ash/system/chromeos/tray_display.h" 61#include "ui/message_center/message_center.h" 62#endif 63 64using views::TrayBubbleView; 65 66namespace ash { 67 68// The minimum width of the system tray menu width. 69const int kMinimumSystemTrayMenuWidth = 300; 70 71namespace internal { 72 73// Class to initialize and manage the SystemTrayBubble and TrayBubbleWrapper 74// instances for a bubble. 75 76class SystemBubbleWrapper { 77 public: 78 // Takes ownership of |bubble|. 79 explicit SystemBubbleWrapper(internal::SystemTrayBubble* bubble) 80 : bubble_(bubble) { 81 } 82 83 // Initializes the bubble view and creates |bubble_wrapper_|. 84 void InitView(TrayBackgroundView* tray, 85 views::View* anchor, 86 TrayBubbleView::InitParams* init_params) { 87 DCHECK(anchor); 88 user::LoginStatus login_status = 89 Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus(); 90 bubble_->InitView(anchor, login_status, init_params); 91 bubble_wrapper_.reset( 92 new internal::TrayBubbleWrapper(tray, bubble_->bubble_view())); 93 if (ash::switches::UseAlternateShelfLayout()) { 94 // The system bubble should not have an arrow. 95 bubble_->bubble_view()->SetArrowPaintType( 96 views::BubbleBorder::PAINT_NONE); 97 } 98 } 99 100 // Convenience accessors: 101 SystemTrayBubble* bubble() const { return bubble_.get(); } 102 SystemTrayBubble::BubbleType bubble_type() const { 103 return bubble_->bubble_type(); 104 } 105 TrayBubbleView* bubble_view() const { return bubble_->bubble_view(); } 106 107 private: 108 scoped_ptr<internal::SystemTrayBubble> bubble_; 109 scoped_ptr<internal::TrayBubbleWrapper> bubble_wrapper_; 110 111 DISALLOW_COPY_AND_ASSIGN(SystemBubbleWrapper); 112}; 113 114} // namespace internal 115 116// SystemTray 117 118using internal::SystemTrayBubble; 119 120SystemTray::SystemTray(internal::StatusAreaWidget* status_area_widget) 121 : internal::TrayBackgroundView(status_area_widget), 122 items_(), 123 default_bubble_height_(0), 124 hide_notifications_(false), 125 tray_accessibility_(NULL) { 126 SetContentsBackground(); 127} 128 129SystemTray::~SystemTray() { 130 // Destroy any child views that might have back pointers before ~View(). 131 system_bubble_.reset(); 132 notification_bubble_.reset(); 133 for (std::vector<SystemTrayItem*>::iterator it = items_.begin(); 134 it != items_.end(); 135 ++it) { 136 (*it)->DestroyTrayView(); 137 } 138} 139 140void SystemTray::InitializeTrayItems(SystemTrayDelegate* delegate) { 141 internal::TrayBackgroundView::Initialize(); 142 CreateItems(delegate); 143} 144 145void SystemTray::CreateItems(SystemTrayDelegate* delegate) { 146#if !defined(OS_WIN) 147 AddTrayItem(new internal::TraySessionLengthLimit(this)); 148 AddTrayItem(new internal::TrayLogoutButton(this)); 149 // In multi-profile user mode we can have multiple user tiles. 150 ash::Shell* shell = ash::Shell::GetInstance(); 151 int maximum_user_profiles = 152 shell->delegate()->IsMultiProfilesEnabled() ? 153 shell->session_state_delegate()->GetMaximumNumberOfLoggedInUsers() : 154 0; 155 // Note: We purposely use one more item then logged in users to account for 156 // the additional separator. 157 for (int i = 0; i <= maximum_user_profiles; i++) 158 AddTrayItem(new internal::TrayUser(this, i)); 159 160#endif 161#if defined(OS_CHROMEOS) 162 AddTrayItem(new internal::TrayEnterprise(this)); 163 AddTrayItem(new internal::TrayLocallyManagedUser(this)); 164#endif 165 AddTrayItem(new internal::TrayIME(this)); 166 tray_accessibility_ = new internal::TrayAccessibility(this); 167 AddTrayItem(tray_accessibility_); 168#if defined(OS_CHROMEOS) 169 AddTrayItem( 170 new internal::TrayPower(this, message_center::MessageCenter::Get())); 171#endif 172#if defined(OS_CHROMEOS) 173 AddTrayItem(new internal::TrayNetwork(this)); 174 AddTrayItem(new internal::TrayVPN(this)); 175 AddTrayItem(new internal::TraySms(this)); 176#endif 177#if !defined(OS_WIN) 178 AddTrayItem(new internal::TrayBluetooth(this)); 179#endif 180 AddTrayItem(new internal::TrayDrive(this)); 181 AddTrayItem(new internal::TrayLocale(this)); 182#if defined(OS_CHROMEOS) 183 AddTrayItem(new internal::TrayDisplay(this)); 184 AddTrayItem(new internal::ScreenCaptureTrayItem(this)); 185 AddTrayItem(new internal::ScreenShareTrayItem(this)); 186 AddTrayItem(new internal::TrayAudio(this)); 187#endif 188#if !defined(OS_WIN) 189 AddTrayItem(new internal::TrayBrightness(this)); 190 AddTrayItem(new internal::TrayCapsLock(this)); 191#endif 192#if defined(OS_CHROMEOS) 193 AddTrayItem(new internal::TraySettings(this)); 194#endif 195 AddTrayItem(new internal::TrayUpdate(this)); 196 AddTrayItem(new internal::TrayDate(this)); 197 198#if defined(OS_LINUX) 199 // Add memory monitor if enabled. 200 CommandLine* cmd = CommandLine::ForCurrentProcess(); 201 if (cmd->HasSwitch(ash::switches::kAshEnableMemoryMonitor)) 202 AddTrayItem(new internal::TrayMonitor(this)); 203#endif 204 205 SetVisible(ash::Shell::GetInstance()->system_tray_delegate()-> 206 GetTrayVisibilityOnStartup()); 207} 208 209void SystemTray::AddTrayItem(SystemTrayItem* item) { 210 items_.push_back(item); 211 212 SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate(); 213 views::View* tray_item = item->CreateTrayView(delegate->GetUserLoginStatus()); 214 item->UpdateAfterShelfAlignmentChange(shelf_alignment()); 215 216 if (tray_item) { 217 tray_container()->AddChildViewAt(tray_item, 0); 218 PreferredSizeChanged(); 219 tray_item_map_[item] = tray_item; 220 } 221} 222 223void SystemTray::RemoveTrayItem(SystemTrayItem* item) { 224 NOTIMPLEMENTED(); 225} 226 227const std::vector<SystemTrayItem*>& SystemTray::GetTrayItems() const { 228 return items_.get(); 229} 230 231void SystemTray::ShowDefaultView(BubbleCreationType creation_type) { 232 ShowDefaultViewWithOffset(creation_type, 233 TrayBubbleView::InitParams::kArrowDefaultOffset); 234} 235 236void SystemTray::ShowDetailedView(SystemTrayItem* item, 237 int close_delay, 238 bool activate, 239 BubbleCreationType creation_type) { 240 std::vector<SystemTrayItem*> items; 241 items.push_back(item); 242 ShowItems(items, true, activate, creation_type, GetTrayXOffset(item)); 243 if (system_bubble_) 244 system_bubble_->bubble()->StartAutoCloseTimer(close_delay); 245} 246 247void SystemTray::SetDetailedViewCloseDelay(int close_delay) { 248 if (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DETAILED)) 249 system_bubble_->bubble()->StartAutoCloseTimer(close_delay); 250} 251 252void SystemTray::HideDetailedView(SystemTrayItem* item) { 253 if (item != detailed_item_) 254 return; 255 DestroySystemBubble(); 256 UpdateNotificationBubble(); 257} 258 259void SystemTray::ShowNotificationView(SystemTrayItem* item) { 260 if (std::find(notification_items_.begin(), notification_items_.end(), item) 261 != notification_items_.end()) 262 return; 263 notification_items_.push_back(item); 264 UpdateNotificationBubble(); 265} 266 267void SystemTray::HideNotificationView(SystemTrayItem* item) { 268 std::vector<SystemTrayItem*>::iterator found_iter = 269 std::find(notification_items_.begin(), notification_items_.end(), item); 270 if (found_iter == notification_items_.end()) 271 return; 272 notification_items_.erase(found_iter); 273 // Only update the notification bubble if visible (i.e. don't create one). 274 if (notification_bubble_) 275 UpdateNotificationBubble(); 276} 277 278void SystemTray::UpdateAfterLoginStatusChange(user::LoginStatus login_status) { 279 DestroySystemBubble(); 280 UpdateNotificationBubble(); 281 282 for (std::vector<SystemTrayItem*>::iterator it = items_.begin(); 283 it != items_.end(); 284 ++it) { 285 (*it)->UpdateAfterLoginStatusChange(login_status); 286 } 287 288 // Items default to SHELF_ALIGNMENT_BOTTOM. Update them if the initial 289 // position of the shelf differs. 290 if (shelf_alignment() != SHELF_ALIGNMENT_BOTTOM) 291 UpdateAfterShelfAlignmentChange(shelf_alignment()); 292 293 SetVisible(true); 294 PreferredSizeChanged(); 295} 296 297void SystemTray::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) { 298 for (std::vector<SystemTrayItem*>::iterator it = items_.begin(); 299 it != items_.end(); 300 ++it) { 301 (*it)->UpdateAfterShelfAlignmentChange(alignment); 302 } 303} 304 305void SystemTray::SetHideNotifications(bool hide_notifications) { 306 if (notification_bubble_) 307 notification_bubble_->bubble()->SetVisible(!hide_notifications); 308 hide_notifications_ = hide_notifications; 309} 310 311bool SystemTray::ShouldShowLauncher() const { 312 return system_bubble_.get() && system_bubble_->bubble()->ShouldShowLauncher(); 313} 314 315bool SystemTray::HasSystemBubble() const { 316 return system_bubble_.get() != NULL; 317} 318 319bool SystemTray::HasNotificationBubble() const { 320 return notification_bubble_.get() != NULL; 321} 322 323bool SystemTray::IsPressed() { 324 return HasSystemBubble(); 325} 326 327internal::SystemTrayBubble* SystemTray::GetSystemBubble() { 328 if (!system_bubble_) 329 return NULL; 330 return system_bubble_->bubble(); 331} 332 333bool SystemTray::IsAnyBubbleVisible() const { 334 return ((system_bubble_.get() && 335 system_bubble_->bubble()->IsVisible()) || 336 (notification_bubble_.get() && 337 notification_bubble_->bubble()->IsVisible())); 338} 339 340bool SystemTray::IsMouseInNotificationBubble() const { 341 if (!notification_bubble_) 342 return false; 343 return notification_bubble_->bubble_view()->GetBoundsInScreen().Contains( 344 Shell::GetScreen()->GetCursorScreenPoint()); 345} 346 347bool SystemTray::CloseSystemBubble() const { 348 if (!system_bubble_) 349 return false; 350 system_bubble_->bubble()->Close(); 351 return true; 352} 353 354bool SystemTray::CloseNotificationBubbleForTest() const { 355 if (!notification_bubble_) 356 return false; 357 notification_bubble_->bubble()->Close(); 358 return true; 359} 360 361// Private methods. 362 363bool SystemTray::HasSystemBubbleType(SystemTrayBubble::BubbleType type) { 364 DCHECK(type != SystemTrayBubble::BUBBLE_TYPE_NOTIFICATION); 365 return system_bubble_.get() && system_bubble_->bubble_type() == type; 366} 367 368void SystemTray::DestroySystemBubble() { 369 system_bubble_.reset(); 370 detailed_item_ = NULL; 371} 372 373void SystemTray::DestroyNotificationBubble() { 374 notification_bubble_.reset(); 375 status_area_widget()->SetHideWebNotifications(false); 376} 377 378int SystemTray::GetTrayXOffset(SystemTrayItem* item) const { 379 // Don't attempt to align the arrow if the shelf is on the left or right. 380 if (shelf_alignment() != SHELF_ALIGNMENT_BOTTOM && 381 shelf_alignment() != SHELF_ALIGNMENT_TOP) 382 return TrayBubbleView::InitParams::kArrowDefaultOffset; 383 384 std::map<SystemTrayItem*, views::View*>::const_iterator it = 385 tray_item_map_.find(item); 386 if (it == tray_item_map_.end()) 387 return TrayBubbleView::InitParams::kArrowDefaultOffset; 388 389 const views::View* item_view = it->second; 390 if (item_view->bounds().IsEmpty()) { 391 // The bounds of item could be still empty if it does not have a visible 392 // tray view. In that case, use the default (minimum) offset. 393 return TrayBubbleView::InitParams::kArrowDefaultOffset; 394 } 395 396 gfx::Point point(item_view->width() / 2, 0); 397 ConvertPointToWidget(item_view, &point); 398 return point.x(); 399} 400 401void SystemTray::ShowDefaultViewWithOffset(BubbleCreationType creation_type, 402 int arrow_offset) { 403 ShowItems(items_.get(), false, true, creation_type, arrow_offset); 404} 405 406void SystemTray::ShowItems(const std::vector<SystemTrayItem*>& items, 407 bool detailed, 408 bool can_activate, 409 BubbleCreationType creation_type, 410 int arrow_offset) { 411 // No system tray bubbles in kiosk mode. 412 if (Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus() == 413 ash::user::LOGGED_IN_KIOSK_APP) { 414 return; 415 } 416 417 // Destroy any existing bubble and create a new one. 418 SystemTrayBubble::BubbleType bubble_type = detailed ? 419 SystemTrayBubble::BUBBLE_TYPE_DETAILED : 420 SystemTrayBubble::BUBBLE_TYPE_DEFAULT; 421 422 // Destroy the notification bubble here so that it doesn't get rebuilt 423 // while we add items to the main bubble_ (e.g. in HideNotificationView). 424 notification_bubble_.reset(); 425 426 if (system_bubble_.get() && creation_type == BUBBLE_USE_EXISTING) { 427 system_bubble_->bubble()->UpdateView(items, bubble_type); 428 } else { 429 // The menu width is fixed, and it is a per language setting. 430 int menu_width = std::max(kMinimumSystemTrayMenuWidth, 431 Shell::GetInstance()->system_tray_delegate()->GetSystemTrayMenuWidth()); 432 433 TrayBubbleView::InitParams init_params(TrayBubbleView::ANCHOR_TYPE_TRAY, 434 GetAnchorAlignment(), 435 menu_width, 436 kTrayPopupMaxWidth); 437 init_params.can_activate = can_activate; 438 if (detailed) { 439 // This is the case where a volume control or brightness control bubble 440 // is created. 441 init_params.max_height = default_bubble_height_; 442 init_params.arrow_color = kBackgroundColor; 443 } else { 444 init_params.arrow_color = kHeaderBackgroundColor; 445 } 446 init_params.arrow_offset = arrow_offset; 447 // For Volume and Brightness we don't want to show an arrow when 448 // they are shown in a bubble by themselves. 449 init_params.arrow_paint_type = views::BubbleBorder::PAINT_NORMAL; 450 if (items.size() == 1 && items[0]->ShouldHideArrow()) 451 init_params.arrow_paint_type = views::BubbleBorder::PAINT_TRANSPARENT; 452 SystemTrayBubble* bubble = new SystemTrayBubble(this, items, bubble_type); 453 system_bubble_.reset(new internal::SystemBubbleWrapper(bubble)); 454 system_bubble_->InitView(this, tray_container(), &init_params); 455 } 456 // Save height of default view for creating detailed views directly. 457 if (!detailed) 458 default_bubble_height_ = system_bubble_->bubble_view()->height(); 459 460 if (detailed && items.size() > 0) 461 detailed_item_ = items[0]; 462 else 463 detailed_item_ = NULL; 464 465 UpdateNotificationBubble(); // State changed, re-create notifications. 466 status_area_widget()->SetHideWebNotifications(true); 467 GetShelfLayoutManager()->UpdateAutoHideState(); 468} 469 470void SystemTray::UpdateNotificationBubble() { 471 // Only show the notification buble if we have notifications. 472 if (notification_items_.empty()) { 473 DestroyNotificationBubble(); 474 return; 475 } 476 // Destroy the existing bubble before constructing a new one. 477 notification_bubble_.reset(); 478 SystemTrayBubble* notification_bubble; 479 notification_bubble = new SystemTrayBubble( 480 this, notification_items_, SystemTrayBubble::BUBBLE_TYPE_NOTIFICATION); 481 views::View* anchor; 482 TrayBubbleView::AnchorType anchor_type; 483 // Tray items might want to show notifications while we are creating and 484 // initializing the |system_bubble_| - but it might not be fully initialized 485 // when coming here - this would produce a crashed like crbug.com/247416. 486 // As such we check the existence of the widget here. 487 if (system_bubble_.get() && 488 system_bubble_->bubble_view() && 489 system_bubble_->bubble_view()->GetWidget()) { 490 anchor = system_bubble_->bubble_view(); 491 anchor_type = TrayBubbleView::ANCHOR_TYPE_BUBBLE; 492 } else { 493 anchor = tray_container(); 494 anchor_type = TrayBubbleView::ANCHOR_TYPE_TRAY; 495 } 496 TrayBubbleView::InitParams init_params(anchor_type, 497 GetAnchorAlignment(), 498 kTrayPopupMinWidth, 499 kTrayPopupMaxWidth); 500 init_params.arrow_color = kBackgroundColor; 501 init_params.arrow_offset = GetTrayXOffset(notification_items_[0]); 502 notification_bubble_.reset( 503 new internal::SystemBubbleWrapper(notification_bubble)); 504 notification_bubble_->InitView(this, anchor, &init_params); 505 506 if (notification_bubble->bubble_view()->child_count() == 0) { 507 // It is possible that none of the items generated actual notifications. 508 DestroyNotificationBubble(); 509 return; 510 } 511 if (hide_notifications_) 512 notification_bubble->SetVisible(false); 513 else 514 status_area_widget()->SetHideWebNotifications(true); 515} 516 517void SystemTray::SetShelfAlignment(ShelfAlignment alignment) { 518 if (alignment == shelf_alignment()) 519 return; 520 internal::TrayBackgroundView::SetShelfAlignment(alignment); 521 UpdateAfterShelfAlignmentChange(alignment); 522 // Destroy any existing bubble so that it is rebuilt correctly. 523 system_bubble_.reset(); 524 // Rebuild any notification bubble. 525 if (notification_bubble_) { 526 notification_bubble_.reset(); 527 UpdateNotificationBubble(); 528 } 529} 530 531void SystemTray::AnchorUpdated() { 532 if (notification_bubble_) { 533 notification_bubble_->bubble_view()->UpdateBubble(); 534 // Ensure that the notification buble is above the launcher/status area. 535 notification_bubble_->bubble_view()->GetWidget()->StackAtTop(); 536 UpdateBubbleViewArrow(notification_bubble_->bubble_view()); 537 } 538 if (system_bubble_) { 539 system_bubble_->bubble_view()->UpdateBubble(); 540 if (!ash::switches::UseAlternateShelfLayout()) 541 UpdateBubbleViewArrow(system_bubble_->bubble_view()); 542 } 543} 544 545base::string16 SystemTray::GetAccessibleNameForTray() { 546 return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBLE_NAME); 547} 548 549void SystemTray::HideBubbleWithView(const TrayBubbleView* bubble_view) { 550 if (system_bubble_.get() && bubble_view == system_bubble_->bubble_view()) { 551 DestroySystemBubble(); 552 UpdateNotificationBubble(); // State changed, re-create notifications. 553 GetShelfLayoutManager()->UpdateAutoHideState(); 554 } else if (notification_bubble_.get() && 555 bubble_view == notification_bubble_->bubble_view()) { 556 DestroyNotificationBubble(); 557 } 558} 559 560bool SystemTray::ClickedOutsideBubble() { 561 if (!system_bubble_) 562 return false; 563 HideBubbleWithView(system_bubble_->bubble_view()); 564 return true; 565} 566 567void SystemTray::BubbleViewDestroyed() { 568 if (system_bubble_) { 569 system_bubble_->bubble()->DestroyItemViews(); 570 system_bubble_->bubble()->BubbleViewDestroyed(); 571 } 572} 573 574void SystemTray::OnMouseEnteredView() { 575 if (system_bubble_) 576 system_bubble_->bubble()->StopAutoCloseTimer(); 577} 578 579void SystemTray::OnMouseExitedView() { 580 if (system_bubble_) 581 system_bubble_->bubble()->RestartAutoCloseTimer(); 582} 583 584base::string16 SystemTray::GetAccessibleNameForBubble() { 585 return GetAccessibleNameForTray(); 586} 587 588gfx::Rect SystemTray::GetAnchorRect( 589 views::Widget* anchor_widget, 590 TrayBubbleView::AnchorType anchor_type, 591 TrayBubbleView::AnchorAlignment anchor_alignment) { 592 return GetBubbleAnchorRect(anchor_widget, anchor_type, anchor_alignment); 593} 594 595void SystemTray::HideBubble(const TrayBubbleView* bubble_view) { 596 HideBubbleWithView(bubble_view); 597} 598 599bool SystemTray::PerformAction(const ui::Event& event) { 600 // If we're already showing the default view, hide it; otherwise, show it 601 // (and hide any popup that's currently shown). 602 if (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DEFAULT)) { 603 system_bubble_->bubble()->Close(); 604 } else { 605 int arrow_offset = TrayBubbleView::InitParams::kArrowDefaultOffset; 606 if (event.IsMouseEvent() || event.type() == ui::ET_GESTURE_TAP) { 607 const ui::LocatedEvent& located_event = 608 static_cast<const ui::LocatedEvent&>(event); 609 if (shelf_alignment() == SHELF_ALIGNMENT_BOTTOM || 610 shelf_alignment() == SHELF_ALIGNMENT_TOP) { 611 gfx::Point point(located_event.x(), 0); 612 ConvertPointToWidget(this, &point); 613 arrow_offset = point.x(); 614 } 615 } 616 ShowDefaultViewWithOffset(BUBBLE_CREATE_NEW, arrow_offset); 617 } 618 return true; 619} 620 621} // namespace ash 622