power_button_menu_view.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  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/power/power_button_menu_view.h"
  5. #include <memory>
  6. #include "ash/capture_mode/capture_mode_controller.h"
  7. #include "ash/constants/ash_features.h"
  8. #include "ash/display/screen_orientation_controller.h"
  9. #include "ash/login/login_screen_controller.h"
  10. #include "ash/public/cpp/new_window_delegate.h"
  11. #include "ash/public/cpp/style/scoped_light_mode_as_default.h"
  12. #include "ash/resources/vector_icons/vector_icons.h"
  13. #include "ash/session/session_controller_impl.h"
  14. #include "ash/shell.h"
  15. #include "ash/strings/grit/ash_strings.h"
  16. #include "ash/style/ash_color_provider.h"
  17. #include "ash/style/system_shadow.h"
  18. #include "ash/system/power/power_button_menu_item_view.h"
  19. #include "ash/system/power/power_button_menu_metrics_type.h"
  20. #include "ash/system/user/login_status.h"
  21. #include "ash/wm/lock_state_controller.h"
  22. #include "ash/wm/tablet_mode/tablet_mode_controller.h"
  23. #include "base/bind.h"
  24. #include "base/callback_helpers.h"
  25. #include "base/time/time.h"
  26. #include "ui/base/l10n/l10n_util.h"
  27. #include "ui/compositor/layer.h"
  28. #include "ui/compositor/layer_animator.h"
  29. #include "ui/compositor/scoped_layer_animation_settings.h"
  30. #include "ui/compositor_extra/shadow.h"
  31. #include "ui/gfx/vector_icon_types.h"
  32. #include "ui/views/accessibility/view_accessibility.h"
  33. #include "ui/views/background.h"
  34. #include "ui/views/highlight_border.h"
  35. namespace ash {
  36. namespace {
  37. // Horizontal and vertical padding of the menu item view.
  38. constexpr int kMenuItemHorizontalPadding = 16;
  39. constexpr int kMenuItemVerticalPadding = 16;
  40. // The rounded corner radius of menu.
  41. constexpr int kMenuCornerRadius = 16;
  42. // Horizontal padding between two menu items.
  43. constexpr int kPaddingBetweenMenuItems = 8;
  44. // Set show/hide animation for layer.
  45. void SetLayerAnimation(ui::Layer* layer,
  46. ui::ImplicitAnimationObserver* observer,
  47. bool show,
  48. const gfx::Transform& transform) {
  49. DCHECK(layer);
  50. auto* animator = layer->GetAnimator();
  51. animator->AbortAllAnimations();
  52. ui::ScopedLayerAnimationSettings animation_settings(animator);
  53. animation_settings.SetTweenType(show ? gfx::Tween::EASE_IN
  54. : gfx::Tween::FAST_OUT_LINEAR_IN);
  55. animation_settings.SetTransitionDuration(
  56. PowerButtonMenuView::kMenuAnimationDuration);
  57. animation_settings.SetPreemptionStrategy(
  58. ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
  59. if (observer)
  60. animation_settings.AddObserver(observer);
  61. layer->SetOpacity(show ? 1.0f : 0.f);
  62. layer->SetTransform(transform);
  63. }
  64. } // namespace
  65. using PowerButtonPosition = PowerButtonController::PowerButtonPosition;
  66. constexpr base::TimeDelta PowerButtonMenuView::kMenuAnimationDuration;
  67. PowerButtonMenuView::PowerButtonMenuView(
  68. ShutdownReason shutdown_reason,
  69. PowerButtonPosition power_button_position)
  70. : shutdown_reason_(shutdown_reason),
  71. power_button_position_(power_button_position) {
  72. SetFocusBehavior(FocusBehavior::ALWAYS);
  73. SetPaintToLayer();
  74. if (features::IsDarkLightModeEnabled()) {
  75. SetBorder(std::make_unique<views::HighlightBorder>(
  76. kMenuCornerRadius, views::HighlightBorder::Type::kHighlightBorder1,
  77. /*use_light_colors=*/false));
  78. }
  79. layer()->SetFillsBoundsOpaquely(false);
  80. layer()->SetRoundedCornerRadius(gfx::RoundedCornersF(kMenuCornerRadius));
  81. layer()->SetBackgroundBlur(ColorProvider::kBackgroundBlurSigma);
  82. GetViewAccessibility().OverrideRole(ax::mojom::Role::kMenu);
  83. GetViewAccessibility().OverrideName(
  84. l10n_util::GetStringUTF16(IDS_ASH_POWER_BUTTON_MENU_ACCESSIBLE));
  85. RecreateItems();
  86. // Create a system shadow for current view.
  87. shadow_ = SystemShadow::CreateShadowOnNinePatchLayerForView(
  88. this, SystemShadow::Type::kElevation12);
  89. shadow_->SetRoundedCornerRadius(kMenuCornerRadius);
  90. }
  91. PowerButtonMenuView::~PowerButtonMenuView() = default;
  92. void PowerButtonMenuView::FocusPowerOffButton() {
  93. power_off_item_->RequestFocus();
  94. }
  95. void PowerButtonMenuView::ScheduleShowHideAnimation(bool show) {
  96. // Set initial state.
  97. SetVisible(true);
  98. // Calculate transform of menu view and shadow bounds.
  99. gfx::Transform transform;
  100. if (show) {
  101. TransformDisplacement transform_displacement = GetTransformDisplacement();
  102. if (transform_displacement.direction == TransformDirection::X)
  103. transform.Translate(transform_displacement.distance, 0);
  104. else if (transform_displacement.direction == TransformDirection::Y)
  105. transform.Translate(0, transform_displacement.distance);
  106. }
  107. SetLayerAnimation(layer(), this, show, transform);
  108. SetLayerAnimation(shadow_->GetLayer(), nullptr, show, transform);
  109. }
  110. PowerButtonMenuView::TransformDisplacement
  111. PowerButtonMenuView::GetTransformDisplacement() const {
  112. TransformDisplacement transform_displacement;
  113. if (power_button_position_ == PowerButtonPosition::NONE ||
  114. !Shell::Get()->tablet_mode_controller()->InTabletMode()) {
  115. transform_displacement.direction = TransformDirection::Y;
  116. transform_displacement.distance = kMenuViewTransformDistanceDp;
  117. return transform_displacement;
  118. }
  119. chromeos::OrientationType screen_orientation =
  120. Shell::Get()->screen_orientation_controller()->GetCurrentOrientation();
  121. bool is_left_or_right = power_button_position_ == PowerButtonPosition::LEFT ||
  122. power_button_position_ == PowerButtonPosition::RIGHT;
  123. if (chromeos::IsLandscapeOrientation(screen_orientation)) {
  124. transform_displacement.direction =
  125. is_left_or_right ? TransformDirection::X : TransformDirection::Y;
  126. } else {
  127. transform_displacement.direction =
  128. is_left_or_right ? TransformDirection::Y : TransformDirection::X;
  129. }
  130. bool positive_transform = false;
  131. if (is_left_or_right) {
  132. bool is_primary = chromeos::IsPrimaryOrientation(screen_orientation);
  133. positive_transform = power_button_position_ == PowerButtonPosition::LEFT
  134. ? is_primary
  135. : !is_primary;
  136. } else {
  137. bool is_landscape_primary_or_portrait_secondary =
  138. screen_orientation == chromeos::OrientationType::kLandscapePrimary ||
  139. screen_orientation == chromeos::OrientationType::kPortraitSecondary;
  140. positive_transform = power_button_position_ == PowerButtonPosition::TOP
  141. ? is_landscape_primary_or_portrait_secondary
  142. : !is_landscape_primary_or_portrait_secondary;
  143. }
  144. transform_displacement.distance = positive_transform
  145. ? kMenuViewTransformDistanceDp
  146. : -kMenuViewTransformDistanceDp;
  147. return transform_displacement;
  148. }
  149. void PowerButtonMenuView::RecreateItems() {
  150. // Helper to add or remove a menu item from |this|. Stores weak pointer to
  151. // |out_item_ptr|.
  152. auto add_remove_item =
  153. [this](bool create, PowerButtonMenuActionType action,
  154. base::RepeatingClosure callback, const gfx::VectorIcon& icon,
  155. const std::u16string& string,
  156. PowerButtonMenuItemView** out_item_ptr) -> void {
  157. // If an item needs to be created and exists, or needs to be destroyed but
  158. // does not exist, there is nothing to be done.
  159. if (create && *out_item_ptr)
  160. return;
  161. if (!create && !*out_item_ptr)
  162. return;
  163. if (create) {
  164. *out_item_ptr = AddChildView(std::make_unique<PowerButtonMenuItemView>(
  165. base::BindRepeating(&PowerButtonMenuView::ButtonPressed,
  166. base::Unretained(this), action,
  167. std::move(callback)),
  168. icon, string));
  169. } else {
  170. std::unique_ptr<PowerButtonMenuItemView> to_delete =
  171. RemoveChildViewT(*out_item_ptr);
  172. *out_item_ptr = nullptr;
  173. }
  174. };
  175. const SessionControllerImpl* const session_controller =
  176. Shell::Get()->session_controller();
  177. const LoginStatus login_status = session_controller->login_status();
  178. const bool create_sign_out = login_status != LoginStatus::NOT_LOGGED_IN;
  179. const bool create_lock_screen = login_status != LoginStatus::LOCKED &&
  180. session_controller->CanLockScreen();
  181. const bool create_capture_mode =
  182. Shell::Get()->tablet_mode_controller()->InTabletMode() &&
  183. !session_controller->IsUserSessionBlocked();
  184. const bool create_feedback = login_status != LoginStatus::LOCKED &&
  185. login_status != LoginStatus::KIOSK_APP;
  186. add_remove_item(
  187. true, PowerButtonMenuActionType::kPowerOff,
  188. base::BindRepeating(
  189. &LockStateController::StartShutdownAnimation,
  190. base::Unretained(Shell::Get()->lock_state_controller()),
  191. shutdown_reason_),
  192. kSystemPowerButtonMenuPowerOffIcon,
  193. l10n_util::GetStringUTF16(IDS_ASH_POWER_BUTTON_MENU_POWER_OFF_BUTTON),
  194. &power_off_item_);
  195. add_remove_item(
  196. create_sign_out, PowerButtonMenuActionType::kSignOut,
  197. base::BindRepeating(&SessionControllerImpl::RequestSignOut,
  198. base::Unretained(Shell::Get()->session_controller())),
  199. kSystemPowerButtonMenuSignOutIcon,
  200. user::GetLocalizedSignOutStringForStatus(login_status, false),
  201. &sign_out_item_);
  202. add_remove_item(
  203. create_lock_screen, PowerButtonMenuActionType::kLockScreen,
  204. base::BindRepeating(&SessionControllerImpl::LockScreen,
  205. base::Unretained(Shell::Get()->session_controller())),
  206. kSystemPowerButtonMenuLockScreenIcon,
  207. l10n_util::GetStringUTF16(IDS_ASH_POWER_BUTTON_MENU_LOCK_SCREEN_BUTTON),
  208. &lock_screen_item_);
  209. add_remove_item(
  210. create_capture_mode, PowerButtonMenuActionType::kCaptureMode,
  211. base::BindRepeating(&CaptureModeController::Start,
  212. base::Unretained(CaptureModeController::Get()),
  213. CaptureModeEntryType::kPowerMenu),
  214. kCaptureModeIcon,
  215. l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_CAPTURE_MODE_BUTTON_LABEL),
  216. &capture_mode_item_);
  217. add_remove_item(
  218. create_feedback, PowerButtonMenuActionType::kFeedback,
  219. base::BindRepeating(
  220. [](Shell* shell) {
  221. if (shell->session_controller()->login_status() ==
  222. LoginStatus::NOT_LOGGED_IN) {
  223. // There is a special flow for feedback while in login screen,
  224. // therefore we trigger the same handler associated with the
  225. // feedback accelerator from the login screen to bring up the
  226. // feedback dialog.
  227. shell->login_screen_controller()->HandleAccelerator(
  228. LoginAcceleratorAction::kShowFeedback);
  229. } else {
  230. NewWindowDelegate::GetInstance()->OpenFeedbackPage();
  231. }
  232. },
  233. Shell::Get()),
  234. kSystemPowerButtonMenuFeedbackIcon,
  235. l10n_util::GetStringUTF16(IDS_ASH_POWER_BUTTON_MENU_FEEDBACK_BUTTON),
  236. &feedback_item_);
  237. }
  238. const char* PowerButtonMenuView::GetClassName() const {
  239. return "PowerButtonMenuView";
  240. }
  241. void PowerButtonMenuView::Layout() {
  242. gfx::Rect rect(GetContentsBounds().origin(),
  243. power_off_item_->GetPreferredSize());
  244. const int y_offset =
  245. kMenuItemVerticalPadding - PowerButtonMenuItemView::kItemBorderThickness;
  246. int x_offset = kMenuItemHorizontalPadding -
  247. PowerButtonMenuItemView::kItemBorderThickness;
  248. rect.Offset(x_offset, y_offset);
  249. power_off_item_->SetBoundsRect(rect);
  250. const int padding_between_items_with_border =
  251. kPaddingBetweenMenuItems -
  252. 2 * PowerButtonMenuItemView::kItemBorderThickness;
  253. x_offset = rect.width() + padding_between_items_with_border;
  254. if (sign_out_item_) {
  255. rect.Offset(x_offset, 0);
  256. sign_out_item_->SetBoundsRect(rect);
  257. if (lock_screen_item_) {
  258. rect.Offset(x_offset, 0);
  259. lock_screen_item_->SetBoundsRect(rect);
  260. }
  261. }
  262. if (capture_mode_item_) {
  263. rect.Offset(x_offset, 0);
  264. capture_mode_item_->SetBoundsRect(rect);
  265. }
  266. if (feedback_item_) {
  267. rect.Offset(x_offset, 0);
  268. feedback_item_->SetBoundsRect(rect);
  269. }
  270. }
  271. gfx::Size PowerButtonMenuView::CalculatePreferredSize() const {
  272. gfx::Size menu_size;
  273. DCHECK(power_off_item_);
  274. menu_size = gfx::Size(0, PowerButtonMenuItemView::kMenuItemHeight +
  275. 2 * kMenuItemVerticalPadding);
  276. int width =
  277. PowerButtonMenuItemView::kMenuItemWidth + 2 * kMenuItemHorizontalPadding;
  278. const int one_item_x_offset =
  279. PowerButtonMenuItemView::kMenuItemWidth + kPaddingBetweenMenuItems;
  280. if (sign_out_item_) {
  281. width += one_item_x_offset;
  282. if (lock_screen_item_)
  283. width += one_item_x_offset;
  284. }
  285. if (capture_mode_item_)
  286. width += one_item_x_offset;
  287. if (feedback_item_)
  288. width += one_item_x_offset;
  289. menu_size.set_width(width);
  290. return menu_size;
  291. }
  292. void PowerButtonMenuView::OnThemeChanged() {
  293. views::View::OnThemeChanged();
  294. ScopedLightModeAsDefault scoped_light_mode_as_default;
  295. SetBackground(
  296. views::CreateSolidBackground(AshColorProvider::Get()->GetBaseLayerColor(
  297. AshColorProvider::BaseLayerType::kTransparent80)));
  298. }
  299. void PowerButtonMenuView::OnImplicitAnimationsCompleted() {
  300. if (layer()->opacity() == 0.f)
  301. SetVisible(false);
  302. if (layer()->opacity() == 1.0f)
  303. RequestFocus();
  304. }
  305. void PowerButtonMenuView::ButtonPressed(PowerButtonMenuActionType action,
  306. base::RepeatingClosure callback) {
  307. RecordMenuActionHistogram(action);
  308. std::move(callback).Run();
  309. Shell::Get()->power_button_controller()->DismissMenu();
  310. }
  311. } // namespace ash