top_shortcuts_view.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. // Copyright 2018 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/unified/top_shortcuts_view.h"
  5. #include <numeric>
  6. #include "ash/accessibility/accessibility_controller_impl.h"
  7. #include "ash/constants/ash_pref_names.h"
  8. #include "ash/public/cpp/ash_view_ids.h"
  9. #include "ash/resources/vector_icons/vector_icons.h"
  10. #include "ash/session/session_controller_impl.h"
  11. #include "ash/shell.h"
  12. #include "ash/shutdown_controller_impl.h"
  13. #include "ash/strings/grit/ash_strings.h"
  14. #include "ash/style/ash_color_provider.h"
  15. #include "ash/style/icon_button.h"
  16. #include "ash/style/pill_button.h"
  17. #include "ash/system/tray/tray_constants.h"
  18. #include "ash/system/tray/tray_popup_utils.h"
  19. #include "ash/system/unified/collapse_button.h"
  20. #include "ash/system/unified/unified_system_tray_controller.h"
  21. #include "ash/system/unified/user_chooser_detailed_view_controller.h"
  22. #include "ash/system/unified/user_chooser_view.h"
  23. #include "ash/system/user/login_status.h"
  24. #include "base/bind.h"
  25. #include "base/cxx17_backports.h"
  26. #include "components/prefs/pref_registry_simple.h"
  27. #include "components/prefs/pref_service.h"
  28. #include "components/vector_icons/vector_icons.h"
  29. #include "ui/color/color_id.h"
  30. #include "ui/gfx/paint_vector_icon.h"
  31. #include "ui/views/border.h"
  32. #include "ui/views/controls/button/button.h"
  33. #include "ui/views/controls/focus_ring.h"
  34. #include "ui/views/controls/highlight_path_generator.h"
  35. #include "ui/views/layout/box_layout.h"
  36. #include "ui/views/layout/fill_layout.h"
  37. #include "ui/views/view_class_properties.h"
  38. namespace ash {
  39. namespace {
  40. class UserAvatarButton : public views::Button {
  41. public:
  42. explicit UserAvatarButton(PressedCallback callback);
  43. UserAvatarButton(const UserAvatarButton&) = delete;
  44. UserAvatarButton& operator=(const UserAvatarButton&) = delete;
  45. ~UserAvatarButton() override = default;
  46. };
  47. UserAvatarButton::UserAvatarButton(PressedCallback callback)
  48. : Button(std::move(callback)) {
  49. SetID(VIEW_ID_USER_AVATAR_BUTTON);
  50. SetLayoutManager(std::make_unique<views::FillLayout>());
  51. SetBorder(views::CreateEmptyBorder(kUnifiedCircularButtonFocusPadding));
  52. AddChildView(CreateUserAvatarView(0 /* user_index */));
  53. SetTooltipText(GetUserItemAccessibleString(0 /* user_index */));
  54. SetInstallFocusRingOnFocus(true);
  55. views::FocusRing::Get(this)->SetColorId(ui::kColorAshFocusRing);
  56. views::InstallCircleHighlightPathGenerator(this);
  57. }
  58. } // namespace
  59. TopShortcutButtonContainer::TopShortcutButtonContainer() = default;
  60. TopShortcutButtonContainer::~TopShortcutButtonContainer() = default;
  61. // Buttons are equally spaced by the default value, but the gap will be
  62. // narrowed evenly when the parent view is not large enough.
  63. void TopShortcutButtonContainer::Layout() {
  64. const gfx::Rect child_area = GetContentsBounds();
  65. views::View::Views visible_children;
  66. std::copy_if(children().cbegin(), children().cend(),
  67. std::back_inserter(visible_children), [](const auto* v) {
  68. return v->GetVisible() && (v->GetPreferredSize().width() > 0);
  69. });
  70. if (visible_children.empty())
  71. return;
  72. const int visible_child_width =
  73. std::accumulate(visible_children.cbegin(), visible_children.cend(), 0,
  74. [](int width, const auto* v) {
  75. return width + v->GetPreferredSize().width();
  76. });
  77. int spacing = 0;
  78. if (visible_children.size() > 1) {
  79. spacing = (child_area.width() - visible_child_width) /
  80. (static_cast<int>(visible_children.size()) - 1);
  81. spacing = base::clamp(spacing, kUnifiedTopShortcutButtonMinSpacing,
  82. kUnifiedTopShortcutButtonDefaultSpacing);
  83. }
  84. int x = child_area.x();
  85. int y = child_area.y() + kUnifiedTopShortcutContainerTopPadding +
  86. kUnifiedCircularButtonFocusPadding.bottom();
  87. for (auto* child : visible_children) {
  88. int child_y = y;
  89. int width = child->GetPreferredSize().width();
  90. if (child == user_avatar_button_) {
  91. x -= kUnifiedCircularButtonFocusPadding.left();
  92. child_y -= kUnifiedCircularButtonFocusPadding.bottom();
  93. } else if (child == sign_out_button_) {
  94. // When there's not enough space, shrink the sign-out button.
  95. const int remainder =
  96. child_area.width() -
  97. (static_cast<int>(visible_children.size()) - 1) * spacing -
  98. (visible_child_width - width);
  99. width = base::clamp(width, 0, std::max(0, remainder));
  100. }
  101. child->SetBounds(x, child_y, width, child->GetHeightForWidth(width));
  102. x += width + spacing;
  103. if (child == user_avatar_button_)
  104. x -= kUnifiedCircularButtonFocusPadding.right();
  105. }
  106. }
  107. gfx::Size TopShortcutButtonContainer::CalculatePreferredSize() const {
  108. int total_horizontal_size = 0;
  109. int num_visible = 0;
  110. for (const auto* child : children()) {
  111. if (!child->GetVisible())
  112. continue;
  113. int child_horizontal_size = child->GetPreferredSize().width();
  114. if (child_horizontal_size == 0)
  115. continue;
  116. total_horizontal_size += child_horizontal_size;
  117. num_visible++;
  118. }
  119. int width =
  120. (num_visible == 0)
  121. ? 0
  122. : total_horizontal_size +
  123. (num_visible - 1) * kUnifiedTopShortcutButtonDefaultSpacing;
  124. int height = kTrayItemSize + kUnifiedCircularButtonFocusPadding.height() +
  125. kUnifiedTopShortcutContainerTopPadding;
  126. return gfx::Size(width, height);
  127. }
  128. const char* TopShortcutButtonContainer::GetClassName() const {
  129. return "TopShortcutButtonContainer";
  130. }
  131. void TopShortcutButtonContainer::AddUserAvatarButton(
  132. views::View* user_avatar_button) {
  133. AddChildView(user_avatar_button);
  134. user_avatar_button_ = user_avatar_button;
  135. }
  136. void TopShortcutButtonContainer::AddSignOutButton(
  137. views::View* sign_out_button) {
  138. AddChildView(sign_out_button);
  139. sign_out_button_ = sign_out_button;
  140. }
  141. TopShortcutsView::TopShortcutsView(UnifiedSystemTrayController* controller) {
  142. DCHECK(controller);
  143. auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
  144. views::BoxLayout::Orientation::kHorizontal, kUnifiedTopShortcutPadding,
  145. kUnifiedTopShortcutSpacing));
  146. layout->set_cross_axis_alignment(
  147. views::BoxLayout::CrossAxisAlignment::kStart);
  148. container_ = new TopShortcutButtonContainer();
  149. AddChildView(container_);
  150. Shell* shell = Shell::Get();
  151. bool is_on_login_screen =
  152. shell->session_controller()->login_status() == LoginStatus::NOT_LOGGED_IN;
  153. bool can_show_settings = TrayPopupUtils::CanOpenWebUISettings();
  154. bool can_lock_screen = shell->session_controller()->CanLockScreen();
  155. if (!is_on_login_screen) {
  156. user_avatar_button_ = new UserAvatarButton(
  157. base::BindRepeating(&UnifiedSystemTrayController::ShowUserChooserView,
  158. base::Unretained(controller)));
  159. user_avatar_button_->SetEnabled(
  160. UserChooserDetailedViewController::IsUserChooserEnabled());
  161. container_->AddUserAvatarButton(user_avatar_button_);
  162. sign_out_button_ = new PillButton(
  163. base::BindRepeating(&UnifiedSystemTrayController::HandleSignOutAction,
  164. base::Unretained(controller)),
  165. user::GetLocalizedSignOutStringForStatus(
  166. Shell::Get()->session_controller()->login_status(),
  167. /*multiline=*/false),
  168. PillButton::Type::kIconless,
  169. /*icon=*/nullptr);
  170. container_->AddSignOutButton(sign_out_button_);
  171. }
  172. bool reboot = shell->shutdown_controller()->reboot_on_shutdown();
  173. power_button_ = new IconButton(
  174. base::BindRepeating(&UnifiedSystemTrayController::HandlePowerAction,
  175. base::Unretained(controller)),
  176. IconButton::Type::kSmall, &kUnifiedMenuPowerIcon,
  177. reboot ? IDS_ASH_STATUS_TRAY_REBOOT : IDS_ASH_STATUS_TRAY_SHUTDOWN);
  178. power_button_->SetID(VIEW_ID_POWER_BUTTON);
  179. container_->AddChildView(power_button_);
  180. if (can_show_settings && can_lock_screen) {
  181. lock_button_ = new IconButton(
  182. base::BindRepeating(&UnifiedSystemTrayController::HandleLockAction,
  183. base::Unretained(controller)),
  184. IconButton::Type::kSmall, &kUnifiedMenuLockIcon,
  185. IDS_ASH_STATUS_TRAY_LOCK);
  186. container_->AddChildView(lock_button_);
  187. }
  188. if (can_show_settings) {
  189. settings_button_ = new IconButton(
  190. base::BindRepeating(&UnifiedSystemTrayController::HandleSettingsAction,
  191. base::Unretained(controller)),
  192. IconButton::Type::kSmall, &vector_icons::kSettingsOutlineIcon,
  193. IDS_ASH_STATUS_TRAY_SETTINGS);
  194. settings_button_->SetID(VIEW_ID_SETTINGS_BUTTON_VIEW);
  195. container_->AddChildView(settings_button_);
  196. local_state_pref_change_registrar_.Init(Shell::Get()->local_state());
  197. local_state_pref_change_registrar_.Add(
  198. prefs::kOsSettingsEnabled,
  199. base::BindRepeating(&TopShortcutsView::UpdateSettingsButtonState,
  200. base::Unretained(this)));
  201. UpdateSettingsButtonState();
  202. }
  203. // |collapse_button_| should be right-aligned, so we make the buttons
  204. // container flex occupying all remaining space.
  205. layout->SetFlexForView(container_, 1);
  206. auto* collapse_button_container =
  207. AddChildView(std::make_unique<views::View>());
  208. collapse_button_ =
  209. collapse_button_container->AddChildView(std::make_unique<CollapseButton>(
  210. base::BindRepeating(&UnifiedSystemTrayController::ToggleExpanded,
  211. base::Unretained(controller))));
  212. const gfx::Size collapse_button_size = collapse_button_->GetPreferredSize();
  213. collapse_button_container->SetPreferredSize(
  214. gfx::Size(collapse_button_size.width(),
  215. collapse_button_size.height() + kUnifiedTopShortcutSpacing));
  216. collapse_button_->SetBoundsRect(gfx::Rect(
  217. gfx::Point(0, kUnifiedTopShortcutSpacing), collapse_button_size));
  218. }
  219. // static
  220. void TopShortcutsView::RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
  221. registry->RegisterBooleanPref(prefs::kOsSettingsEnabled, true);
  222. }
  223. void TopShortcutsView::SetExpandedAmount(double expanded_amount) {
  224. collapse_button_->SetExpandedAmount(expanded_amount);
  225. }
  226. const char* TopShortcutsView::GetClassName() const {
  227. return "TopShortcutsView";
  228. }
  229. void TopShortcutsView::UpdateSettingsButtonState() {
  230. PrefService* const local_state = Shell::Get()->local_state();
  231. const bool settings_icon_enabled =
  232. local_state->GetBoolean(prefs::kOsSettingsEnabled);
  233. settings_button_->SetState(settings_icon_enabled
  234. ? views::Button::STATE_NORMAL
  235. : views::Button::STATE_DISABLED);
  236. }
  237. } // namespace ash