window_cycle_tab_slider.cc 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. // Copyright 2020 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/wm/window_cycle/window_cycle_tab_slider.h"
  5. #include "ash/session/session_controller_impl.h"
  6. #include "ash/shell.h"
  7. #include "ash/strings/grit/ash_strings.h"
  8. #include "ash/style/ash_color_provider.h"
  9. #include "ash/wm/mru_window_tracker.h"
  10. #include "ash/wm/window_cycle/window_cycle_controller.h"
  11. #include "base/bind.h"
  12. #include "base/strings/utf_string_conversions.h"
  13. #include "components/prefs/pref_service.h"
  14. #include "ui/base/l10n/l10n_util.h"
  15. #include "ui/base/metadata/metadata_impl_macros.h"
  16. #include "ui/compositor/layer.h"
  17. #include "ui/compositor/scoped_layer_animation_settings.h"
  18. #include "ui/gfx/canvas.h"
  19. #include "ui/gfx/geometry/rect.h"
  20. #include "ui/gfx/geometry/transform_util.h"
  21. #include "ui/views/accessibility/accessibility_paint_checks.h"
  22. #include "ui/views/background.h"
  23. #include "ui/views/controls/highlight_path_generator.h"
  24. #include "ui/views/layout/box_layout.h"
  25. namespace ash {
  26. namespace {
  27. // Animation
  28. // The animation duration for the translation of |active_button_selector_| on
  29. // mode change.
  30. constexpr auto kToggleSlideDuration = base::Milliseconds(150);
  31. // The insets of the focus ring of the tab slider button.
  32. constexpr int kTabSliderButtonFocusInsets = 4;
  33. } // namespace
  34. //////////////////////////////////////////////////////////////////////////////
  35. // WindowCycleTabSlider, public:
  36. WindowCycleTabSlider::WindowCycleTabSlider()
  37. : active_button_selector_(AddChildView(std::make_unique<views::View>())),
  38. buttons_container_(AddChildView(std::make_unique<views::View>())),
  39. all_desks_tab_slider_button_(buttons_container_->AddChildView(
  40. std::make_unique<WindowCycleTabSliderButton>(
  41. base::BindRepeating(
  42. &WindowCycleController::OnModeChanged,
  43. base::Unretained(Shell::Get()->window_cycle_controller()),
  44. /*per_desk=*/false,
  45. WindowCycleController::ModeSwitchSource::kClick),
  46. l10n_util::GetStringUTF16(IDS_ASH_ALT_TAB_ALL_DESKS_MODE)))),
  47. current_desk_tab_slider_button_(buttons_container_->AddChildView(
  48. std::make_unique<WindowCycleTabSliderButton>(
  49. base::BindRepeating(
  50. &WindowCycleController::OnModeChanged,
  51. base::Unretained(Shell::Get()->window_cycle_controller()),
  52. /*per_desk=*/true,
  53. WindowCycleController::ModeSwitchSource::kClick),
  54. l10n_util::GetStringUTF16(IDS_ASH_ALT_TAB_CURRENT_DESK_MODE)))) {
  55. SetPaintToLayer();
  56. layer()->SetFillsBoundsOpaquely(false);
  57. // Layout two buttons in button containers.
  58. buttons_container_->SetPaintToLayer();
  59. buttons_container_->layer()->SetFillsBoundsOpaquely(false);
  60. buttons_container_->SetLayoutManager(std::make_unique<views::BoxLayout>(
  61. views::BoxLayout::Orientation::kHorizontal, gfx::Insets(), 0));
  62. // All buttons should have the same width and height.
  63. const gfx::Size button_size = GetPreferredSizeForButtons();
  64. all_desks_tab_slider_button_->SetPreferredSize(button_size);
  65. current_desk_tab_slider_button_->SetPreferredSize(button_size);
  66. // Setup an active button selector.
  67. active_button_selector_->SetPreferredSize(
  68. gfx::Size(button_size.width() + 2 * kTabSliderButtonFocusInsets,
  69. button_size.height() + 2 * kTabSliderButtonFocusInsets));
  70. active_button_selector_->SetPaintToLayer();
  71. active_button_selector_->layer()->SetFillsBoundsOpaquely(false);
  72. active_button_selector_->SetLayoutManager(std::make_unique<views::BoxLayout>(
  73. views::BoxLayout::Orientation::kHorizontal, gfx::Insets(), 0));
  74. const int active_button_selector_round_radius =
  75. int{active_button_selector_->GetPreferredSize().height() / 2};
  76. // Create highlight border for the selector to be displayed during keyboard
  77. // navigation.
  78. auto border = std::make_unique<WmHighlightItemBorder>(
  79. active_button_selector_round_radius, gfx::Insets());
  80. highlight_border_ = border.get();
  81. active_button_selector_->SetBorder(std::move(border));
  82. views::InstallRoundRectHighlightPathGenerator(
  83. active_button_selector_, gfx::Insets(),
  84. active_button_selector_round_radius);
  85. // Create background for the selector to show an active button.
  86. auto* active_button_selector_background =
  87. active_button_selector_->AddChildView(std::make_unique<views::View>());
  88. active_button_selector_background->SetPaintToLayer();
  89. active_button_selector_background->layer()->SetFillsBoundsOpaquely(false);
  90. active_button_selector_background->SetPreferredSize(
  91. gfx::Size(button_size.width(), button_size.height()));
  92. active_button_selector_background->SetBackground(
  93. views::CreateRoundedRectBackground(
  94. AshColorProvider::Get()->GetControlsLayerColor(
  95. AshColorProvider::ControlsLayerType::
  96. kControlBackgroundColorActive),
  97. int{button_size.height() / 2}));
  98. // Add the tab slider background.
  99. buttons_container_->SetBackground(views::CreateRoundedRectBackground(
  100. AshColorProvider::Get()->GetControlsLayerColor(
  101. AshColorProvider::ControlsLayerType::kControlBackgroundColorInactive),
  102. int{button_size.height() / 2}));
  103. // Read alt-tab mode from user prefs via |IsAltTabPerActiveDesk|, which
  104. // handle multiple cases of different flags enabled and the number of desk.
  105. const bool per_desk =
  106. Shell::Get()->window_cycle_controller()->IsAltTabPerActiveDesk();
  107. all_desks_tab_slider_button_->SetToggled(!per_desk);
  108. current_desk_tab_slider_button_->SetToggled(per_desk);
  109. // TODO(crbug.com/1218186): Remove this, this is in place temporarily to be
  110. // able to submit accessibility checks. This crashes if fetching a11y node
  111. // data during paint because `active_button_selector_` is null.
  112. active_button_selector_->SetProperty(views::kSkipAccessibilityPaintChecks,
  113. true);
  114. active_button_selector_->SetFocusBehavior(View::FocusBehavior::ALWAYS);
  115. }
  116. void WindowCycleTabSlider::SetFocus(bool focus) {
  117. if (is_focused_ == focus)
  118. return;
  119. is_focused_ = focus;
  120. highlight_border_->SetFocused(is_focused_);
  121. active_button_selector_->SchedulePaint();
  122. }
  123. void WindowCycleTabSlider::OnModePrefsChanged() {
  124. const bool per_desk =
  125. Shell::Get()->window_cycle_controller()->IsAltTabPerActiveDesk();
  126. // Refresh tab slider UI to reflect the new mode.
  127. all_desks_tab_slider_button_->SetToggled(!per_desk);
  128. current_desk_tab_slider_button_->SetToggled(per_desk);
  129. UpdateActiveButtonSelector(per_desk);
  130. active_button_selector_->RequestFocus();
  131. }
  132. void WindowCycleTabSlider::Layout() {
  133. const gfx::Size button_size = GetPreferredSizeForButtons();
  134. buttons_container_->SetSize(
  135. gfx::Size(2 * button_size.width(), button_size.height()));
  136. active_button_selector_->SetBounds(
  137. Shell::Get()->window_cycle_controller()->IsAltTabPerActiveDesk()
  138. ? button_size.width() - kTabSliderButtonFocusInsets
  139. : -kTabSliderButtonFocusInsets,
  140. -kTabSliderButtonFocusInsets,
  141. button_size.width() + 2 * kTabSliderButtonFocusInsets,
  142. button_size.height() + 2 * kTabSliderButtonFocusInsets);
  143. }
  144. gfx::Size WindowCycleTabSlider::CalculatePreferredSize() const {
  145. return buttons_container_->GetPreferredSize();
  146. }
  147. //////////////////////////////////////////////////////////////////////////////
  148. // WindowCycleTabSlider, private:
  149. void WindowCycleTabSlider::UpdateActiveButtonSelector(bool per_desk) {
  150. auto active_button_selector_bounds = active_button_selector_->bounds();
  151. if (active_button_selector_bounds.IsEmpty()) {
  152. // OnModePrefsChanged() is called in the ctor so the
  153. // |active_button_selector_| has not been laid out yet so exit early.
  154. return;
  155. }
  156. auto* active_button_selector_layer = active_button_selector_->layer();
  157. ui::ScopedLayerAnimationSettings scoped_settings(
  158. active_button_selector_layer->GetAnimator());
  159. scoped_settings.SetTransitionDuration(kToggleSlideDuration);
  160. scoped_settings.SetTweenType(gfx::Tween::Type::FAST_OUT_SLOW_IN_2);
  161. scoped_settings.SetPreemptionStrategy(
  162. ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
  163. const gfx::Size button_size = GetPreferredSizeForButtons();
  164. const gfx::Rect new_selector_bounds =
  165. gfx::Rect(per_desk ? button_size.width() - kTabSliderButtonFocusInsets
  166. : -kTabSliderButtonFocusInsets,
  167. -kTabSliderButtonFocusInsets,
  168. button_size.width() + 2 * kTabSliderButtonFocusInsets,
  169. button_size.height() + 2 * kTabSliderButtonFocusInsets);
  170. active_button_selector_layer->SetTransform(
  171. gfx::TransformBetweenRects(gfx::RectF(active_button_selector_bounds),
  172. gfx::RectF(new_selector_bounds)));
  173. }
  174. gfx::Size WindowCycleTabSlider::GetPreferredSizeForButtons() {
  175. gfx::Size preferred_size = all_desks_tab_slider_button_->GetPreferredSize();
  176. preferred_size.SetToMax(current_desk_tab_slider_button_->GetPreferredSize());
  177. return preferred_size;
  178. }
  179. BEGIN_METADATA(WindowCycleTabSlider, views::View)
  180. END_METADATA
  181. } // namespace ash