status_area_widget.cc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668
  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. #include "ash/system/status_area_widget.h"
  5. #include "ash/capture_mode/stop_recording_button_tray.h"
  6. #include "ash/constants/ash_features.h"
  7. #include "ash/constants/ash_switches.h"
  8. #include "ash/keyboard/ui/keyboard_ui_controller.h"
  9. #include "ash/projector/projector_annotation_tray.h"
  10. #include "ash/public/cpp/shelf_config.h"
  11. #include "ash/session/session_controller_impl.h"
  12. #include "ash/shelf/shelf.h"
  13. #include "ash/shelf/shelf_layout_manager.h"
  14. #include "ash/shelf/shelf_widget.h"
  15. #include "ash/shell.h"
  16. #include "ash/system/accessibility/dictation_button_tray.h"
  17. #include "ash/system/accessibility/select_to_speak/select_to_speak_tray.h"
  18. #include "ash/system/eche/eche_tray.h"
  19. #include "ash/system/holding_space/holding_space_tray.h"
  20. #include "ash/system/ime_menu/ime_menu_tray.h"
  21. #include "ash/system/media/media_tray.h"
  22. #include "ash/system/model/clock_model.h"
  23. #include "ash/system/model/system_tray_model.h"
  24. #include "ash/system/overview/overview_button_tray.h"
  25. #include "ash/system/palette/palette_tray.h"
  26. #include "ash/system/phonehub/phone_hub_tray.h"
  27. #include "ash/system/session/logout_button_tray.h"
  28. #include "ash/system/status_area_widget_delegate.h"
  29. #include "ash/system/tray/status_area_overflow_button_tray.h"
  30. #include "ash/system/tray/tray_background_view.h"
  31. #include "ash/system/tray/tray_constants.h"
  32. #include "ash/system/tray/tray_container.h"
  33. #include "ash/system/unified/date_tray.h"
  34. #include "ash/system/unified/unified_system_tray.h"
  35. #include "ash/system/virtual_keyboard/virtual_keyboard_tray.h"
  36. #include "ash/wm/tablet_mode/tablet_mode_controller.h"
  37. #include "base/command_line.h"
  38. #include "base/containers/adapters.h"
  39. #include "base/i18n/time_formatting.h"
  40. #include "base/metrics/histogram_macros.h"
  41. #include "chromeos/ash/services/assistant/public/cpp/features.h"
  42. #include "media/base/media_switches.h"
  43. #include "ui/compositor/layer.h"
  44. #include "ui/compositor/scoped_layer_animation_settings.h"
  45. #include "ui/display/display.h"
  46. namespace ash {
  47. ////////////////////////////////////////////////////////////////////////////////
  48. // StatusAreaWidget::ScopedTrayBubbleCounter
  49. StatusAreaWidget::ScopedTrayBubbleCounter::ScopedTrayBubbleCounter(
  50. StatusAreaWidget* status_area_widget)
  51. : status_area_widget_(status_area_widget->weak_ptr_factory_.GetWeakPtr()) {
  52. if (status_area_widget_->tray_bubble_count_ == 0) {
  53. status_area_widget_->shelf()
  54. ->shelf_layout_manager()
  55. ->OnShelfTrayBubbleVisibilityChanged(/*bubble_shown=*/true);
  56. }
  57. ++status_area_widget_->tray_bubble_count_;
  58. }
  59. StatusAreaWidget::ScopedTrayBubbleCounter::~ScopedTrayBubbleCounter() {
  60. // ScopedTrayBubbleCounter may live longer than StatusAreaWidget.
  61. if (!status_area_widget_)
  62. return;
  63. --status_area_widget_->tray_bubble_count_;
  64. if (status_area_widget_->tray_bubble_count_ == 0) {
  65. status_area_widget_->shelf()
  66. ->shelf_layout_manager()
  67. ->OnShelfTrayBubbleVisibilityChanged(/*bubble_shown=*/false);
  68. }
  69. DCHECK_GE(status_area_widget_->tray_bubble_count_, 0);
  70. }
  71. ////////////////////////////////////////////////////////////////////////////////
  72. // StatusAreaWidget
  73. StatusAreaWidget::StatusAreaWidget(aura::Window* status_container, Shelf* shelf)
  74. : status_area_widget_delegate_(new StatusAreaWidgetDelegate(shelf)),
  75. shelf_(shelf) {
  76. DCHECK(status_container);
  77. DCHECK(shelf);
  78. views::Widget::InitParams params(
  79. views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
  80. params.delegate = status_area_widget_delegate_;
  81. params.name = "StatusAreaWidget";
  82. params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
  83. params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  84. params.parent = status_container;
  85. Init(std::move(params));
  86. set_focus_on_creation(false);
  87. SetContentsView(status_area_widget_delegate_);
  88. }
  89. void StatusAreaWidget::Initialize() {
  90. DCHECK(!initialized_);
  91. // Create the child views, left to right.
  92. overflow_button_tray_ =
  93. AddTrayButton(std::make_unique<StatusAreaOverflowButtonTray>(shelf_));
  94. holding_space_tray_ =
  95. AddTrayButton(std::make_unique<HoldingSpaceTray>(shelf_));
  96. logout_button_tray_ =
  97. AddTrayButton(std::make_unique<LogoutButtonTray>(shelf_));
  98. dictation_button_tray_ =
  99. AddTrayButton(std::make_unique<DictationButtonTray>(shelf_));
  100. select_to_speak_tray_ =
  101. AddTrayButton(std::make_unique<SelectToSpeakTray>(shelf_));
  102. ime_menu_tray_ = AddTrayButton(std::make_unique<ImeMenuTray>(shelf_));
  103. virtual_keyboard_tray_ =
  104. AddTrayButton(std::make_unique<VirtualKeyboardTray>(shelf_));
  105. stop_recording_button_tray_ =
  106. AddTrayButton(std::make_unique<StopRecordingButtonTray>(shelf_));
  107. if (features::IsProjectorAnnotatorEnabled()) {
  108. projector_annotation_tray_ =
  109. AddTrayButton(std::make_unique<ProjectorAnnotationTray>(shelf_));
  110. }
  111. palette_tray_ = AddTrayButton(std::make_unique<PaletteTray>(shelf_));
  112. if (base::FeatureList::IsEnabled(media::kGlobalMediaControlsForChromeOS)) {
  113. media_tray_ = AddTrayButton(std::make_unique<MediaTray>(shelf_));
  114. }
  115. if (chromeos::features::IsEcheSWAEnabled()) {
  116. eche_tray_ = AddTrayButton(std::make_unique<EcheTray>(shelf_));
  117. }
  118. if (chromeos::features::IsPhoneHubEnabled()) {
  119. phone_hub_tray_ = AddTrayButton(std::make_unique<PhoneHubTray>(shelf_));
  120. }
  121. auto unified_system_tray = std::make_unique<UnifiedSystemTray>(shelf_);
  122. unified_system_tray_ = unified_system_tray.get();
  123. if (features::IsCalendarViewEnabled()) {
  124. date_tray_ =
  125. AddTrayButton(std::make_unique<DateTray>(shelf_, unified_system_tray_));
  126. }
  127. AddTrayButton(std::move(unified_system_tray));
  128. overview_button_tray_ =
  129. AddTrayButton(std::make_unique<OverviewButtonTray>(shelf_));
  130. // Each tray_button's animation will be disabled for the life time of this
  131. // local `animation_disablers`, which means the closures to enable the
  132. // animation will be executed when it's out of this method (Initialize())
  133. // scope.
  134. std::list<base::ScopedClosureRunner> animation_disablers;
  135. // Initialize after all trays have been created.
  136. for (TrayBackgroundView* tray_button : tray_buttons_) {
  137. tray_button->Initialize();
  138. animation_disablers.push_back(tray_button->DisableShowAnimation());
  139. }
  140. EnsureTrayOrder();
  141. UpdateAfterLoginStatusChange(
  142. Shell::Get()->session_controller()->login_status());
  143. UpdateLayout(/*animate=*/false);
  144. Shell::Get()->session_controller()->AddObserver(this);
  145. // NOTE: Container may be hidden depending on login/display state.
  146. Show();
  147. initialized_ = true;
  148. }
  149. StatusAreaWidget::~StatusAreaWidget() {
  150. Shell::Get()->session_controller()->RemoveObserver(this);
  151. status_area_widget_delegate_->Shutdown();
  152. }
  153. // static
  154. StatusAreaWidget* StatusAreaWidget::ForWindow(aura::Window* window) {
  155. return Shelf::ForWindow(window)->status_area_widget();
  156. }
  157. void StatusAreaWidget::UpdateAfterLoginStatusChange(LoginStatus login_status) {
  158. if (login_status_ == login_status)
  159. return;
  160. login_status_ = login_status;
  161. for (TrayBackgroundView* tray_button : tray_buttons_)
  162. tray_button->UpdateAfterLoginStatusChange();
  163. }
  164. void StatusAreaWidget::SetSystemTrayVisibility(bool visible) {
  165. TrayBackgroundView* tray = unified_system_tray_;
  166. tray->SetVisiblePreferred(visible);
  167. if (features::IsCalendarViewEnabled())
  168. date_tray_->SetVisiblePreferred(visible);
  169. if (visible) {
  170. Show();
  171. } else {
  172. tray->CloseBubble();
  173. Hide();
  174. }
  175. }
  176. void StatusAreaWidget::OnSessionStateChanged(
  177. session_manager::SessionState state) {
  178. for (TrayBackgroundView* tray_button : tray_buttons_)
  179. tray_button->UpdateBackground();
  180. }
  181. void StatusAreaWidget::UpdateCollapseState() {
  182. collapse_state_ = CalculateCollapseState();
  183. if (collapse_state_ == CollapseState::COLLAPSED) {
  184. CalculateButtonVisibilityForCollapsedState();
  185. } else {
  186. // All tray buttons are visible when the status area is not collapsible.
  187. // This is the most common state. They are also all visible when the status
  188. // area is expanded by the user.
  189. overflow_button_tray_->SetVisiblePreferred(collapse_state_ ==
  190. CollapseState::EXPANDED);
  191. for (TrayBackgroundView* tray_button : tray_buttons_) {
  192. tray_button->set_show_when_collapsed(true);
  193. tray_button->UpdateAfterStatusAreaCollapseChange();
  194. }
  195. }
  196. status_area_widget_delegate_->OnStatusAreaCollapseStateChanged(
  197. collapse_state_);
  198. }
  199. void StatusAreaWidget::LogVisiblePodCountMetric() {
  200. int visible_pod_count = 0;
  201. for (auto* tray_button : tray_buttons_) {
  202. if (tray_button == overflow_button_tray_ ||
  203. tray_button == overview_button_tray_ ||
  204. tray_button == unified_system_tray_ || tray_button == date_tray_ ||
  205. !tray_button->GetVisible()) {
  206. continue;
  207. }
  208. visible_pod_count += 1;
  209. }
  210. if (Shell::Get()->tablet_mode_controller()->InTabletMode()) {
  211. UMA_HISTOGRAM_COUNTS_100("ChromeOS.SystemTray.Tablet.ShelfPodCount",
  212. visible_pod_count);
  213. } else {
  214. UMA_HISTOGRAM_COUNTS_100("ChromeOS.SystemTray.ShelfPodCount",
  215. visible_pod_count);
  216. }
  217. }
  218. void StatusAreaWidget::CalculateTargetBounds() {
  219. for (TrayBackgroundView* tray_button : tray_buttons_)
  220. tray_button->CalculateTargetBounds();
  221. status_area_widget_delegate_->CalculateTargetBounds();
  222. gfx::Size status_size(status_area_widget_delegate_->GetTargetBounds().size());
  223. const gfx::Size shelf_size = shelf_->shelf_widget()->GetTargetBounds().size();
  224. const gfx::Point shelf_origin =
  225. shelf_->shelf_widget()->GetTargetBounds().origin();
  226. if (shelf_->IsHorizontalAlignment())
  227. status_size.set_height(shelf_size.height());
  228. else
  229. status_size.set_width(shelf_size.width());
  230. gfx::Point status_origin = shelf_->SelectValueForShelfAlignment(
  231. gfx::Point(0, 0),
  232. gfx::Point(shelf_size.width() - status_size.width(),
  233. shelf_size.height() - status_size.height()),
  234. gfx::Point(0, shelf_size.height() - status_size.height()));
  235. if (shelf_->IsHorizontalAlignment() && !base::i18n::IsRTL())
  236. status_origin.set_x(shelf_size.width() - status_size.width());
  237. status_origin.Offset(shelf_origin.x(), shelf_origin.y());
  238. target_bounds_ = gfx::Rect(status_origin, status_size);
  239. }
  240. gfx::Rect StatusAreaWidget::GetTargetBounds() const {
  241. return target_bounds_;
  242. }
  243. void StatusAreaWidget::UpdateLayout(bool animate) {
  244. const LayoutInputs new_layout_inputs = GetLayoutInputs();
  245. if (layout_inputs_ == new_layout_inputs)
  246. return;
  247. if (!new_layout_inputs.should_animate)
  248. animate = false;
  249. for (TrayBackgroundView* tray_button : tray_buttons_)
  250. tray_button->UpdateLayout();
  251. status_area_widget_delegate_->UpdateLayout(animate);
  252. // Having a window which is visible but does not have an opacity is an
  253. // illegal state.
  254. ui::Layer* layer = GetNativeView()->layer();
  255. layer->SetOpacity(new_layout_inputs.opacity);
  256. if (new_layout_inputs.opacity)
  257. ShowInactive();
  258. else
  259. Hide();
  260. ui::ScopedLayerAnimationSettings animation_setter(layer->GetAnimator());
  261. animation_setter.SetTransitionDuration(
  262. animate ? ShelfConfig::Get()->shelf_animation_duration()
  263. : base::Milliseconds(0));
  264. animation_setter.SetTweenType(gfx::Tween::EASE_OUT);
  265. animation_setter.SetPreemptionStrategy(
  266. ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
  267. SetBounds(new_layout_inputs.bounds);
  268. layout_inputs_ = new_layout_inputs;
  269. }
  270. void StatusAreaWidget::UpdateTargetBoundsForGesture(int shelf_position) {
  271. if (shelf_->IsHorizontalAlignment())
  272. target_bounds_.set_y(shelf_position);
  273. else
  274. target_bounds_.set_x(shelf_position);
  275. }
  276. void StatusAreaWidget::HandleLocaleChange() {
  277. // Here we force the layer's bounds to be updated for text direction (if
  278. // needed).
  279. status_area_widget_delegate_->RemoveAllChildViewsWithoutDeleting();
  280. for (auto* tray_button : tray_buttons_) {
  281. tray_button->HandleLocaleChange();
  282. status_area_widget_delegate_->AddChildView(tray_button);
  283. }
  284. EnsureTrayOrder();
  285. }
  286. void StatusAreaWidget::NotifyAnyBubbleVisibilityChanged(
  287. views::Widget* bubble_widget,
  288. bool visible) {
  289. for (auto* tray_button : tray_buttons_) {
  290. tray_button->OnAnyBubbleVisibilityChanged(bubble_widget, visible);
  291. }
  292. }
  293. void StatusAreaWidget::CalculateButtonVisibilityForCollapsedState() {
  294. if (!initialized_)
  295. return;
  296. DCHECK(collapse_state_ == CollapseState::COLLAPSED);
  297. bool force_collapsible = base::CommandLine::ForCurrentProcess()->HasSwitch(
  298. switches::kAshForceStatusAreaCollapsible);
  299. // If |stop_recording_button_tray_| is visible, make some space in tray for
  300. // it.
  301. const int stop_recording_button_width =
  302. stop_recording_button_tray_->visible_preferred()
  303. ? stop_recording_button_tray_->GetPreferredSize().width()
  304. : 0;
  305. // We update visibility of each tray button based on the available width.
  306. const int available_width = GetCollapseAvailableWidth(force_collapsible) -
  307. stop_recording_button_width;
  308. // First, reset all tray button to be hidden.
  309. overflow_button_tray_->ResetStateToCollapsed();
  310. for (TrayBackgroundView* tray_button : tray_buttons_)
  311. tray_button->set_show_when_collapsed(false);
  312. // Iterate backwards making tray buttons visible until |available_width| is
  313. // exceeded.
  314. TrayBackgroundView* previous_tray = nullptr;
  315. bool show_overflow_button = false;
  316. int used_width = 0;
  317. for (TrayBackgroundView* tray : base::Reversed(tray_buttons_)) {
  318. // Skip non-enabled tray buttons.
  319. if (!tray->visible_preferred())
  320. continue;
  321. // Skip |stop_recording_button_tray_| since it's always visible.
  322. if (tray == stop_recording_button_tray_)
  323. continue;
  324. // Show overflow button once available width is exceeded.
  325. int tray_width = tray->tray_container()->GetPreferredSize().width();
  326. if (used_width + tray_width > available_width) {
  327. show_overflow_button = true;
  328. // Maybe remove the last tray button to make room for the overflow tray.
  329. int overflow_button_width =
  330. overflow_button_tray_->GetPreferredSize().width();
  331. if (previous_tray && used_width + overflow_button_width > available_width)
  332. previous_tray->set_show_when_collapsed(false);
  333. break;
  334. }
  335. tray->set_show_when_collapsed(true);
  336. previous_tray = tray;
  337. used_width += tray_width;
  338. }
  339. // Skip |stop_recording_button_tray_| so it's always visible.
  340. if (stop_recording_button_tray_->visible_preferred())
  341. stop_recording_button_tray_->set_show_when_collapsed(true);
  342. overflow_button_tray_->SetVisiblePreferred(show_overflow_button);
  343. overflow_button_tray_->UpdateAfterStatusAreaCollapseChange();
  344. for (TrayBackgroundView* tray_button : tray_buttons_)
  345. tray_button->UpdateAfterStatusAreaCollapseChange();
  346. }
  347. void StatusAreaWidget::EnsureTrayOrder() {
  348. if (projector_annotation_tray_) {
  349. status_area_widget_delegate_->ReorderChildView(projector_annotation_tray_,
  350. 1);
  351. }
  352. status_area_widget_delegate_->ReorderChildView(
  353. stop_recording_button_tray_, projector_annotation_tray_ ? 2 : 1);
  354. }
  355. StatusAreaWidget::CollapseState StatusAreaWidget::CalculateCollapseState()
  356. const {
  357. // The status area is only collapsible in tablet mode. Otherwise, we just show
  358. // all trays.
  359. if (!Shell::Get()->tablet_mode_controller())
  360. return CollapseState::NOT_COLLAPSIBLE;
  361. // An update may occur during initialization of the shelf, so just skip it.
  362. if (!initialized_)
  363. return CollapseState::NOT_COLLAPSIBLE;
  364. bool is_collapsible =
  365. Shell::Get()->tablet_mode_controller()->InTabletMode() &&
  366. ShelfConfig::Get()->is_in_app();
  367. bool force_collapsible = base::CommandLine::ForCurrentProcess()->HasSwitch(
  368. switches::kAshForceStatusAreaCollapsible);
  369. is_collapsible |= force_collapsible;
  370. CollapseState state = CollapseState::NOT_COLLAPSIBLE;
  371. if (is_collapsible) {
  372. // Update the collapse state based on the previous overflow button state.
  373. state = overflow_button_tray_->state() ==
  374. StatusAreaOverflowButtonTray::CLICK_TO_EXPAND
  375. ? CollapseState::COLLAPSED
  376. : CollapseState::EXPANDED;
  377. } else {
  378. state = CollapseState::NOT_COLLAPSIBLE;
  379. }
  380. if (state == CollapseState::COLLAPSED) {
  381. // We might not need to be collapsed, if there is enough space for all the
  382. // buttons.
  383. const int available_width = GetCollapseAvailableWidth(force_collapsible);
  384. int used_width = 0;
  385. for (TrayBackgroundView* tray : base::Reversed(tray_buttons_)) {
  386. // If we reach the final overflow tray button, then all the tray buttons
  387. // fit and there is no need for a collapse state.
  388. if (tray == overflow_button_tray_)
  389. return CollapseState::NOT_COLLAPSIBLE;
  390. // Skip non-enabled tray buttons.
  391. if (!tray->visible_preferred())
  392. continue;
  393. int tray_width = tray->tray_container()->GetPreferredSize().width();
  394. if (used_width + tray_width > available_width)
  395. break;
  396. used_width += tray_width;
  397. }
  398. }
  399. return state;
  400. }
  401. TrayBackgroundView* StatusAreaWidget::GetSystemTrayAnchor() const {
  402. // Use the target visibility of the layer instead of the visibility of the
  403. // view because the view is still visible when fading away, but we do not want
  404. // to anchor to this element in that case.
  405. if (overview_button_tray_->layer()->GetTargetVisibility())
  406. return overview_button_tray_;
  407. return unified_system_tray_;
  408. }
  409. gfx::Rect StatusAreaWidget::GetMediaTrayAnchorRect() const {
  410. if (!media_tray_)
  411. return gfx::Rect();
  412. // Calculate anchor rect of media tray bubble. This is required because the
  413. // bubble can be visible while the tray button is hidden. (e.g. when user
  414. // clicks the unpin button in the dialog, which will not close the dialog)
  415. bool found_media_tray = false;
  416. int offset = 0;
  417. // Accumulate the width/height of all visible tray buttons after media tray.
  418. for (views::View* tray_button : tray_buttons_) {
  419. if (tray_button == media_tray_) {
  420. found_media_tray = true;
  421. continue;
  422. }
  423. if (!found_media_tray || !tray_button->GetVisible())
  424. continue;
  425. offset += shelf_->IsHorizontalAlignment() ? tray_button->width()
  426. : tray_button->height();
  427. }
  428. // Use system tray anchor view (system tray or overview button tray if
  429. // visible) to find media tray button's origin.
  430. gfx::Rect system_tray_bounds = GetSystemTrayAnchor()->GetBoundsInScreen();
  431. switch (shelf_->alignment()) {
  432. case ShelfAlignment::kBottom:
  433. case ShelfAlignment::kBottomLocked:
  434. if (base::i18n::IsRTL()) {
  435. return gfx::Rect(system_tray_bounds.origin() + gfx::Vector2d(offset, 0),
  436. gfx::Size());
  437. } else {
  438. return gfx::Rect(
  439. system_tray_bounds.top_right() - gfx::Vector2d(offset, 0),
  440. gfx::Size());
  441. }
  442. case ShelfAlignment::kLeft:
  443. return gfx::Rect(
  444. system_tray_bounds.bottom_right() - gfx::Vector2d(0, offset),
  445. gfx::Size());
  446. case ShelfAlignment::kRight:
  447. return gfx::Rect(
  448. system_tray_bounds.bottom_left() - gfx::Vector2d(0, offset),
  449. gfx::Size());
  450. }
  451. NOTREACHED();
  452. return gfx::Rect();
  453. }
  454. bool StatusAreaWidget::ShouldShowShelf() const {
  455. // If it has main bubble, return true.
  456. if (unified_system_tray_->IsBubbleShown())
  457. return true;
  458. // If any tray is showing a context menu, the shelf should be visible.
  459. for (TrayBackgroundView* tray_button : tray_buttons_) {
  460. if (tray_button->IsShowingMenu())
  461. return true;
  462. }
  463. // If it has a slider bubble, return false.
  464. if (unified_system_tray_->IsSliderBubbleShown())
  465. return false;
  466. // All other tray bubbles on the same display with status area widget will
  467. // force the shelf to be visible.
  468. return tray_bubble_count_ > 0;
  469. }
  470. bool StatusAreaWidget::IsMessageBubbleShown() const {
  471. return unified_system_tray_->IsBubbleShown();
  472. }
  473. void StatusAreaWidget::SchedulePaint() {
  474. for (TrayBackgroundView* tray_button : tray_buttons_)
  475. tray_button->SchedulePaint();
  476. }
  477. bool StatusAreaWidget::OnNativeWidgetActivationChanged(bool active) {
  478. if (!Widget::OnNativeWidgetActivationChanged(active))
  479. return false;
  480. if (active)
  481. status_area_widget_delegate_->SetPaneFocusAndFocusDefault();
  482. return true;
  483. }
  484. void StatusAreaWidget::OnMouseEvent(ui::MouseEvent* event) {
  485. if (event->IsMouseWheelEvent()) {
  486. ui::MouseWheelEvent* mouse_wheel_event = event->AsMouseWheelEvent();
  487. shelf_->ProcessMouseWheelEvent(mouse_wheel_event);
  488. return;
  489. }
  490. // Clicking anywhere except the virtual keyboard tray icon should hide the
  491. // virtual keyboard.
  492. gfx::Point location = event->location();
  493. views::View::ConvertPointFromWidget(virtual_keyboard_tray_, &location);
  494. if (event->type() == ui::ET_MOUSE_PRESSED &&
  495. !virtual_keyboard_tray_->HitTestPoint(location)) {
  496. keyboard::KeyboardUIController::Get()->HideKeyboardImplicitlyByUser();
  497. }
  498. views::Widget::OnMouseEvent(event);
  499. }
  500. void StatusAreaWidget::OnGestureEvent(ui::GestureEvent* event) {
  501. // Tapping anywhere except the virtual keyboard tray icon should hide the
  502. // virtual keyboard.
  503. gfx::Point location = event->location();
  504. views::View::ConvertPointFromWidget(virtual_keyboard_tray_, &location);
  505. if (event->type() == ui::ET_GESTURE_TAP_DOWN &&
  506. !virtual_keyboard_tray_->HitTestPoint(location)) {
  507. keyboard::KeyboardUIController::Get()->HideKeyboardImplicitlyByUser();
  508. }
  509. views::Widget::OnGestureEvent(event);
  510. }
  511. void StatusAreaWidget::OnScrollEvent(ui::ScrollEvent* event) {
  512. shelf_->ProcessScrollEvent(event);
  513. if (!event->handled())
  514. views::Widget::OnScrollEvent(event);
  515. }
  516. template <typename TrayButtonT>
  517. TrayButtonT* StatusAreaWidget::AddTrayButton(
  518. std::unique_ptr<TrayButtonT> tray_button) {
  519. tray_buttons_.push_back(tray_button.get());
  520. return status_area_widget_delegate_->AddChildView(std::move(tray_button));
  521. }
  522. // Specialization declared here for use in tests.
  523. template TrayBackgroundView* StatusAreaWidget::AddTrayButton<
  524. TrayBackgroundView>(std::unique_ptr<TrayBackgroundView> tray_button);
  525. StatusAreaWidget::LayoutInputs StatusAreaWidget::GetLayoutInputs() const {
  526. unsigned int child_visibility_bitmask = 0;
  527. DCHECK(tray_buttons_.size() <
  528. std::numeric_limits<decltype(child_visibility_bitmask)>::digits);
  529. for (unsigned int i = 0; i < tray_buttons_.size(); ++i) {
  530. if (tray_buttons_[i]->GetVisible())
  531. child_visibility_bitmask |= 1 << i;
  532. }
  533. bool should_animate = true;
  534. // Do not animate when tray items are added and removed (See
  535. // crbug.com/1067199).
  536. if (layout_inputs_) {
  537. const bool is_horizontal_alignment = shelf_->IsHorizontalAlignment();
  538. const gfx::Rect current_bounds = layout_inputs_->bounds;
  539. if ((is_horizontal_alignment &&
  540. current_bounds.width() != target_bounds_.width()) ||
  541. (!is_horizontal_alignment &&
  542. current_bounds.height() != target_bounds_.height())) {
  543. should_animate = false;
  544. }
  545. }
  546. return {target_bounds_, CalculateCollapseState(),
  547. shelf_->shelf_layout_manager()->GetOpacity(),
  548. child_visibility_bitmask, should_animate};
  549. }
  550. int StatusAreaWidget::GetCollapseAvailableWidth(bool force_collapsible) const {
  551. const int shelf_width =
  552. shelf_->shelf_widget()->GetClientAreaBoundsInScreen().width();
  553. if (!force_collapsible)
  554. return shelf_width / 2 - kStatusAreaLeftPaddingForOverflow;
  555. int available_width = kStatusAreaForceCollapseAvailableWidth;
  556. // If calendar view is enabled, add the date tray width to the collapse
  557. // available width.
  558. if (features::IsCalendarViewEnabled()) {
  559. DCHECK(date_tray_);
  560. available_width += date_tray_->tray_container()->GetPreferredSize().width();
  561. }
  562. return available_width;
  563. }
  564. } // namespace ash