drag_handle.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  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/shelf/drag_handle.h"
  5. #include "ash/accessibility/accessibility_controller_impl.h"
  6. #include "ash/constants/ash_features.h"
  7. #include "ash/controls/contextual_tooltip.h"
  8. #include "ash/public/cpp/shelf_config.h"
  9. #include "ash/session/session_controller_impl.h"
  10. #include "ash/shelf/shelf_layout_manager.h"
  11. #include "ash/shelf/shelf_observer.h"
  12. #include "ash/shelf/shelf_widget.h"
  13. #include "ash/strings/grit/ash_strings.h"
  14. #include "ash/style/ash_color_provider.h"
  15. #include "ash/wm/overview/overview_controller.h"
  16. #include "base/bind.h"
  17. #include "base/timer/timer.h"
  18. #include "ui/accessibility/ax_node_data.h"
  19. #include "ui/base/l10n/l10n_util.h"
  20. #include "ui/compositor/layer.h"
  21. #include "ui/compositor/scoped_layer_animation_settings.h"
  22. #include "ui/wm/core/coordinate_conversion.h"
  23. namespace ash {
  24. namespace {
  25. // Vertical padding to make the drag handle easier to tap.
  26. constexpr int kVerticalClickboxPadding = 15;
  27. // Drag handle translation distance for the first part of nudge animation.
  28. constexpr int kInAppToHomeNudgeVerticalMarginRise = -4;
  29. // Drag handle translation distance for the second part of nudge animation.
  30. constexpr int kInAppToHomeVerticalMarginDrop = 10;
  31. // Drag handle contextual nudge text box translation distance for the nudge
  32. // animation at the end.
  33. constexpr int kInAppToHomeNudgeVerticalMarginDrop = 8;
  34. // Animation time for each translation of drag handle to show contextual nudge.
  35. constexpr base::TimeDelta kInAppToHomeAnimationTime = base::Milliseconds(300);
  36. // Animation time to return drag handle to original position after hiding
  37. // contextual nudge.
  38. constexpr base::TimeDelta kInAppToHomeHideAnimationDuration =
  39. base::Milliseconds(600);
  40. // Animation time to return drag handle to original position after the user taps
  41. // to hide the contextual nudge.
  42. constexpr base::TimeDelta kInAppToHomeHideOnTapAnimationDuration =
  43. base::Milliseconds(100);
  44. // Delay between animating drag handle and tooltip opacity.
  45. constexpr base::TimeDelta kInAppToHomeNudgeOpacityDelay =
  46. base::Milliseconds(500);
  47. // Fade in time for drag handle nudge tooltip.
  48. constexpr base::TimeDelta kInAppToHomeNudgeOpacityAnimationDuration =
  49. base::Milliseconds(200);
  50. // Delay before animating the drag handle and showing the drag handle nudge.
  51. constexpr base::TimeDelta kShowNudgeDelay = base::Seconds(2);
  52. // This class is deleted after OnImplicitAnimationsCompleted() is called.
  53. class HideNudgeObserver : public ui::ImplicitAnimationObserver {
  54. public:
  55. explicit HideNudgeObserver(ContextualNudge* drag_handle_nudge)
  56. : drag_handle_nudge_(drag_handle_nudge) {}
  57. ~HideNudgeObserver() override = default;
  58. // ui::ImplicitAnimationObserver:
  59. void OnImplicitAnimationsCompleted() override {
  60. drag_handle_nudge_->GetWidget()->CloseWithReason(
  61. views::Widget::ClosedReason::kUnspecified);
  62. delete this;
  63. }
  64. private:
  65. ContextualNudge* const drag_handle_nudge_;
  66. };
  67. } // namespace
  68. DragHandle::DragHandle(float drag_handle_corner_radius, Shelf* shelf)
  69. : views::Button(base::BindRepeating(&DragHandle::ButtonPressed,
  70. base::Unretained(this))),
  71. shelf_(shelf) {
  72. SetPaintToLayer(ui::LAYER_SOLID_COLOR);
  73. layer()->SetRoundedCornerRadius(
  74. {drag_handle_corner_radius, drag_handle_corner_radius,
  75. drag_handle_corner_radius, drag_handle_corner_radius});
  76. SetSize(ShelfConfig::Get()->DragHandleSize());
  77. SetEventTargeter(std::make_unique<views::ViewTargeter>(this));
  78. SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
  79. shell_observation_.Observe(Shell::Get());
  80. Shell::Get()->accessibility_controller()->AddObserver(this);
  81. shelf_->AddObserver(this);
  82. OnAccessibilityStatusChanged();
  83. }
  84. DragHandle::~DragHandle() {
  85. StopObservingImplicitAnimations();
  86. Shell::Get()->accessibility_controller()->RemoveObserver(this);
  87. shelf_->RemoveObserver(this);
  88. }
  89. bool DragHandle::DoesIntersectRect(const views::View* target,
  90. const gfx::Rect& rect) const {
  91. DCHECK_EQ(target, this);
  92. gfx::Rect drag_handle_bounds = target->GetLocalBounds();
  93. drag_handle_bounds.set_y(drag_handle_bounds.y() - kVerticalClickboxPadding);
  94. drag_handle_bounds.set_height(drag_handle_bounds.height() +
  95. 2 * kVerticalClickboxPadding);
  96. return drag_handle_bounds.Intersects(rect);
  97. }
  98. bool DragHandle::MaybeShowDragHandleNudge() {
  99. // Stop observing overview state if nudge show timer has fired.
  100. if (!show_drag_handle_nudge_timer_.IsRunning())
  101. overview_observation_.Reset();
  102. if (!features::AreContextualNudgesEnabled())
  103. return false;
  104. // Do not show drag handle nudge if it is already shown or drag handle is not
  105. // visible.
  106. if (gesture_nudge_target_visibility() ||
  107. window_drag_from_shelf_in_progress_ || !GetVisible() ||
  108. SplitViewController::Get(shelf_->shelf_widget()->GetNativeWindow())
  109. ->InSplitViewMode()) {
  110. return false;
  111. }
  112. show_nudge_animation_in_progress_ = true;
  113. auto_hide_lock_ = std::make_unique<Shelf::ScopedAutoHideLock>(shelf_);
  114. StopDragHandleNudgeShowTimer();
  115. ShowDragHandleNudge();
  116. return true;
  117. }
  118. void DragHandle::ShowDragHandleNudge() {
  119. DCHECK(!gesture_nudge_target_visibility_);
  120. PrefService* pref =
  121. Shell::Get()->session_controller()->GetLastActiveUserPrefService();
  122. base::TimeDelta nudge_duration = contextual_tooltip::GetNudgeTimeout(
  123. pref, contextual_tooltip::TooltipType::kInAppToHome);
  124. AnimateDragHandleShow();
  125. ShowDragHandleTooltip();
  126. gesture_nudge_target_visibility_ = true;
  127. split_view_observation_.Observe(
  128. SplitViewController::Get(shelf_->shelf_widget()->GetNativeWindow()));
  129. if (!nudge_duration.is_zero()) {
  130. hide_drag_handle_nudge_timer_.Start(
  131. FROM_HERE, nudge_duration,
  132. base::BindOnce(&DragHandle::HideDragHandleNudge, base::Unretained(this),
  133. contextual_tooltip::DismissNudgeReason::kTimeout));
  134. }
  135. contextual_tooltip::HandleNudgeShown(
  136. pref, contextual_tooltip::TooltipType::kInAppToHome);
  137. }
  138. void DragHandle::ScheduleShowDragHandleNudge() {
  139. if (gesture_nudge_target_visibility_ ||
  140. show_drag_handle_nudge_timer_.IsRunning() ||
  141. window_drag_from_shelf_in_progress_ ||
  142. Shell::Get()->overview_controller()->InOverviewSession()) {
  143. return;
  144. }
  145. // Observe overview controller to detect overview session start - this should
  146. // cancel the scheduled nudge show.
  147. overview_observation_.Observe(Shell::Get()->overview_controller());
  148. show_drag_handle_nudge_timer_.Start(
  149. FROM_HERE, kShowNudgeDelay,
  150. base::BindOnce(base::IgnoreResult(&DragHandle::MaybeShowDragHandleNudge),
  151. base::Unretained(this)));
  152. }
  153. void DragHandle::HideDragHandleNudge(
  154. contextual_tooltip::DismissNudgeReason reason) {
  155. StopDragHandleNudgeShowTimer();
  156. if (!gesture_nudge_target_visibility())
  157. return;
  158. split_view_observation_.Reset();
  159. hide_drag_handle_nudge_timer_.Stop();
  160. if (reason == contextual_tooltip::DismissNudgeReason::kPerformedGesture) {
  161. contextual_tooltip::HandleGesturePerformed(
  162. Shell::Get()->session_controller()->GetLastActiveUserPrefService(),
  163. contextual_tooltip::TooltipType::kInAppToHome);
  164. }
  165. HideDragHandleNudgeHelper(/*hidden_by_tap=*/reason ==
  166. contextual_tooltip::DismissNudgeReason::kTap);
  167. gesture_nudge_target_visibility_ = false;
  168. }
  169. void DragHandle::SetWindowDragFromShelfInProgress(bool gesture_in_progress) {
  170. if (window_drag_from_shelf_in_progress_ == gesture_in_progress)
  171. return;
  172. window_drag_from_shelf_in_progress_ = gesture_in_progress;
  173. // If the contextual nudge is not yet shown, make sure that any scheduled
  174. // nudge show request is canceled.
  175. if (!gesture_nudge_target_visibility()) {
  176. StopDragHandleNudgeShowTimer();
  177. return;
  178. }
  179. // If the drag handle nudge is shown when the gesture to home or overview
  180. // starts, keep it around until the gesture completes.
  181. if (window_drag_from_shelf_in_progress_) {
  182. hide_drag_handle_nudge_timer_.Stop();
  183. } else {
  184. HideDragHandleNudge(
  185. contextual_tooltip::DismissNudgeReason::kPerformedGesture);
  186. }
  187. }
  188. void DragHandle::UpdateColor() {
  189. layer()->SetColor(AshColorProvider::Get()->GetContentLayerColor(
  190. AshColorProvider::ContentLayerType::kShelfHandleColor));
  191. }
  192. void DragHandle::OnGestureEvent(ui::GestureEvent* event) {
  193. if (!features::AreContextualNudgesEnabled() ||
  194. !gesture_nudge_target_visibility_) {
  195. return;
  196. }
  197. if (event->type() == ui::ET_GESTURE_TAP) {
  198. HandleTapOnNudge();
  199. event->StopPropagation();
  200. }
  201. }
  202. gfx::Rect DragHandle::GetAnchorBoundsInScreen() const {
  203. gfx::Rect anchor_bounds = ConvertRectToWidget(GetLocalBounds());
  204. // Ignore any transform set on the drag handle - drag handle is used as an
  205. // anchor for contextual nudges, and their bounds are set relative to the
  206. // handle bounds without transform (for example, for in-app to home nudge both
  207. // drag handle and the nudge will have non-indentity, identical transforms).
  208. gfx::Point origin_in_screen = anchor_bounds.origin();
  209. layer()->transform().TransformPointReverse(&origin_in_screen);
  210. // If the parent widget has a transform set, it should be ignored as well (the
  211. // transform is set during shelf widget animations, and will animate to
  212. // identity transform), so the nudge bounds are set relative to the target
  213. // shelf bounds.
  214. aura::Window* const widget_window = GetWidget()->GetNativeWindow();
  215. origin_in_screen += widget_window->bounds().origin().OffsetFromOrigin();
  216. wm::ConvertPointToScreen(widget_window->parent(), &origin_in_screen);
  217. anchor_bounds.set_origin(origin_in_screen);
  218. return anchor_bounds;
  219. }
  220. void DragHandle::GetAccessibleNodeData(ui::AXNodeData* node_data) {
  221. Button::GetAccessibleNodeData(node_data);
  222. std::u16string accessible_name = std::u16string();
  223. switch (shelf_->shelf_layout_manager()->hotseat_state()) {
  224. case HotseatState::kNone:
  225. case HotseatState::kShownClamshell:
  226. case HotseatState::kShownHomeLauncher:
  227. break;
  228. case HotseatState::kHidden:
  229. accessible_name = l10n_util::GetStringUTF16(
  230. IDS_ASH_DRAG_HANDLE_HOTSEAT_SHOW_ACCESSIBLE_NAME);
  231. break;
  232. case HotseatState::kExtended:
  233. // The name should be empty when the hotseat is extended but we cannot
  234. // hide it.
  235. if (force_show_hotseat_resetter_)
  236. accessible_name = l10n_util::GetStringUTF16(
  237. IDS_ASH_DRAG_HANDLE_HOTSEAT_HIDE_ACCESSIBLE_NAME);
  238. break;
  239. }
  240. node_data->SetName(accessible_name);
  241. }
  242. void DragHandle::OnThemeChanged() {
  243. views::Button::OnThemeChanged();
  244. UpdateColor();
  245. }
  246. void DragHandle::OnOverviewModeStarting() {
  247. StopDragHandleNudgeShowTimer();
  248. }
  249. void DragHandle::OnShellDestroying() {
  250. shell_observation_.Reset();
  251. // Removes the overview controller observer.
  252. StopDragHandleNudgeShowTimer();
  253. hide_drag_handle_nudge_timer_.Stop();
  254. }
  255. void DragHandle::OnSplitViewStateChanged(
  256. SplitViewController::State previous_state,
  257. SplitViewController::State state) {
  258. if (SplitViewController::Get(shelf_->shelf_widget()->GetNativeWindow())
  259. ->InSplitViewMode()) {
  260. HideDragHandleNudge(contextual_tooltip::DismissNudgeReason::kOther);
  261. }
  262. }
  263. void DragHandle::OnHotseatStateChanged(HotseatState old_state,
  264. HotseatState new_state) {
  265. // Reset |force_show_hotseat_resetter_| when it is no longer extended.
  266. if (force_show_hotseat_resetter_ && new_state != HotseatState::kExtended) {
  267. shelf_->hotseat_widget()->set_manually_extended(false);
  268. force_show_hotseat_resetter_.RunAndReset();
  269. }
  270. }
  271. void DragHandle::OnAccessibilityStatusChanged() {
  272. // Only enable the button if shelf controls are shown for accessibility.
  273. views::View::SetEnabled(
  274. ShelfConfig::Get()->ShelfControlsForcedShownForAccessibility());
  275. }
  276. void DragHandle::ButtonPressed() {
  277. if (shelf_->shelf_layout_manager()->hotseat_state() ==
  278. HotseatState::kHidden) {
  279. force_show_hotseat_resetter_ =
  280. shelf_->shelf_widget()->ForceShowHotseatInTabletMode();
  281. } else if (force_show_hotseat_resetter_) {
  282. // Hide hotseat only if it's been brought up by tapping the drag handle.
  283. shelf_->hotseat_widget()->set_manually_extended(false);
  284. force_show_hotseat_resetter_.RunAndReset();
  285. }
  286. }
  287. void DragHandle::OnImplicitAnimationsCompleted() {
  288. show_nudge_animation_in_progress_ = false;
  289. auto_hide_lock_.reset();
  290. }
  291. void DragHandle::ShowDragHandleTooltip() {
  292. DCHECK(!drag_handle_nudge_);
  293. drag_handle_nudge_ = new ContextualNudge(
  294. this, nullptr /*parent_window*/, ContextualNudge::Position::kTop,
  295. gfx::Insets(), l10n_util::GetStringUTF16(IDS_ASH_DRAG_HANDLE_NUDGE),
  296. AshColorProvider::Get()->GetContentLayerColor(
  297. AshColorProvider::ContentLayerType::kTextColorPrimary),
  298. base::BindRepeating(&DragHandle::HandleTapOnNudge,
  299. weak_factory_.GetWeakPtr()));
  300. drag_handle_nudge_->GetWidget()->Show();
  301. drag_handle_nudge_->label()->layer()->SetOpacity(0.0f);
  302. {
  303. // Layer transform should be animated after a delay so the animator must
  304. // first schedules a pause for transform animation.
  305. ui::LayerAnimator* transform_animator =
  306. drag_handle_nudge_->GetWidget()->GetLayer()->GetAnimator();
  307. transform_animator->SchedulePauseForProperties(
  308. kInAppToHomeAnimationTime, ui::LayerAnimationElement::TRANSFORM);
  309. // Enqueue transform animation to start after pause.
  310. ui::ScopedLayerAnimationSettings transform_animation_settings(
  311. transform_animator);
  312. transform_animation_settings.SetTweenType(gfx::Tween::FAST_OUT_LINEAR_IN);
  313. transform_animation_settings.SetTransitionDuration(
  314. kInAppToHomeAnimationTime);
  315. transform_animation_settings.SetPreemptionStrategy(
  316. ui::LayerAnimator::ENQUEUE_NEW_ANIMATION);
  317. // gfx::Transform translate;
  318. gfx::Transform translate;
  319. translate.Translate(0, kInAppToHomeNudgeVerticalMarginDrop);
  320. drag_handle_nudge_->GetWidget()->GetLayer()->SetTransform(translate);
  321. }
  322. {
  323. // Layer opacity should be animated after a delay so the animator must first
  324. // schedules a pause for opacity animation.
  325. ui::LayerAnimator* opacity_animator =
  326. drag_handle_nudge_->label()->layer()->GetAnimator();
  327. opacity_animator->SchedulePauseForProperties(
  328. kInAppToHomeNudgeOpacityDelay, ui::LayerAnimationElement::OPACITY);
  329. // Enqueue opacity animation to start after pause.
  330. ui::ScopedLayerAnimationSettings opacity_animation_settings(
  331. opacity_animator);
  332. opacity_animation_settings.SetPreemptionStrategy(
  333. ui::LayerAnimator::ENQUEUE_NEW_ANIMATION);
  334. opacity_animation_settings.SetTweenType(gfx::Tween::LINEAR);
  335. opacity_animation_settings.SetTransitionDuration(
  336. kInAppToHomeNudgeOpacityAnimationDuration);
  337. opacity_animation_settings.AddObserver(this);
  338. drag_handle_nudge_->label()->layer()->SetOpacity(1.0f);
  339. }
  340. }
  341. void DragHandle::HideDragHandleNudgeHelper(bool hidden_by_tap) {
  342. ScheduleDragHandleTranslationAnimation(
  343. 0,
  344. hidden_by_tap ? kInAppToHomeHideOnTapAnimationDuration
  345. : kInAppToHomeHideAnimationDuration,
  346. hidden_by_tap ? gfx::Tween::FAST_OUT_LINEAR_IN
  347. : gfx::Tween::FAST_OUT_SLOW_IN,
  348. ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
  349. if (drag_handle_nudge_) {
  350. ui::LayerAnimator* opacity_animator =
  351. drag_handle_nudge_->label()->layer()->GetAnimator();
  352. ui::ScopedLayerAnimationSettings opacity_animation_settings(
  353. opacity_animator);
  354. opacity_animation_settings.SetPreemptionStrategy(
  355. ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
  356. opacity_animation_settings.SetTweenType(gfx::Tween::LINEAR);
  357. opacity_animation_settings.SetTransitionDuration(
  358. hidden_by_tap ? kInAppToHomeHideOnTapAnimationDuration
  359. : kInAppToHomeNudgeOpacityAnimationDuration);
  360. // Register an animation observer to close the tooltip widget once the label
  361. // opacity is animated to 0 as the widget will no longer be needed after
  362. // this point.
  363. opacity_animation_settings.AddObserver(
  364. new HideNudgeObserver(drag_handle_nudge_));
  365. drag_handle_nudge_->label()->layer()->SetOpacity(0.0f);
  366. drag_handle_nudge_ = nullptr;
  367. }
  368. }
  369. void DragHandle::AnimateDragHandleShow() {
  370. // Drag handle is animated in two steps that run in sequence. The first step
  371. // uses |IMMEDIATELY_ANIMATE_TO_NEW_TARGET| to preempt any in-progress
  372. // animations while the second step uses |ENQUEUE_NEW_ANIMATION| so it runs
  373. // after the first animation.
  374. ScheduleDragHandleTranslationAnimation(
  375. kInAppToHomeNudgeVerticalMarginRise, kInAppToHomeAnimationTime,
  376. gfx::Tween::FAST_OUT_SLOW_IN,
  377. ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
  378. ScheduleDragHandleTranslationAnimation(
  379. kInAppToHomeVerticalMarginDrop, kInAppToHomeAnimationTime,
  380. gfx::Tween::FAST_OUT_LINEAR_IN, ui::LayerAnimator::ENQUEUE_NEW_ANIMATION);
  381. }
  382. void DragHandle::ScheduleDragHandleTranslationAnimation(
  383. int vertical_offset,
  384. base::TimeDelta animation_time,
  385. gfx::Tween::Type tween_type,
  386. ui::LayerAnimator::PreemptionStrategy strategy) {
  387. ui::ScopedLayerAnimationSettings animation(layer()->GetAnimator());
  388. animation.SetTweenType(tween_type);
  389. animation.SetTransitionDuration(animation_time);
  390. animation.SetPreemptionStrategy(strategy);
  391. gfx::Transform translate;
  392. translate.Translate(0, vertical_offset);
  393. SetTransform(translate);
  394. }
  395. void DragHandle::HandleTapOnNudge() {
  396. if (!drag_handle_nudge_)
  397. return;
  398. HideDragHandleNudge(contextual_tooltip::DismissNudgeReason::kTap);
  399. }
  400. void DragHandle::StopDragHandleNudgeShowTimer() {
  401. show_drag_handle_nudge_timer_.Stop();
  402. overview_observation_.Reset();
  403. }
  404. } // namespace ash