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/metrics/user_metrics_recorder.h" 9 #include "ash/shelf/shelf_layout_manager.h" 10 #include "ash/shell.h" 11 #include "ash/shell/panel_window.h" 12 #include "ash/shell_window_ids.h" 13 #include "ash/system/audio/tray_audio.h" 14 #include "ash/system/bluetooth/tray_bluetooth.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/status_area_widget.h" 19 #include "ash/system/tray/system_tray_delegate.h" 20 #include "ash/system/tray/system_tray_item.h" 21 #include "ash/system/tray/tray_bubble_wrapper.h" 22 #include "ash/system/tray/tray_constants.h" 23 #include "ash/system/tray_accessibility.h" 24 #include "ash/system/tray_update.h" 25 #include "ash/system/user/login_status.h" 26 #include "ash/system/user/tray_user.h" 27 #include "ash/system/user/tray_user_separator.h" 28 #include "ash/system/web_notification/web_notification_tray.h" 29 #include "base/logging.h" 30 #include "base/strings/utf_string_conversions.h" 31 #include "base/timer/timer.h" 32 #include "grit/ash_strings.h" 33 #include "ui/aura/window_event_dispatcher.h" 34 #include "ui/base/l10n/l10n_util.h" 35 #include "ui/compositor/layer.h" 36 #include "ui/events/event_constants.h" 37 #include "ui/gfx/canvas.h" 38 #include "ui/gfx/screen.h" 39 #include "ui/gfx/skia_util.h" 40 #include "ui/views/border.h" 41 #include "ui/views/controls/label.h" 42 #include "ui/views/layout/box_layout.h" 43 #include "ui/views/layout/fill_layout.h" 44 #include "ui/views/view.h" 45 46 #if defined(OS_CHROMEOS) 47 #include "ash/system/chromeos/audio/tray_audio_chromeos.h" 48 #include "ash/system/chromeos/brightness/tray_brightness.h" 49 #include "ash/system/chromeos/enterprise/tray_enterprise.h" 50 #include "ash/system/chromeos/managed/tray_locally_managed_user.h" 51 #include "ash/system/chromeos/network/tray_network.h" 52 #include "ash/system/chromeos/network/tray_sms.h" 53 #include "ash/system/chromeos/network/tray_vpn.h" 54 #include "ash/system/chromeos/power/tray_power.h" 55 #include "ash/system/chromeos/rotation/tray_rotation_lock.h" 56 #include "ash/system/chromeos/screen_security/screen_capture_tray_item.h" 57 #include "ash/system/chromeos/screen_security/screen_share_tray_item.h" 58 #include "ash/system/chromeos/session/tray_session_length_limit.h" 59 #include "ash/system/chromeos/settings/tray_settings.h" 60 #include "ash/system/chromeos/tray_caps_lock.h" 61 #include "ash/system/chromeos/tray_display.h" 62 #include "ash/system/chromeos/tray_tracing.h" 63 #include "ash/system/tray/media_security/multi_profile_media_tray_item.h" 64 #include "ui/message_center/message_center.h" 65 #elif defined(OS_WIN) 66 #include "ash/system/win/audio/tray_audio_win.h" 67 #include "media/audio/win/core_audio_util_win.h" 68 #endif 69 70 using views::TrayBubbleView; 71 72 namespace ash { 73 74 // The minimum width of the system tray menu width. 75 const int kMinimumSystemTrayMenuWidth = 300; 76 77 // Class to initialize and manage the SystemTrayBubble and TrayBubbleWrapper 78 // instances for a bubble. 79 80 class SystemBubbleWrapper { 81 public: 82 // Takes ownership of |bubble|. 83 explicit SystemBubbleWrapper(SystemTrayBubble* bubble) 84 : bubble_(bubble), is_persistent_(false) {} 85 86 // Initializes the bubble view and creates |bubble_wrapper_|. 87 void InitView(TrayBackgroundView* tray, 88 views::View* anchor, 89 TrayBubbleView::InitParams* init_params, 90 bool is_persistent) { 91 DCHECK(anchor); 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; 100 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(); 105 } 106 } 107 108 // Convenience accessors: 109 SystemTrayBubble* bubble() const { return bubble_.get(); } 110 SystemTrayBubble::BubbleType bubble_type() const { 111 return bubble_->bubble_type(); 112 } 113 TrayBubbleView* bubble_view() const { return bubble_->bubble_view(); } 114 bool is_persistent() const { return is_persistent_; } 115 116 private: 117 scoped_ptr<SystemTrayBubble> bubble_; 118 scoped_ptr<TrayBubbleWrapper> bubble_wrapper_; 119 bool is_persistent_; 120 121 DISALLOW_COPY_AND_ASSIGN(SystemBubbleWrapper); 122 }; 123 124 125 // SystemTray 126 127 SystemTray::SystemTray(StatusAreaWidget* status_area_widget) 128 : TrayBackgroundView(status_area_widget), 129 items_(), 130 default_bubble_height_(0), 131 hide_notifications_(false), 132 full_system_tray_menu_(false), 133 tray_accessibility_(NULL), 134 tray_date_(NULL) { 135 SetContentsBackground(); 136 } 137 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(); 143 it != items_.end(); 144 ++it) { 145 (*it)->DestroyTrayView(); 146 } 147 } 148 149 void SystemTray::InitializeTrayItems(SystemTrayDelegate* delegate) { 150 TrayBackgroundView::Initialize(); 151 CreateItems(delegate); 152 } 153 154 void SystemTray::CreateItems(SystemTrayDelegate* delegate) { 155 #if !defined(OS_WIN) 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)); 162 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)); 167 } 168 #endif 169 170 tray_accessibility_ = new TrayAccessibility(this); 171 tray_date_ = new TrayDate(this); 172 173 #if defined(OS_CHROMEOS) 174 AddTrayItem(new TraySessionLengthLimit(this)); 175 AddTrayItem(new TrayEnterprise(this)); 176 AddTrayItem(new TrayLocallyManagedUser(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 AddTrayItem(new TrayDrive(this)); 186 AddTrayItem(new TrayDisplay(this)); 187 AddTrayItem(new ScreenCaptureTrayItem(this)); 188 AddTrayItem(new ScreenShareTrayItem(this)); 189 AddTrayItem(new MultiProfileMediaTrayItem(this)); 190 AddTrayItem(new TrayAudioChromeOs(this)); 191 AddTrayItem(new TrayBrightness(this)); 192 AddTrayItem(new TrayCapsLock(this)); 193 AddTrayItem(new TraySettings(this)); 194 AddTrayItem(new TrayUpdate(this)); 195 AddTrayItem(new TrayRotationLock(this)); 196 AddTrayItem(tray_date_); 197 #elif defined(OS_WIN) 198 AddTrayItem(tray_accessibility_); 199 if (media::CoreAudioUtil::IsSupported()) 200 AddTrayItem(new TrayAudioWin(this)); 201 AddTrayItem(new TrayUpdate(this)); 202 AddTrayItem(tray_date_); 203 #elif defined(OS_LINUX) 204 AddTrayItem(new TrayIME(this)); 205 AddTrayItem(tray_accessibility_); 206 AddTrayItem(new TrayBluetooth(this)); 207 AddTrayItem(new TrayDrive(this)); 208 AddTrayItem(new TrayUpdate(this)); 209 AddTrayItem(tray_date_); 210 #endif 211 212 SetVisible(ash::Shell::GetInstance()->system_tray_delegate()-> 213 GetTrayVisibilityOnStartup()); 214 } 215 216 void SystemTray::AddTrayItem(SystemTrayItem* item) { 217 items_.push_back(item); 218 219 SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate(); 220 views::View* tray_item = item->CreateTrayView(delegate->GetUserLoginStatus()); 221 item->UpdateAfterShelfAlignmentChange(shelf_alignment()); 222 223 if (tray_item) { 224 tray_container()->AddChildViewAt(tray_item, 0); 225 PreferredSizeChanged(); 226 tray_item_map_[item] = tray_item; 227 } 228 } 229 230 void SystemTray::RemoveTrayItem(SystemTrayItem* item) { 231 NOTIMPLEMENTED(); 232 } 233 234 const std::vector<SystemTrayItem*>& SystemTray::GetTrayItems() const { 235 return items_.get(); 236 } 237 238 void SystemTray::ShowDefaultView(BubbleCreationType creation_type) { 239 ShowDefaultViewWithOffset( 240 creation_type, 241 TrayBubbleView::InitParams::kArrowDefaultOffset, 242 false); 243 } 244 245 void SystemTray::ShowPersistentDefaultView() { 246 ShowItems(items_.get(), 247 false, 248 false, 249 BUBBLE_CREATE_NEW, 250 TrayBubbleView::InitParams::kArrowDefaultOffset, 251 true); 252 } 253 254 void SystemTray::ShowDetailedView(SystemTrayItem* item, 255 int close_delay, 256 bool activate, 257 BubbleCreationType creation_type) { 258 std::vector<SystemTrayItem*> items; 259 // The detailed view with timeout means a UI to show the current system state, 260 // like the audio level or brightness. Such UI should behave as persistent and 261 // keep its own logic for the appearance. 262 bool persistent = ( 263 !activate && close_delay > 0 && creation_type == BUBBLE_CREATE_NEW); 264 items.push_back(item); 265 ShowItems( 266 items, true, activate, creation_type, GetTrayXOffset(item), persistent); 267 if (system_bubble_) 268 system_bubble_->bubble()->StartAutoCloseTimer(close_delay); 269 } 270 271 void SystemTray::SetDetailedViewCloseDelay(int close_delay) { 272 if (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DETAILED)) 273 system_bubble_->bubble()->StartAutoCloseTimer(close_delay); 274 } 275 276 void SystemTray::HideDetailedView(SystemTrayItem* item) { 277 if (item != detailed_item_) 278 return; 279 DestroySystemBubble(); 280 UpdateNotificationBubble(); 281 } 282 283 void SystemTray::ShowNotificationView(SystemTrayItem* item) { 284 if (std::find(notification_items_.begin(), notification_items_.end(), item) 285 != notification_items_.end()) 286 return; 287 notification_items_.push_back(item); 288 UpdateNotificationBubble(); 289 } 290 291 void SystemTray::HideNotificationView(SystemTrayItem* item) { 292 std::vector<SystemTrayItem*>::iterator found_iter = 293 std::find(notification_items_.begin(), notification_items_.end(), item); 294 if (found_iter == notification_items_.end()) 295 return; 296 notification_items_.erase(found_iter); 297 // Only update the notification bubble if visible (i.e. don't create one). 298 if (notification_bubble_) 299 UpdateNotificationBubble(); 300 } 301 302 void SystemTray::UpdateAfterLoginStatusChange(user::LoginStatus login_status) { 303 DestroySystemBubble(); 304 UpdateNotificationBubble(); 305 306 for (std::vector<SystemTrayItem*>::iterator it = items_.begin(); 307 it != items_.end(); 308 ++it) { 309 (*it)->UpdateAfterLoginStatusChange(login_status); 310 } 311 312 // Items default to SHELF_ALIGNMENT_BOTTOM. Update them if the initial 313 // position of the shelf differs. 314 if (shelf_alignment() != SHELF_ALIGNMENT_BOTTOM) 315 UpdateAfterShelfAlignmentChange(shelf_alignment()); 316 317 SetVisible(true); 318 PreferredSizeChanged(); 319 } 320 321 void SystemTray::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) { 322 for (std::vector<SystemTrayItem*>::iterator it = items_.begin(); 323 it != items_.end(); 324 ++it) { 325 (*it)->UpdateAfterShelfAlignmentChange(alignment); 326 } 327 } 328 329 void SystemTray::SetHideNotifications(bool hide_notifications) { 330 if (notification_bubble_) 331 notification_bubble_->bubble()->SetVisible(!hide_notifications); 332 hide_notifications_ = hide_notifications; 333 } 334 335 bool SystemTray::ShouldShowShelf() const { 336 return system_bubble_.get() && system_bubble_->bubble()->ShouldShowShelf(); 337 } 338 339 bool SystemTray::HasSystemBubble() const { 340 return system_bubble_.get() != NULL; 341 } 342 343 bool SystemTray::HasNotificationBubble() const { 344 return notification_bubble_.get() != NULL; 345 } 346 347 SystemTrayBubble* SystemTray::GetSystemBubble() { 348 if (!system_bubble_) 349 return NULL; 350 return system_bubble_->bubble(); 351 } 352 353 bool SystemTray::IsAnyBubbleVisible() const { 354 return ((system_bubble_.get() && 355 system_bubble_->bubble()->IsVisible()) || 356 (notification_bubble_.get() && 357 notification_bubble_->bubble()->IsVisible())); 358 } 359 360 bool SystemTray::IsMouseInNotificationBubble() const { 361 if (!notification_bubble_) 362 return false; 363 return notification_bubble_->bubble_view()->GetBoundsInScreen().Contains( 364 Shell::GetScreen()->GetCursorScreenPoint()); 365 } 366 367 bool SystemTray::CloseSystemBubble() const { 368 if (!system_bubble_) 369 return false; 370 system_bubble_->bubble()->Close(); 371 return true; 372 } 373 374 views::View* SystemTray::GetHelpButtonView() const { 375 return tray_date_->GetHelpButtonView(); 376 } 377 378 bool SystemTray::CloseNotificationBubbleForTest() const { 379 if (!notification_bubble_) 380 return false; 381 notification_bubble_->bubble()->Close(); 382 return true; 383 } 384 385 // Private methods. 386 387 bool SystemTray::HasSystemBubbleType(SystemTrayBubble::BubbleType type) { 388 DCHECK(type != SystemTrayBubble::BUBBLE_TYPE_NOTIFICATION); 389 return system_bubble_.get() && system_bubble_->bubble_type() == type; 390 } 391 392 void SystemTray::DestroySystemBubble() { 393 CloseSystemBubbleAndDeactivateSystemTray(); 394 detailed_item_ = NULL; 395 UpdateWebNotifications(); 396 } 397 398 void SystemTray::DestroyNotificationBubble() { 399 if (notification_bubble_) { 400 notification_bubble_.reset(); 401 UpdateWebNotifications(); 402 } 403 } 404 405 int SystemTray::GetTrayXOffset(SystemTrayItem* item) const { 406 // Don't attempt to align the arrow if the shelf is on the left or right. 407 if (shelf_alignment() != SHELF_ALIGNMENT_BOTTOM && 408 shelf_alignment() != SHELF_ALIGNMENT_TOP) 409 return TrayBubbleView::InitParams::kArrowDefaultOffset; 410 411 std::map<SystemTrayItem*, views::View*>::const_iterator it = 412 tray_item_map_.find(item); 413 if (it == tray_item_map_.end()) 414 return TrayBubbleView::InitParams::kArrowDefaultOffset; 415 416 const views::View* item_view = it->second; 417 if (item_view->bounds().IsEmpty()) { 418 // The bounds of item could be still empty if it does not have a visible 419 // tray view. In that case, use the default (minimum) offset. 420 return TrayBubbleView::InitParams::kArrowDefaultOffset; 421 } 422 423 gfx::Point point(item_view->width() / 2, 0); 424 ConvertPointToWidget(item_view, &point); 425 return point.x(); 426 } 427 428 void SystemTray::ShowDefaultViewWithOffset(BubbleCreationType creation_type, 429 int arrow_offset, 430 bool persistent) { 431 if (creation_type != BUBBLE_USE_EXISTING) { 432 Shell::GetInstance()->metrics()->RecordUserMetricsAction( 433 ash::UMA_STATUS_AREA_MENU_OPENED); 434 } 435 ShowItems(items_.get(), false, true, creation_type, arrow_offset, persistent); 436 } 437 438 void SystemTray::ShowItems(const std::vector<SystemTrayItem*>& items, 439 bool detailed, 440 bool can_activate, 441 BubbleCreationType creation_type, 442 int arrow_offset, 443 bool persistent) { 444 // No system tray bubbles in kiosk mode. 445 if (Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus() == 446 ash::user::LOGGED_IN_KIOSK_APP) { 447 return; 448 } 449 450 // Destroy any existing bubble and create a new one. 451 SystemTrayBubble::BubbleType bubble_type = detailed ? 452 SystemTrayBubble::BUBBLE_TYPE_DETAILED : 453 SystemTrayBubble::BUBBLE_TYPE_DEFAULT; 454 455 // Destroy the notification bubble here so that it doesn't get rebuilt 456 // while we add items to the main bubble_ (e.g. in HideNotificationView). 457 notification_bubble_.reset(); 458 if (system_bubble_.get() && creation_type == BUBBLE_USE_EXISTING) { 459 system_bubble_->bubble()->UpdateView(items, bubble_type); 460 // If ChromeVox is enabled, focus the default item if no item is focused. 461 if (Shell::GetInstance()->accessibility_delegate()-> 462 IsSpokenFeedbackEnabled()) { 463 system_bubble_->bubble()->FocusDefaultIfNeeded(); 464 } 465 } else { 466 // Remember if the menu is a single property (like e.g. volume) or the 467 // full tray menu. Note that in case of the |BUBBLE_USE_EXISTING| case 468 // above, |full_system_tray_menu_| does not get changed since the fact that 469 // the menu is full (or not) doesn't change even if a "single property" 470 // (like network) replaces most of the menu. 471 full_system_tray_menu_ = items.size() > 1; 472 // The menu width is fixed, and it is a per language setting. 473 int menu_width = std::max(kMinimumSystemTrayMenuWidth, 474 Shell::GetInstance()->system_tray_delegate()->GetSystemTrayMenuWidth()); 475 476 TrayBubbleView::InitParams init_params(TrayBubbleView::ANCHOR_TYPE_TRAY, 477 GetAnchorAlignment(), 478 menu_width, 479 kTrayPopupMaxWidth); 480 init_params.can_activate = can_activate; 481 init_params.first_item_has_no_margin = true; 482 if (detailed) { 483 // This is the case where a volume control or brightness control bubble 484 // is created. 485 init_params.max_height = default_bubble_height_; 486 init_params.arrow_color = kBackgroundColor; 487 } else { 488 init_params.arrow_color = kHeaderBackgroundColor; 489 } 490 init_params.arrow_offset = arrow_offset; 491 if (bubble_type == SystemTrayBubble::BUBBLE_TYPE_DEFAULT) 492 init_params.close_on_deactivate = !persistent; 493 // For Volume and Brightness we don't want to show an arrow when 494 // they are shown in a bubble by themselves. 495 init_params.arrow_paint_type = views::BubbleBorder::PAINT_NORMAL; 496 if (items.size() == 1 && items[0]->ShouldHideArrow()) 497 init_params.arrow_paint_type = views::BubbleBorder::PAINT_TRANSPARENT; 498 SystemTrayBubble* bubble = new SystemTrayBubble(this, items, bubble_type); 499 system_bubble_.reset(new SystemBubbleWrapper(bubble)); 500 system_bubble_->InitView(this, tray_container(), &init_params, persistent); 501 } 502 // Save height of default view for creating detailed views directly. 503 if (!detailed) 504 default_bubble_height_ = system_bubble_->bubble_view()->height(); 505 506 if (detailed && items.size() > 0) 507 detailed_item_ = items[0]; 508 else 509 detailed_item_ = NULL; 510 511 UpdateNotificationBubble(); // State changed, re-create notifications. 512 if (!notification_bubble_) 513 UpdateWebNotifications(); 514 GetShelfLayoutManager()->UpdateAutoHideState(); 515 516 // When we show the system menu in our alternate shelf layout, we need to 517 // tint the background. 518 if (full_system_tray_menu_) 519 SetDrawBackgroundAsActive(true); 520 } 521 522 void SystemTray::UpdateNotificationBubble() { 523 // Only show the notification bubble if we have notifications. 524 if (notification_items_.empty()) { 525 DestroyNotificationBubble(); 526 return; 527 } 528 // Destroy the existing bubble before constructing a new one. 529 notification_bubble_.reset(); 530 SystemTrayBubble* notification_bubble; 531 notification_bubble = new SystemTrayBubble( 532 this, notification_items_, SystemTrayBubble::BUBBLE_TYPE_NOTIFICATION); 533 views::View* anchor; 534 TrayBubbleView::AnchorType anchor_type; 535 // Tray items might want to show notifications while we are creating and 536 // initializing the |system_bubble_| - but it might not be fully initialized 537 // when coming here - this would produce a crashed like crbug.com/247416. 538 // As such we check the existence of the widget here. 539 if (system_bubble_.get() && 540 system_bubble_->bubble_view() && 541 system_bubble_->bubble_view()->GetWidget()) { 542 anchor = system_bubble_->bubble_view(); 543 anchor_type = TrayBubbleView::ANCHOR_TYPE_BUBBLE; 544 } else { 545 anchor = tray_container(); 546 anchor_type = TrayBubbleView::ANCHOR_TYPE_TRAY; 547 } 548 TrayBubbleView::InitParams init_params(anchor_type, 549 GetAnchorAlignment(), 550 kTrayPopupMinWidth, 551 kTrayPopupMaxWidth); 552 init_params.first_item_has_no_margin = true; 553 init_params.arrow_color = kBackgroundColor; 554 init_params.arrow_offset = GetTrayXOffset(notification_items_[0]); 555 notification_bubble_.reset(new SystemBubbleWrapper(notification_bubble)); 556 notification_bubble_->InitView(this, anchor, &init_params, false); 557 558 if (notification_bubble->bubble_view()->child_count() == 0) { 559 // It is possible that none of the items generated actual notifications. 560 DestroyNotificationBubble(); 561 return; 562 } 563 if (hide_notifications_) 564 notification_bubble->SetVisible(false); 565 else 566 UpdateWebNotifications(); 567 } 568 569 void SystemTray::UpdateWebNotifications() { 570 TrayBubbleView* bubble_view = NULL; 571 if (notification_bubble_) 572 bubble_view = notification_bubble_->bubble_view(); 573 else if (system_bubble_) 574 bubble_view = system_bubble_->bubble_view(); 575 576 int height = 0; 577 if (bubble_view) { 578 gfx::Rect work_area = Shell::GetScreen()->GetDisplayNearestWindow( 579 bubble_view->GetWidget()->GetNativeView()).work_area(); 580 if (GetShelfLayoutManager()->GetAlignment() != SHELF_ALIGNMENT_TOP) { 581 height = std::max( 582 0, work_area.height() - bubble_view->GetBoundsInScreen().y()); 583 } else { 584 height = std::max( 585 0, bubble_view->GetBoundsInScreen().bottom() - work_area.y()); 586 } 587 } 588 status_area_widget()->web_notification_tray()->SetSystemTrayHeight(height); 589 } 590 591 void SystemTray::SetShelfAlignment(ShelfAlignment alignment) { 592 if (alignment == shelf_alignment()) 593 return; 594 TrayBackgroundView::SetShelfAlignment(alignment); 595 UpdateAfterShelfAlignmentChange(alignment); 596 // Destroy any existing bubble so that it is rebuilt correctly. 597 CloseSystemBubbleAndDeactivateSystemTray(); 598 // Rebuild any notification bubble. 599 if (notification_bubble_) { 600 notification_bubble_.reset(); 601 UpdateNotificationBubble(); 602 } 603 } 604 605 void SystemTray::AnchorUpdated() { 606 if (notification_bubble_) { 607 notification_bubble_->bubble_view()->UpdateBubble(); 608 // Ensure that the notification buble is above the shelf/status area. 609 notification_bubble_->bubble_view()->GetWidget()->StackAtTop(); 610 UpdateBubbleViewArrow(notification_bubble_->bubble_view()); 611 } 612 if (system_bubble_) { 613 system_bubble_->bubble_view()->UpdateBubble(); 614 UpdateBubbleViewArrow(system_bubble_->bubble_view()); 615 } 616 } 617 618 base::string16 SystemTray::GetAccessibleNameForTray() { 619 return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBLE_NAME); 620 } 621 622 void SystemTray::BubbleResized(const TrayBubbleView* bubble_view) { 623 UpdateWebNotifications(); 624 } 625 626 void SystemTray::HideBubbleWithView(const TrayBubbleView* bubble_view) { 627 if (system_bubble_.get() && bubble_view == system_bubble_->bubble_view()) { 628 DestroySystemBubble(); 629 UpdateNotificationBubble(); // State changed, re-create notifications. 630 GetShelfLayoutManager()->UpdateAutoHideState(); 631 } else if (notification_bubble_.get() && 632 bubble_view == notification_bubble_->bubble_view()) { 633 DestroyNotificationBubble(); 634 } 635 } 636 637 bool SystemTray::ClickedOutsideBubble() { 638 if (!system_bubble_ || system_bubble_->is_persistent()) 639 return false; 640 HideBubbleWithView(system_bubble_->bubble_view()); 641 return true; 642 } 643 644 void SystemTray::BubbleViewDestroyed() { 645 if (system_bubble_) { 646 system_bubble_->bubble()->DestroyItemViews(); 647 system_bubble_->bubble()->BubbleViewDestroyed(); 648 } 649 } 650 651 void SystemTray::OnMouseEnteredView() { 652 if (system_bubble_) 653 system_bubble_->bubble()->StopAutoCloseTimer(); 654 } 655 656 void SystemTray::OnMouseExitedView() { 657 if (system_bubble_) 658 system_bubble_->bubble()->RestartAutoCloseTimer(); 659 } 660 661 base::string16 SystemTray::GetAccessibleNameForBubble() { 662 return GetAccessibleNameForTray(); 663 } 664 665 gfx::Rect SystemTray::GetAnchorRect( 666 views::Widget* anchor_widget, 667 TrayBubbleView::AnchorType anchor_type, 668 TrayBubbleView::AnchorAlignment anchor_alignment) const { 669 return GetBubbleAnchorRect(anchor_widget, anchor_type, anchor_alignment); 670 } 671 672 void SystemTray::HideBubble(const TrayBubbleView* bubble_view) { 673 HideBubbleWithView(bubble_view); 674 } 675 676 views::View* SystemTray::GetTrayItemViewForTest(SystemTrayItem* item) { 677 std::map<SystemTrayItem*, views::View*>::iterator it = 678 tray_item_map_.find(item); 679 return it == tray_item_map_.end() ? NULL : it->second; 680 } 681 682 TrayDate* SystemTray::GetTrayDateForTesting() const { return tray_date_; } 683 684 bool SystemTray::PerformAction(const ui::Event& event) { 685 // If we're already showing the default view, hide it; otherwise, show it 686 // (and hide any popup that's currently shown). 687 if (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DEFAULT)) { 688 system_bubble_->bubble()->Close(); 689 } else { 690 int arrow_offset = TrayBubbleView::InitParams::kArrowDefaultOffset; 691 if (event.IsMouseEvent() || event.type() == ui::ET_GESTURE_TAP) { 692 const ui::LocatedEvent& located_event = 693 static_cast<const ui::LocatedEvent&>(event); 694 if (shelf_alignment() == SHELF_ALIGNMENT_BOTTOM || 695 shelf_alignment() == SHELF_ALIGNMENT_TOP) { 696 gfx::Point point(located_event.x(), 0); 697 ConvertPointToWidget(this, &point); 698 arrow_offset = point.x(); 699 } 700 } 701 ShowDefaultViewWithOffset(BUBBLE_CREATE_NEW, arrow_offset, false); 702 } 703 return true; 704 } 705 706 void SystemTray::CloseSystemBubbleAndDeactivateSystemTray() { 707 system_bubble_.reset(); 708 // When closing a system bubble with the alternate shelf layout, we need to 709 // turn off the active tinting of the shelf. 710 if (full_system_tray_menu_) { 711 SetDrawBackgroundAsActive(false); 712 full_system_tray_menu_ = false; 713 } 714 } 715 716 } // namespace ash 717