-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMain.hs
129 lines (101 loc) · 4.95 KB
/
Main.hs
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
module Main where
import Control.Concurrent (threadDelay, forkIO)
import Control.Monad (liftM, when)
import Control.Monad.Trans (liftIO)
import qualified Graphics.UI.Gtk as GTK
import Graphics.UI.Gtk.Builder (builderNew, builderAddFromFile, builderGetObject)
import Data.Board (Board, newBoard, emptyBoard)
import Interface.Board (Cell, BoardState(..), translate)
import UI.SharedMem (Display(..), initDisplay, Mem, newMem, atomically,
display, alterBoard, alterDisplay, cellAt, mapPair)
import UI.Board (drawBoard, highlightCell)
main :: IO ()
main = do
state <- atomically $ newMem initBoard initDisplay
GTK.initGUI
builder <- builderNew
builderAddFromFile builder "UI/life.glade"
mainWin <- builderGetObject builder GTK.castToWindow "mainWin"
mainWin `GTK.on` GTK.deleteEvent $ liftIO GTK.mainQuit >> return False
GTK.widgetShowAll mainWin
gameView <- builderGetObject builder GTK.castToDrawingArea "gameView"
viewport <- GTK.widgetGetDrawWindow gameView
[nextBtn, prevBtn] <- sequence $
map (getButton builder) ["nextBtn", "prevBtn"]
[alterBtn, playBtn] <- sequence $
map (getToggle builder) ["alterBtn", "playBtn"]
arrowCursor <- GTK.cursorNew GTK.Arrow
GTK.drawWindowSetCursor viewport $ Just arrowCursor
GTK.widgetAddEvents gameView [GTK.PointerMotionMask]
mainWin `GTK.on` GTK.keyPressEvent $ keyMoveDisplay state viewport
gameView `GTK.on` GTK.exposeEvent $ do
liftIO $ do
updateDisplaySize gameView state
drawBoard viewport state
return False
gameView `GTK.on` GTK.motionNotifyEvent $ do
liftIO $ GTK.toggleButtonGetActive alterBtn >>= \alt -> when alt $
liftM (mapPair $ flip div 10) (GTK.widgetGetPointer gameView)
>>= highlightCell viewport state
return False
gameView `GTK.on` GTK.leaveNotifyEvent $ do
liftIO $ drawBoard viewport state
return False
gameView `GTK.on` GTK.buttonPressEvent $ do
liftIO $ GTK.toggleButtonGetActive alterBtn >>= \alt -> when alt $ do
cell <- GTK.widgetGetPointer gameView >>= cellFromCoordinates state
atomically $ alterBoard (alter cell) state
return False
let withBoard f = liftIO (alterBoardAction state viewport f) >> return False
nextBtn `GTK.on` GTK.buttonReleaseEvent $ withBoard next
prevBtn `GTK.on` GTK.buttonReleaseEvent $ withBoard previous
playBtn `GTK.on` GTK.buttonReleaseEvent $ do
isOn <- liftIO $ GTK.toggleButtonGetActive playBtn
liftIO . atomically $ alterDisplay (\d -> d { autoNext = not isOn }) state
return False
forkIO $ autoPlay state viewport
GTK.mainGUI
where
initBoard = newBoard [(3, 4), (3, 5), (3, 6)]
getButton bld name = builderGetObject bld GTK.castToButton name
getToggle bld name = builderGetObject bld GTK.castToToggleButton name
alterBoardAction :: BoardState b => Mem b -> GTK.DrawWindow -> (b -> b) -> IO ()
alterBoardAction state viewport f = do
atomically $ alterBoard f state
drawBoard viewport state
keyMoveDisplay :: BoardState b => Mem b -> GTK.DrawWindow -> GTK.EventM GTK.EKey Bool
keyMoveDisplay state viewport = do
movement <- liftM keyToMvmnt GTK.eventKeyVal
liftIO $ do
atomically $ alterDisplay (move movement) state
drawBoard viewport state
return True
where
keyToMvmnt 65361 = ((-1), 0) -- left
keyToMvmnt 65362 = (0, (-1)) -- up
keyToMvmnt 65363 = (1, 0) -- right
keyToMvmnt 65364 = (0, 1) -- down
keyToMvmnt _ = (0, 0)
move (x, y) displ = let (fx, fy) = firstCell displ
(lx, ly) = lastCell displ in displ {
firstCell = (fx + x, fy + y),
lastCell = (lx + x, ly + y)
}
updateDisplaySize :: (BoardState b, GTK.WidgetClass v) => v -> Mem b -> IO ()
updateDisplaySize view mem = liftIO $ do
(width, height) <- liftM rectSize $ GTK.widgetGetAllocation view
let size = (width `div` 10, height `div` 10)
atomically $ do
first <- liftM firstCell $ display mem
alterDisplay (update $ translate first size) mem
where
rectSize (GTK.Rectangle fx fy lx ly) = (lx - fx, ly - fy)
update cell disp = disp { lastCell = cell }
cellFromCoordinates :: Mem b -> (Int, Int) -> IO Cell
cellFromCoordinates state coords = atomically $ cellAt state coords
autoPlay :: BoardState b => Mem b -> GTK.DrawWindow -> IO ()
autoPlay state view = do
auto <- atomically $ liftM autoNext (display state)
when auto $ GTK.postGUIAsync $ alterBoardAction state view next
threadDelay 1000000
autoPlay state view