chromium/ash/display/display_alignment_controller.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_DISPLAY_DISPLAY_ALIGNMENT_CONTROLLER_H_
#define ASH_DISPLAY_DISPLAY_ALIGNMENT_CONTROLLER_H_

#include <memory>

#include "ash/ash_export.h"
#include "ash/public/cpp/session/session_observer.h"
#include "ui/display/manager/display_manager_observer.h"
#include "ui/events/event_handler.h"
#include "ui/gfx/geometry/vector2d.h"

namespace base {
class OneShotTimer;
}  // namespace base

namespace ash {

class DisplayAlignmentIndicator;

// DisplayAlignmentController is responsible for creating new
// DisplayAlignmentIndicators when the activation criteria is met.
// TODO(1091497): Consider combining DisplayHighlightController and
// DisplayAlignmentController.
class ASH_EXPORT DisplayAlignmentController
    : public ui::EventHandler,
      public display::DisplayManagerObserver,
      public SessionObserver {
 public:
  enum class DisplayAlignmentState {
    // No indicators shown and mouse is not on edge
    kIdle,

    // Mouse is currently on one of the edges.
    kOnEdge,

    // The indicators are visible.
    kIndicatorsVisible,

    // A display is being dragged around in display layouts. Preview indicators
    // are being updated and shown.
    kLayoutPreview,

    // Screen is locked or there is only one display.
    kDisabled,
  };

  DisplayAlignmentController();
  DisplayAlignmentController(const DisplayAlignmentController&) = delete;
  DisplayAlignmentController& operator=(const DisplayAlignmentController&) =
      delete;
  ~DisplayAlignmentController() override;

  // display::DisplayManagerObserver
  void OnDidApplyDisplayChanges() override;
  void OnDisplaysInitialized() override;

  // ui::EventHandler:
  void OnMouseEvent(ui::MouseEvent* event) override;

  // SessionObserver:
  void OnLockStateChanged(bool locked) override;

  // Update positions of display alignment preview highlights. Display being
  // dragged is specified by |display_id|. |preview_indicators_| is
  // populated with indicators from this display and its neighbors as
  // it is not possible for |display_id| to change mid-drag.
  void DisplayDragged(int64_t display_id, int32_t delta_x, int32_t delta_y);

  void SetTimerForTesting(std::unique_ptr<base::OneShotTimer> timer);

  const std::vector<std::unique_ptr<DisplayAlignmentIndicator>>&
  GetActiveIndicatorsForTesting();

  int64_t GetDraggedDisplayIdForTesting() const;

 private:
  // Show all indicators on |src_display| and other indicators that shares
  // an edge with |src_display|. Indicators on other displays are shown without
  // pills. All indicators are created in this method and stored in
  // |active_indicators_| to be destroyed in ResetState().
  void ShowIndicators(const display::Display& src_display);

  // Clears all indicators, containers, timer, and resets the state back to
  // kIdle.
  void ResetState();

  // Used to transition to kDisable if required. Called whenever display
  // configuration or lock state updates.
  void RefreshState();

  // Updates, shows/hides preview indicators according to changes reported by
  // DisplayDragged().
  void ComputePreviewIndicators();

  // Stores all DisplayAlignmentIndicators currently being shown. All indicators
  // should either belong to or be a shared edge of display with
  // |triggered_display_id_|. Indicators are created upon activation in
  // ShowIndicators() or upon adjusting display layout in
  // ComputePreviewIndicators() and cleared in ResetState().
  std::vector<std::unique_ptr<DisplayAlignmentIndicator>> active_indicators_;

  // Timer used for both edge trigger timeouts and hiding indicators.
  std::unique_ptr<base::OneShotTimer> action_trigger_timer_;

  // Tracks current state of the controller. Mostly used to determine if action
  // is taken in OnMouseEvent();
  DisplayAlignmentState current_state_ = DisplayAlignmentState::kIdle;

  // Tracks if the screen is locked to disable highlights.
  bool is_locked_ = false;

  // Keeps track of the most recent display where the mouse hit the edge.
  // Prevents activating indicators when user hits edges of different displays.
  int64_t triggered_display_id_ = display::kInvalidDisplayId;

  // Number of times the mouse was on an edge of some display specified by
  // |triggered_display_id_| recently.
  int trigger_count_ = 0;

  // ID of display currently beign dragged. Cannot change from one valid
  // ID to another as dropping the dragged display causes changes to display
  // configuration, resetting this ID.
  int64_t dragged_display_id_;

  // The difference between dragged display's actual position and preview
  // position. Is an accumulation of |delta_x| and |delta_y| from
  // DisplayDragged(). The offset is reset when a configuration event occurs.
  gfx::Vector2d dragged_offset_;
};

}  // namespace ash

#endif  // ASH_DISPLAY_DISPLAY_ALIGNMENT_CONTROLLER_H_