chromium/ash/shelf/drag_handle.h

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_SHELF_DRAG_HANDLE_H_
#define ASH_SHELF_DRAG_HANDLE_H_

#include "ash/ash_export.h"
#include "ash/controls/contextual_nudge.h"
#include "ash/controls/contextual_tooltip.h"
#include "ash/shelf/shelf.h"
#include "ash/shell_observer.h"
#include "ash/wm/overview/overview_observer.h"
#include "ash/wm/splitview/split_view_controller.h"
#include "ash/wm/splitview/split_view_observer.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/timer/timer.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/compositor/layer_animator.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/view_targeter_delegate.h"

namespace ash {

class OverviewController;
class Shell;

class ASH_EXPORT DragHandle : public views::Button,
                              public views::ViewTargeterDelegate,
                              public AccessibilityObserver,
                              public OverviewObserver,
                              public ShellObserver,
                              public ui::ImplicitAnimationObserver,
                              public SplitViewObserver,
                              public ShelfObserver {
  METADATA_HEADER(DragHandle, views::Button)

 public:
  DragHandle(float drag_handle_corner_radius, Shelf* shelf);
  DragHandle(const DragHandle&) = delete;
  ~DragHandle() override;

  DragHandle& operator=(const DragHandle&) = delete;

  // views::ViewTargeterDelegate:
  bool DoesIntersectRect(const views::View* target,
                         const gfx::Rect& rect) const override;

  // Checks whether the drag handle nudge should be shown. Handles the
  // |show_drag_handle_nudge_timer_|, |nudge_visible_|, and
  // |show_nudge_animation_in_progress_|.
  // Returns whether the nudge will be shown.
  bool MaybeShowDragHandleNudge();

  // Animates drag handle and tooltip for drag handle teaching users that
  // swiping up on will take the user back to the home screen.
  void ShowDragHandleNudge();

  // Schedule showing the drag handle.
  void ScheduleShowDragHandleNudge();

  // Immediately begins the animation to return the drag handle back to its
  // original position and hide the tooltip.
  void HideDragHandleNudge(contextual_tooltip::DismissNudgeReason reason,
                           bool animate);

  // Called when the window drag from shelf starts or ends. The drag handle
  // contextual nudge will remain visible while the gesture is in progress.
  void SetWindowDragFromShelfInProgress(bool gesture_in_progress);

  // views::View:
  void OnGestureEvent(ui::GestureEvent* event) override;
  gfx::Rect GetAnchorBoundsInScreen() const override;
  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
  void OnThemeChanged() override;

  // OverviewObserver:
  void OnOverviewModeStarting() override;

  // ShellObserver:
  void OnShellDestroying() override;

  // SplitViewObserver:
  void OnSplitViewStateChanged(SplitViewController::State previous_state,
                               SplitViewController::State state) override;

  // ShelfObserver:
  void OnHotseatStateChanged(HotseatState old_state,
                             HotseatState new_state) override;

  ContextualNudge* drag_handle_nudge() { return drag_handle_nudge_; }

  bool gesture_nudge_target_visibility() const {
    return gesture_nudge_target_visibility_;
  }

  bool show_nudge_animation_in_progress() const {
    return show_nudge_animation_in_progress_;
  }

  bool has_show_drag_handle_timer_for_testing() {
    return show_drag_handle_nudge_timer_.IsRunning();
  }

  void fire_show_drag_handle_timer_for_testing() {
    show_drag_handle_nudge_timer_.FireNow();
  }

  bool has_hide_drag_handle_timer_for_testing() {
    return hide_drag_handle_nudge_timer_.IsRunning();
  }

  void fire_hide_drag_handle_timer_for_testing() {
    hide_drag_handle_nudge_timer_.FireNow();
  }

 private:
  // AccessibilityObserver:
  void OnAccessibilityStatusChanged() override;

  // Show/hide hotseat in tablet mode. This is only available when spoken
  // feedback is enabled.
  void ButtonPressed();

  // ui::ImplicitAnimationObserver:
  void OnImplicitAnimationsCompleted() override;

  // Animates tooltip for drag handle gesture.
  void ShowDragHandleTooltip();

  // Helper function to hide the drag handle nudge. Called by
  // |hide_drag_handle_nudge_timer_|.
  void HideDragHandleNudgeHelper(bool hidden_by_tap, bool animate);

  // Helper function to animate the drag handle for the drag handle gesture
  // contextual nudge.
  void AnimateDragHandleShow();

  // Animates translation of the drag handle by |vertical_offset| over
  // |animation_time| using |strategy|.
  void ScheduleDragHandleTranslationAnimation(
      int vertical_offset,
      base::TimeDelta animation_time,
      gfx::Tween::Type tween_type,
      ui::LayerAnimator::PreemptionStrategy strategy);

  // Handler for tap gesture on the contextual nudge widget. It hides the nudge.
  void HandleTapOnNudge();

  // Stops the timer to show the drag handle nudge.
  void StopDragHandleNudgeShowTimer();

  // Sets accessible states of the view.
  void UpdateExpandedCollapsedAccessibleState() const;

  // Pointer to the shelf that owns the drag handle.
  const raw_ptr<Shelf> shelf_;

  // Timer to hide drag handle nudge if it has a timed life.
  base::OneShotTimer hide_drag_handle_nudge_timer_;

  // Timer to animate the drag handle and show the nudge.
  base::OneShotTimer show_drag_handle_nudge_timer_;

  // Tracks the target visibility of the gesture nudge.
  bool gesture_nudge_target_visibility_ = false;

  // Tracks whether the in app shelf to home nudge is animating to the visible
  // state. Set to true when animation starts, set to false when animation
  // completes.
  bool show_nudge_animation_in_progress_ = false;

  // Whether window drag from shelf (i.e. gesture from in-app shelf to home or
  // overview) is currently in progress. If the contextual nudge is shown when
  // the gesture starts, it should remain shown until the gesture ends.
  // Set by ShelfLayoutManager using SetWindowDragFromShelfInProgress().
  bool window_drag_from_shelf_in_progress_ = false;

  // A label used to educate users about swipe gestures on the drag handle.
  raw_ptr<ContextualNudge> drag_handle_nudge_ = nullptr;

  std::unique_ptr<Shelf::ScopedAutoHideLock> auto_hide_lock_;

  base::ScopedClosureRunner force_show_hotseat_resetter_;

  base::ScopedObservation<SplitViewController, SplitViewObserver>
      split_view_observation_{this};

  base::ScopedObservation<OverviewController, OverviewObserver>
      overview_observation_{this};

  base::ScopedObservation<Shell, ShellObserver> shell_observation_{this};

  base::WeakPtrFactory<DragHandle> weak_factory_{this};
};

}  // namespace ash

#endif  // ASH_SHELF_DRAG_HANDLE_H_