#ifndef ASH_WM_TILE_GROUP_WINDOW_SPLITTER_H_
#define ASH_WM_TILE_GROUP_WINDOW_SPLITTER_H_
#include <memory>
#include <optional>
#include "ash/ash_export.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "ui/aura/window_observer.h"
#include "ui/events/velocity_tracker/velocity_tracker.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/rect.h"
namespace aura {
class Window;
}
namespace ash {
class PhantomWindowController;
class ASH_EXPORT WindowSplitter : public aura::WindowObserver {
public:
enum class SplitRegion {
kNone = 0,
kLeft = 1,
kRight = 2,
kTop = 3,
kBottom = 4,
kMaxValue = kBottom,
};
enum class DragType {
kIncomplete = 0,
kNoSplit = 1,
kSplit = 2,
kMaxValue = kSplit,
};
struct SplitWindowInfo {
gfx::Rect topmost_window_bounds;
gfx::Rect dragged_window_bounds;
SplitRegion split_region = SplitRegion::kNone;
bool operator==(const SplitWindowInfo&) const;
};
static constexpr gfx::Insets kBaseTriggerMargins = gfx::Insets::VH(35, 45);
static constexpr base::TimeDelta kDwellActivationDuration =
base::Milliseconds(450);
static constexpr base::TimeDelta kDwellCancellationDuration =
base::Milliseconds(1500);
static constexpr double kDwellMaxVelocityPixelsPerSec = 60.0;
static std::optional<SplitWindowInfo> MaybeSplitWindow(
aura::Window* topmost_window,
aura::Window* dragged_window,
const gfx::PointF& screen_location);
explicit WindowSplitter(aura::Window* dragged_window);
WindowSplitter(const WindowSplitter&) = delete;
WindowSplitter& operator=(const WindowSplitter&) = delete;
~WindowSplitter() override;
void UpdateDrag(const gfx::PointF& location_in_screen, bool can_split);
void CompleteDrag(const gfx::PointF& last_location_in_screen);
void Disengage();
void OnWindowDestroying(aura::Window* window) override;
const PhantomWindowController* GetPhantomWindowControllerForTesting() const {
return phantom_window_controller_.get();
}
private:
aura::Window* dragged_window() {
return dragged_window_observation_.GetSource();
}
aura::Window* topmost_window() {
return topmost_window_observation_.GetSource();
}
void UpdateTopMostWindow(aura::Window* topmost_window);
void RestartDwellTimer();
void RemovePhantomWindow();
void ShowPhantomWindowCallback();
void ShowPhantomWindow(const gfx::Rect& bounds);
bool ReadyToSplit() const { return !!phantom_window_controller_; }
void RecordMetricsOnEndDrag();
DragType GetDragType() const;
void UpdateCursorLocation(const gfx::PointF& location_in_screen);
double GetCursorVelocitySquared() const;
base::ScopedObservation<aura::Window, aura::WindowObserver>
dragged_window_observation_{this};
base::ScopedObservation<aura::Window, aura::WindowObserver>
topmost_window_observation_{this};
gfx::PointF last_location_in_screen_;
std::optional<SplitWindowInfo> last_split_window_info_;
bool is_drag_updated_ = false;
bool is_drag_completed_ = false;
SplitRegion completed_split_region_ = SplitRegion::kNone;
std::unique_ptr<PhantomWindowController> phantom_window_controller_;
uint32_t phantom_window_shown_count_ = 0;
const base::TimeTicks drag_start_time_;
base::OneShotTimer dwell_activation_timer_;
base::OneShotTimer dwell_cancellation_timer_;
ui::VelocityTracker velocity_tracker_;
base::WeakPtrFactory<WindowSplitter> weak_ptr_factory_{this};
};
}
#endif