1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
module Chelleport where
import Chelleport.AppShell (Action (AppAction, SysQuit), EventHandler, Update, hideWindow, setupAppShell)
import Chelleport.Control (currentMousePosition, eventToKeycode, isKeyPress, isKeyPressWith, isKeyReleaseWith, moveMouse, triggerMouseLeftClick)
import Chelleport.Draw (cellSize)
import Chelleport.KeySequence (findMatchPosition, generateGrid, isValidKey, nextChars, toKeyChar)
import Chelleport.Types
import Chelleport.Utils (cIntToInt, intToCInt)
import qualified Chelleport.View
import Data.List ((\\))
import Data.Maybe (fromMaybe, isJust)
import Foreign.C (CInt)
import qualified SDL
open :: IO ()
open = setupAppShell initialState update eventToAction Chelleport.View.render
initialState :: DrawContext -> IO State
initialState _ctx = do
let cells = fromMaybe (pure undefined) $ generateGrid 0 (rows, columns) hintKeys
pure $
State
{ stateGrid = cells,
stateKeySequence = [],
stateIsMatched = False,
stateIsShiftPressed = False
}
where
rows = 12
columns = 12
hintKeys = ['A' .. 'Z'] \\ "Q"
directionalIncrement :: (CInt, CInt) -> Char -> (Int, Int)
directionalIncrement (incx, incy) = \case
'H' -> (-cIntToInt incx, 0)
'L' -> (cIntToInt incx, 0)
'K' -> (0, -cIntToInt incy)
'J' -> (0, cIntToInt incy)
_ -> undefined
update :: Update State AppAction
-- Act on key inputs
update state ctx (FilterSequence key) =
case liftA2 (,) (toKeyChar key) validChars of
Just (keyChar, validChars')
| stateIsMatched state && keyChar `elem` ("HJKL" :: String) -> do
incr <- incrementValue
let action = IncrementMouseCursor $ directionalIncrement incr keyChar
pure (state, Just . AppAction $ action)
| keyChar `elem` validChars' -> do
let newKeySequence = stateKeySequence state ++ [keyChar]
let matchPosition = findMatchPosition newKeySequence $ stateGrid state
let state' = state {stateKeySequence = newKeySequence, stateIsMatched = isJust matchPosition}
pure (state', AppAction . MoveMousePosition <$> matchPosition)
_ -> pure (state, Nothing)
where
validChars = nextChars (stateKeySequence state) (stateGrid state)
incrementValue = do
(wcell, hcell) <- cellSize state ctx
if stateIsShiftPressed state
then pure (wcell `div` 4, hcell `div` 4)
else pure (wcell `div` 16, hcell `div` 16)
-- Move mouse incrementally
update state ctx (IncrementMouseCursor (incx, incy)) = do
(SDL.V2 curx cury) <- currentMousePosition ctx
moveMouse ctx (curx + intToCInt incx) (cury + intToCInt incy)
pure (state, Nothing)
-- Move mouse to given position
update state ctx (MoveMousePosition (row, col)) = do
(x, y) <- getPosition
moveMouse ctx x y
pure (state, Nothing)
where
getPosition = do
(wcell, hcell) <- cellSize state ctx
let x = (wcell `div` 2) + wcell * intToCInt col
let y = (hcell `div` 2) + hcell * intToCInt row
pure (x, y)
-- Reset entered key sequence and state
update state _ctx ResetKeys = do
pure (state {stateKeySequence = [], stateIsMatched = False}, Nothing)
-- Trigger left click
update state ctx TriggerLeftClick = do
hideWindow ctx
triggerMouseLeftClick ctx
pure (state, Just SysQuit)
-- Set/unset whether shift is pressed
update state _ctx (UpdateShiftState shift) = pure (state {stateIsShiftPressed = shift}, Nothing)
eventToAction :: EventHandler State AppAction
eventToAction _state event =
case SDL.eventPayload event of
SDL.QuitEvent -> Just SysQuit
SDL.KeyboardEvent ev
| isKeyPressWith ev SDL.KeycodeQ -> Just SysQuit
| isKeyPressWith ev SDL.KeycodeEscape -> Just SysQuit
| isKeyPressWith ev SDL.KeycodeSpace -> Just $ AppAction TriggerLeftClick
| isKeyPressWith ev SDL.KeycodeTab -> Just $ AppAction ResetKeys
| isKeyPress ev && isValidKey (eventToKeycode ev) ->
Just . AppAction $ FilterSequence $ eventToKeycode ev
| isKeyPressWith ev SDL.KeycodeLShift || isKeyPressWith ev SDL.KeycodeRShift ->
Just . AppAction $ UpdateShiftState True
| isKeyReleaseWith ev SDL.KeycodeLShift || isKeyReleaseWith ev SDL.KeycodeRShift ->
Just . AppAction $ UpdateShiftState False
_ -> Nothing
|