packages feed

tinytools-0.1.0.4: src/Potato/Flow/Controller/Input.hs

{-# LANGUAGE RecordWildCards #-}

module Potato.Flow.Controller.Input (
  KeyModifier(..)
  , KeyboardData(..)
  , KeyboardKey(..)
  , MouseButton(..)
  , MouseDragState(..)
  , LMouseData(..)
  , MouseDrag(..)
  , mouseDrag_isActive
  , newDrag
  , continueDrag
  , cancelDrag
  , mouseDragDelta
  , RelMouseDrag(..)
  , toRelMouseDrag
) where

import           Relude

import           Potato.Flow.Math
import           Potato.Flow.OwlState

import           Control.Exception    (assert)
import           Data.Default

-- TODO consider adding space as a modifier so you can do space drag panning in web version
data KeyModifier = KeyModifier_Shift | KeyModifier_Alt | KeyModifier_Ctrl deriving (Show, Eq)

-- KEYBOARD
data KeyboardData = KeyboardData KeyboardKey [KeyModifier] deriving (Show)

data KeyboardKey =
  KeyboardKey_Esc
  | KeyboardKey_Return
  | KeyboardKey_Space
  | KeyboardKey_Delete
  | KeyboardKey_Backspace
  | KeyboardKey_Left
  | KeyboardKey_Right
  | KeyboardKey_Up
  | KeyboardKey_Down
  | KeyboardKey_Home
  | KeyboardKey_End
  | KeyboardKey_PageUp
  | KeyboardKey_PageDown
  | KeyboardKey_Char Char
  -- not really a keypress but it's fine to put it here
  | KeyboardKey_Paste Text

  -- easier to handle as keyboard input
  | KeyboardKey_Scroll Int
  deriving (Show, Eq)

-- MOUSE
data MouseButton = MouseButton_Left | MouseButton_Middle | MouseButton_Right deriving (Show, Eq)

data MouseDragState = MouseDragState_Down | MouseDragState_Dragging | MouseDragState_Up | MouseDragState_Cancelled deriving (Show, Eq)


-- TODO is there a way to optionally support more fidelity here?
-- NOTE mouse drags are sent as click streams
data LMouseData = LMouseData {
  _lMouseData_position       :: XY
  , _lMouseData_isRelease    :: Bool
  , _lMouseData_button       :: MouseButton
  , _lMouseData_modifiers    :: [KeyModifier]
  -- TODO get rid of this, instead split input into 2
  , _lMouseData_isLayerMouse :: Bool
} deriving (Show, Eq)

data MouseDrag = MouseDrag {
  _mouseDrag_from           :: XY
  , _mouseDrag_button       :: MouseButton -- tracks button on start of drag
  , _mouseDrag_modifiers    :: [KeyModifier] -- tracks modifiers held at current state of drag
  , _mouseDrag_to           :: XY -- likely not needed as they will be in the input event, but whatever
  , _mouseDrag_state        :: MouseDragState
  -- TODO get rid of this, instead split input into 2
  , _mouseDrag_isLayerMouse :: Bool
} deriving (Show, Eq)

mouseDrag_isActive :: MouseDrag -> Bool
mouseDrag_isActive MouseDrag {..} = case _mouseDrag_state of
  MouseDragState_Down     -> True
  MouseDragState_Dragging -> True
  _                       -> False

instance Default MouseDrag where
  def = MouseDrag {
      _mouseDrag_from  = 0
      , _mouseDrag_button = MouseButton_Left
      , _mouseDrag_modifiers = []
      , _mouseDrag_to    = 0
      , _mouseDrag_state = MouseDragState_Up -- if the last state was MouseDragState_Up we are ready to process more inputs fresh
      , _mouseDrag_isLayerMouse = False
    }

newDrag :: LMouseData -> MouseDrag
newDrag LMouseData {..} = assert (not _lMouseData_isRelease) $ MouseDrag {
    _mouseDrag_from = _lMouseData_position
    , _mouseDrag_button = _lMouseData_button
    , _mouseDrag_modifiers = _lMouseData_modifiers
    , _mouseDrag_to = _lMouseData_position
    , _mouseDrag_state = MouseDragState_Down
    , _mouseDrag_isLayerMouse = _lMouseData_isLayerMouse
  }

continueDrag :: LMouseData -> MouseDrag -> MouseDrag
continueDrag LMouseData {..} md = md {
    _mouseDrag_to = _lMouseData_position
    , _mouseDrag_state = if _lMouseData_isRelease
      then MouseDragState_Up
      else MouseDragState_Dragging
    , _mouseDrag_modifiers = _lMouseData_modifiers
  }

cancelDrag :: MouseDrag -> MouseDrag
cancelDrag md = md { _mouseDrag_state = case _mouseDrag_state md of
    MouseDragState_Up -> MouseDragState_Up
    _                 -> MouseDragState_Cancelled
  }

-- wats this for D:?
mouseDragDelta :: MouseDrag -> MouseDrag -> XY
mouseDragDelta md prev = (_mouseDrag_to md) - (_mouseDrag_to prev)

-- TODO rename to CanvasMouseDrag
-- represents MouseDrag in local coordinates (e.g. canvas coordinates)
newtype RelMouseDrag = RelMouseDrag MouseDrag deriving (Show)

toRelMouseDrag :: OwlPFState -> XY -> MouseDrag -> RelMouseDrag
toRelMouseDrag pfs pan md = RelMouseDrag $ md {
    _mouseDrag_from = owlPFState_toCanvasCoordinates pfs (_mouseDrag_from md) - pan
    , _mouseDrag_to = owlPFState_toCanvasCoordinates pfs (_mouseDrag_to md) - pan
  }