power_button_menu_screen_view.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  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_screen_view.h"
  5. #include <utility>
  6. #include "ash/shell.h"
  7. #include "ash/style/ash_color_provider.h"
  8. #include "ash/style/default_color_constants.h"
  9. #include "ash/style/default_colors.h"
  10. #include "ash/system/power/power_button_menu_metrics_type.h"
  11. #include "ash/system/power/power_button_menu_view.h"
  12. #include "ash/wm/tablet_mode/tablet_mode_controller.h"
  13. #include "ui/compositor/layer.h"
  14. #include "ui/compositor/layer_animation_observer.h"
  15. #include "ui/compositor/scoped_layer_animation_settings.h"
  16. #include "ui/display/display.h"
  17. #include "ui/display/screen.h"
  18. #include "ui/views/widget/widget.h"
  19. namespace ash {
  20. constexpr int PowerButtonMenuView::kMenuViewTransformDistanceDp;
  21. namespace {
  22. // Opacity of the power button menu fullscreen background shield.
  23. constexpr float kPowerButtonMenuOpacity = 0.4f;
  24. // TODO(minch): Get the internal display size instead if needed.
  25. // Gets the landscape size of the primary display. For landscape orientation,
  26. // the width is always larger than height.
  27. gfx::Size GetPrimaryDisplayLandscapeSize() {
  28. gfx::Rect bounds = display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
  29. return gfx::Size(std::max(bounds.width(), bounds.height()),
  30. std::min(bounds.width(), bounds.height()));
  31. }
  32. // Adjust the menu's |actual_position| to be at least kMenuTransformDistanceDp
  33. // from the edge of the display. |menu_size| means the width or height of the
  34. // menu and |actual_position| is x-coordinate or y-coordinate of the menu.
  35. // |display_edge| is the width or height of the display in landscape_primary
  36. // orientation depending on the power button's posotion.
  37. int AdjustMenuEdgeForDisplaySize(int actual_position,
  38. int display_edge,
  39. int menu_size) {
  40. return std::min(display_edge -
  41. PowerButtonMenuView::kMenuViewTransformDistanceDp -
  42. menu_size,
  43. std::max(PowerButtonMenuView::kMenuViewTransformDistanceDp,
  44. actual_position));
  45. }
  46. } // namespace
  47. using PowerButtonPosition = PowerButtonController::PowerButtonPosition;
  48. using TransformDirection = PowerButtonMenuView::TransformDirection;
  49. class PowerButtonMenuScreenView::PowerButtonMenuBackgroundView
  50. : public views::View,
  51. public ui::ImplicitAnimationObserver {
  52. public:
  53. PowerButtonMenuBackgroundView(base::RepeatingClosure show_animation_done)
  54. : show_animation_done_(show_animation_done) {
  55. SetPaintToLayer(ui::LAYER_SOLID_COLOR);
  56. layer()->SetOpacity(0.f);
  57. }
  58. PowerButtonMenuBackgroundView(const PowerButtonMenuBackgroundView&) = delete;
  59. PowerButtonMenuBackgroundView& operator=(
  60. const PowerButtonMenuBackgroundView&) = delete;
  61. ~PowerButtonMenuBackgroundView() override = default;
  62. void OnImplicitAnimationsCompleted() override {
  63. PowerButtonController* power_button_controller =
  64. Shell::Get()->power_button_controller();
  65. if (layer()->opacity() == 0.f) {
  66. SetVisible(false);
  67. power_button_controller->DismissMenu();
  68. }
  69. if (layer()->opacity() == kPowerButtonMenuOpacity)
  70. show_animation_done_.Run();
  71. }
  72. void ScheduleShowHideAnimation(bool show) {
  73. SetVisible(true);
  74. layer()->GetAnimator()->AbortAllAnimations();
  75. ui::ScopedLayerAnimationSettings animation(layer()->GetAnimator());
  76. animation.AddObserver(this);
  77. animation.SetTweenType(show ? gfx::Tween::EASE_IN_2
  78. : gfx::Tween::FAST_OUT_LINEAR_IN);
  79. animation.SetTransitionDuration(
  80. PowerButtonMenuView::kMenuAnimationDuration);
  81. animation.SetPreemptionStrategy(
  82. ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
  83. layer()->SetOpacity(show ? kPowerButtonMenuOpacity : 0.f);
  84. }
  85. // views::View:
  86. const char* GetClassName() const override {
  87. return "PowerButtonMenuBackgroundView";
  88. }
  89. private:
  90. // views::View:
  91. void OnThemeChanged() override {
  92. views::View::OnThemeChanged();
  93. layer()->SetColor(
  94. DeprecatedGetBaseLayerColor(AshColorProvider::BaseLayerType::kOpaque,
  95. kPowerButtonMenuFullscreenShieldColor));
  96. }
  97. // A callback for when the animation that shows the power menu has finished.
  98. base::RepeatingClosure show_animation_done_;
  99. };
  100. PowerButtonMenuScreenView::PowerButtonMenuScreenView(
  101. ShutdownReason shutdown_reason,
  102. PowerButtonPosition power_button_position,
  103. double power_button_offset_percentage,
  104. base::RepeatingClosure show_animation_done)
  105. : power_button_position_(power_button_position),
  106. power_button_offset_percentage_(power_button_offset_percentage) {
  107. power_button_screen_background_shield_ =
  108. new PowerButtonMenuBackgroundView(show_animation_done);
  109. AddChildView(power_button_screen_background_shield_);
  110. power_button_menu_view_ =
  111. new PowerButtonMenuView(shutdown_reason, power_button_position_);
  112. AddChildView(power_button_menu_view_);
  113. AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
  114. }
  115. PowerButtonMenuScreenView::~PowerButtonMenuScreenView() = default;
  116. void PowerButtonMenuScreenView::ScheduleShowHideAnimation(bool show) {
  117. power_button_screen_background_shield_->ScheduleShowHideAnimation(show);
  118. power_button_menu_view_->ScheduleShowHideAnimation(show);
  119. }
  120. void PowerButtonMenuScreenView::ResetOpacity() {
  121. for (ui::Layer* layer : {power_button_screen_background_shield_->layer(),
  122. power_button_menu_view_->layer()}) {
  123. DCHECK(layer);
  124. layer->SetOpacity(0.f);
  125. }
  126. }
  127. void PowerButtonMenuScreenView::OnWidgetShown(
  128. PowerButtonController::PowerButtonPosition position,
  129. double offset_percentage) {
  130. power_button_position_ = position;
  131. power_button_offset_percentage_ = offset_percentage;
  132. // The order here matters. RecreateItems() must be called before calling
  133. // UpdateMenuBoundsOrigins(), since the latter relies on the
  134. // power_button_menu_view_'s preferred size, which depends on the items added
  135. // to the view.
  136. power_button_menu_view_->RecreateItems();
  137. if (power_button_position_ != PowerButtonPosition::NONE)
  138. UpdateMenuBoundsOrigins();
  139. Layout();
  140. }
  141. const char* PowerButtonMenuScreenView::GetClassName() const {
  142. return "PowerButtonMenuScreenView";
  143. }
  144. void PowerButtonMenuScreenView::Layout() {
  145. power_button_screen_background_shield_->SetBoundsRect(GetContentsBounds());
  146. gfx::Rect menu_bounds = GetMenuBounds();
  147. PowerButtonMenuView::TransformDisplacement transform_displacement =
  148. power_button_menu_view_->GetTransformDisplacement();
  149. if (transform_displacement.direction == TransformDirection::X)
  150. menu_bounds.set_x(menu_bounds.x() - transform_displacement.distance);
  151. else if (transform_displacement.direction == TransformDirection::Y)
  152. menu_bounds.set_y(menu_bounds.y() - transform_displacement.distance);
  153. power_button_menu_view_->SetBoundsRect(menu_bounds);
  154. }
  155. bool PowerButtonMenuScreenView::OnMousePressed(const ui::MouseEvent& event) {
  156. return true;
  157. }
  158. void PowerButtonMenuScreenView::OnMouseReleased(const ui::MouseEvent& event) {
  159. ScheduleShowHideAnimation(false);
  160. RecordMenuActionHistogram(PowerButtonMenuActionType::kDismissByMouse);
  161. }
  162. bool PowerButtonMenuScreenView::AcceleratorPressed(
  163. const ui::Accelerator& accelerator) {
  164. DCHECK_EQ(ui::VKEY_ESCAPE, accelerator.key_code());
  165. Shell::Get()->power_button_controller()->DismissMenu();
  166. RecordMenuActionHistogram(PowerButtonMenuActionType::kDismissByEsc);
  167. return true;
  168. }
  169. void PowerButtonMenuScreenView::OnGestureEvent(ui::GestureEvent* event) {
  170. if (event->type() != ui::ET_GESTURE_TAP_DOWN)
  171. return;
  172. // Dismisses the menu if tap anywhere on the background shield.
  173. ScheduleShowHideAnimation(false);
  174. RecordMenuActionHistogram(PowerButtonMenuActionType::kDismissByTouch);
  175. }
  176. void PowerButtonMenuScreenView::OnDisplayMetricsChanged(
  177. const display::Display& display,
  178. uint32_t changed_metrics) {
  179. GetWidget()->SetBounds(
  180. display::Screen::GetScreen()->GetPrimaryDisplay().bounds());
  181. LayoutWithoutTransform();
  182. }
  183. void PowerButtonMenuScreenView::LayoutWithoutTransform() {
  184. power_button_screen_background_shield_->SetBoundsRect(GetContentsBounds());
  185. power_button_menu_view_->layer()->SetTransform(gfx::Transform());
  186. power_button_menu_view_->SetBoundsRect(GetMenuBounds());
  187. }
  188. void PowerButtonMenuScreenView::UpdateMenuBoundsOrigins() {
  189. // Power button position offset in pixels from the top when the button is at
  190. // the left/right of the screen after rotation.
  191. int left_power_button_y = 0, right_power_button_y = 0;
  192. // Power button position offset in pixels from the left when the button is at
  193. // the top/bottom of the screen after rotation.
  194. int top_power_button_x = 0, bottom_power_button_x = 0;
  195. // The screen orientation when the power button is at the
  196. // left/right/top/bottom of the screen after rotation.
  197. chromeos::OrientationType left_screen_orientation, right_screen_orientation,
  198. top_screen_orientation, bottom_screen_orientation;
  199. const gfx::Size landscape_size = GetPrimaryDisplayLandscapeSize();
  200. int display_width = landscape_size.width();
  201. int display_height = landscape_size.height();
  202. int display_edge_for_adjust = landscape_size.height();
  203. if (power_button_position_ == PowerButtonPosition::TOP ||
  204. power_button_position_ == PowerButtonPosition::BOTTOM) {
  205. std::swap(display_width, display_height);
  206. display_edge_for_adjust = landscape_size.width();
  207. }
  208. int power_button_offset = display_height * power_button_offset_percentage_;
  209. switch (power_button_position_) {
  210. case PowerButtonPosition::LEFT:
  211. case PowerButtonPosition::BOTTOM:
  212. left_power_button_y = bottom_power_button_x = power_button_offset;
  213. right_power_button_y = top_power_button_x =
  214. display_height - power_button_offset;
  215. break;
  216. case PowerButtonPosition::RIGHT:
  217. case PowerButtonPosition::TOP:
  218. left_power_button_y = bottom_power_button_x =
  219. display_height - power_button_offset;
  220. right_power_button_y = top_power_button_x = power_button_offset;
  221. break;
  222. default:
  223. NOTREACHED();
  224. return;
  225. }
  226. switch (power_button_position_) {
  227. case PowerButtonPosition::LEFT:
  228. left_screen_orientation = chromeos::OrientationType::kLandscapePrimary;
  229. right_screen_orientation = chromeos::OrientationType::kLandscapeSecondary;
  230. top_screen_orientation = chromeos::OrientationType::kPortraitPrimary;
  231. bottom_screen_orientation = chromeos::OrientationType::kPortraitSecondary;
  232. break;
  233. case PowerButtonPosition::RIGHT:
  234. left_screen_orientation = chromeos::OrientationType::kLandscapeSecondary;
  235. right_screen_orientation = chromeos::OrientationType::kLandscapePrimary;
  236. top_screen_orientation = chromeos::OrientationType::kPortraitSecondary;
  237. bottom_screen_orientation = chromeos::OrientationType::kPortraitPrimary;
  238. break;
  239. case PowerButtonPosition::TOP:
  240. left_screen_orientation = chromeos::OrientationType::kPortraitSecondary;
  241. right_screen_orientation = chromeos::OrientationType::kPortraitPrimary;
  242. top_screen_orientation = chromeos::OrientationType::kLandscapePrimary;
  243. bottom_screen_orientation =
  244. chromeos::OrientationType::kLandscapeSecondary;
  245. break;
  246. case PowerButtonPosition::BOTTOM:
  247. left_screen_orientation = chromeos::OrientationType::kPortraitPrimary;
  248. right_screen_orientation = chromeos::OrientationType::kPortraitSecondary;
  249. top_screen_orientation = chromeos::OrientationType::kLandscapeSecondary;
  250. bottom_screen_orientation = chromeos::OrientationType::kLandscapePrimary;
  251. break;
  252. default:
  253. NOTREACHED();
  254. return;
  255. }
  256. menu_bounds_origins_.clear();
  257. const gfx::Size menu_size = power_button_menu_view_->GetPreferredSize();
  258. // Power button position offset from the left when the button is at the left
  259. // is always zero.
  260. menu_bounds_origins_.insert(std::make_pair(
  261. left_screen_orientation,
  262. gfx::Point(PowerButtonMenuView::kMenuViewTransformDistanceDp,
  263. AdjustMenuEdgeForDisplaySize(
  264. left_power_button_y - menu_size.height() / 2,
  265. display_edge_for_adjust, menu_size.height()))));
  266. menu_bounds_origins_.insert(std::make_pair(
  267. right_screen_orientation,
  268. gfx::Point(display_width -
  269. PowerButtonMenuView::kMenuViewTransformDistanceDp -
  270. menu_size.width(),
  271. AdjustMenuEdgeForDisplaySize(
  272. right_power_button_y - menu_size.height() / 2,
  273. display_edge_for_adjust, menu_size.height()))));
  274. // Power button position offset from the top when the button is at the top
  275. // is always zero.
  276. menu_bounds_origins_.insert(std::make_pair(
  277. top_screen_orientation,
  278. gfx::Point(AdjustMenuEdgeForDisplaySize(
  279. top_power_button_x - menu_size.width() / 2,
  280. display_edge_for_adjust, menu_size.width()),
  281. PowerButtonMenuView::kMenuViewTransformDistanceDp)));
  282. menu_bounds_origins_.insert(std::make_pair(
  283. bottom_screen_orientation,
  284. gfx::Point(AdjustMenuEdgeForDisplaySize(
  285. bottom_power_button_x - menu_size.width() / 2,
  286. display_edge_for_adjust, menu_size.width()),
  287. display_width -
  288. PowerButtonMenuView::kMenuViewTransformDistanceDp -
  289. menu_size.height())));
  290. }
  291. gfx::Rect PowerButtonMenuScreenView::GetMenuBounds() {
  292. gfx::Rect menu_bounds;
  293. if (power_button_position_ == PowerButtonPosition::NONE ||
  294. !Shell::Get()->tablet_mode_controller()->InTabletMode()) {
  295. menu_bounds = GetContentsBounds();
  296. menu_bounds.ClampToCenteredSize(
  297. power_button_menu_view_->GetPreferredSize());
  298. } else {
  299. menu_bounds.set_origin(
  300. menu_bounds_origins_[Shell::Get()
  301. ->screen_orientation_controller()
  302. ->GetCurrentOrientation()]);
  303. menu_bounds.set_size(power_button_menu_view_->GetPreferredSize());
  304. }
  305. return menu_bounds;
  306. }
  307. } // namespace ash