1 {-# LANGUAGE FlexibleContexts, MultiParamTypeClasses, TupleSections #-}
3 module RMCA.GUI.MultiBoard where
6 import Control.Concurrent.MVar
8 import Control.Monad.IO.Class
11 import qualified Data.IntMap as M
14 import Data.ReactiveValue
15 import Graphics.UI.Gtk
16 import Graphics.UI.Gtk.Board.TiledBoard hiding (Board)
17 import Graphics.UI.Gtk.Layout.BackgroundContainer
18 import Graphics.UI.Gtk.Reactive.Gtk2
21 import RMCA.IOClockworks
22 import RMCA.Layer.LayerConf
24 import RMCA.ReactiveValueAtomicUpdate
26 import RMCA.Translator.Message
34 createNotebook :: ( ReactiveValueRead addLayer () IO
35 , ReactiveValueRead rmLayer () IO
36 , ReactiveValueAtomicUpdate board (M.IntMap ([Note],[Message])) IO
42 -> MCBMVar StaticLayerConf
43 -> MCBMVar DynLayerConf
47 , ReactiveFieldRead IO (M.IntMap Board)
48 , CBRef (M.IntMap LayerConf)
49 , ReactiveFieldRead IO
50 (M.IntMap (ReactiveFieldWrite IO [PlayHead]))
52 createNotebook boardQueue tc addLayerRV rmLayerRV
53 statMCBMVar dynMCBMVar synthMCBMVar guiCellMCBMVar = do
55 let curPageRV = ReactiveFieldReadWrite setter getter notifier
56 where (ReactiveFieldRead getter _) = notebookGetCurrentPagePassive n
57 -- afterSwitchPage is deprecated but switchPage gets us
58 -- the old page number and not the new one and using
59 -- afterSwitchPage doesn't trigger a warning so…
60 setter = postGUIAsync . notebookSetCurrentPage n
61 notifier io = void $ afterSwitchPage n (const io)
63 pageChanRV <- newCBMVarRW []
64 let foundHole = let foundHole' [] = 0
65 foundHole' [x] = x + 1
66 foundHole' (x:y:xs) = if x + 1 /= y then x + 1 else foundHole (y:xs)
70 let curChanRV = liftR2 (!!) pageChanRV curPageRV
71 ------------------------------------------------------------------------------
73 ------------------------------------------------------------------------------
75 chanMapRV <- newCBMVarRW M.empty
76 guiCellHidMVar <- newEmptyMVar
77 let clickHandler ioBoard = do
80 (\iPos' -> liftIO $ do
81 let iPos = actualTile iPos'
82 postGUIAsync $ void $ tryPutMVar state iPos
85 boardOnRelease ioBoard
87 let fPos = actualTile fPos'
89 liftIO $ postGUIAsync $ do
90 mp <- boardGetPiece fPos ioBoard
91 mstate <- tryTakeMVar state
92 when (fPos `elem` validArea && isJust mp) $ do
93 let piece = snd $ fromJust mp
94 when (button == RightButton && maybe False (== fPos) mstate) $ do
95 let nCell = rotateGUICell piece
96 boardSetPiece fPos (Player,nCell) ioBoard
97 nmp <- boardGetPiece fPos ioBoard
98 when (button == LeftButton && isJust nmp) $ do
99 let nCell = snd $ fromJust nmp
100 mOHid <- tryTakeMVar guiCellHidMVar
101 maybe (return ()) (removeCallbackMCBMVar guiCellMCBMVar) mOHid
102 reactiveValueWrite guiCellMCBMVar nCell
103 nHid <- installCallbackMCBMVar guiCellMCBMVar $ do
104 cp <- reactiveValueRead curChanRV
105 guiVal <- reactiveValueRead guiCellMCBMVar
106 mChanRV <- fmap (M.lookup cp)
107 (reactiveValueRead chanMapRV)
108 when (isNothing mChanRV) $ error "Can't get piece array!"
109 let (_,pieceArrRV,_) = fromJust mChanRV
110 reactiveValueWrite (pieceArrRV ! fPos) guiVal
111 putMVar guiCellHidMVar nHid
115 boardCont <- backgroundContainerNew
116 guiBoard <- attachGameRules =<< initGame
117 clickHandler guiBoard
118 centerBoard <- alignmentNew 0.5 0.5 0 0
119 containerAdd centerBoard guiBoard
120 containerAdd boardCont centerBoard
122 fstP <- notebookAppendPage n boardCont layerName
123 notebookPageNumber <- newCBMVarRW (1 :: Int)
125 initBoardRV tc guiBoard >>=
126 \(boardRV, pieceArrRV, phRV) -> reactiveValueRead chanMapRV >>=
127 reactiveValueWrite chanMapRV . M.insert fstP (boardRV,pieceArrRV,phRV)
129 reactiveValueRead pageChanRV >>=
130 reactiveValueWrite pageChanRV . (\pc -> pc ++ [foundHole pc])
132 layerMapRV <- newCBRef $ M.insert fstP defaultLayerConf M.empty
133 reactiveValueOnCanRead layerMapRV $ do
134 synth <- fmap (fmap (\(_,_,s) -> s)) (reactiveValueRead layerMapRV)
135 sequence_ $ M.elems $ M.mapWithKey
136 (\chan mess -> reactiveValueAppend boardQueue $
137 M.singleton chan $ ([],) $ synthMessage chan mess) synth
139 let updateDynLayer cp = do
140 nDyn <- reactiveValueRead dynMCBMVar
141 reactiveValueUpdate_ layerMapRV
142 (M.adjust (\(stat,_,synth) -> (stat,nDyn,synth)) cp)
144 nSynth <- reactiveValueRead synthMCBMVar
145 reactiveValueUpdate_ layerMapRV
146 (M.adjust (\(stat,dyn,_) -> (stat,dyn,nSynth)) cp)
147 reactiveValueAppend boardQueue $
148 M.singleton cp $ ([],) $ synthMessage cp nSynth
149 updateStatLayer cp = do
150 nStat <- reactiveValueRead statMCBMVar
151 reactiveValueUpdate_ layerMapRV
152 (M.adjust (\(_,dyn,synth) -> (nStat,dyn,synth)) cp)
154 statHidMVar <- newEmptyMVar
155 dynHidMVar <- newEmptyMVar
156 synthHidMVar <- newEmptyMVar
158 installCallbackMCBMVar statMCBMVar
159 (reactiveValueRead curChanRV >>= updateStatLayer) >>= putMVar statHidMVar
160 installCallbackMCBMVar dynMCBMVar
161 (reactiveValueRead curChanRV >>= updateDynLayer) >>= putMVar dynHidMVar
162 installCallbackMCBMVar synthMCBMVar
163 (reactiveValueRead curChanRV >>= updateSynth) >>= putMVar synthHidMVar
165 ------------------------------------------------------------------------------
167 ------------------------------------------------------------------------------
169 reactiveValueOnCanRead addLayerRV $ postGUIAsync $ do
170 np <- reactiveValueRead notebookPageNumber
171 unless (np >= maxLayers) $ do
172 reactiveValueWrite notebookPageNumber (np + 1)
173 nBoardCont <- backgroundContainerNew
175 nGuiBoard <- attachGameRules =<< initGame
176 clickHandler nGuiBoard
177 nCenterBoard <- alignmentNew 0.5 0.5 0 0
178 containerAdd nCenterBoard nGuiBoard
179 containerAdd nBoardCont nCenterBoard
181 notebookAppendPage n nBoardCont layerName
182 pChan <- reactiveValueRead pageChanRV
183 let newCP = foundHole pChan
184 (nBoardRV, nPieceArrRV, nPhRV) <- initBoardRV tc nGuiBoard
186 reactiveValueRead chanMapRV >>=
187 reactiveValueWrite chanMapRV . M.insert newCP (nBoardRV,nPieceArrRV,nPhRV)
188 reactiveValueRead layerMapRV >>=
189 reactiveValueWrite layerMapRV . M.insert newCP defaultLayerConf
191 --reactiveValueWrite curPageRV newP
192 reactiveValueWrite pageChanRV (pChan ++ [newCP])
195 reactiveValueOnCanRead rmLayerRV $ postGUIAsync $ do
196 np <- reactiveValueRead notebookPageNumber
198 cp <- reactiveValueRead curPageRV
199 oldCP <- reactiveValueRead curChanRV
200 let rmIndex :: Int -> [a] -> [a]
201 rmIndex n l = take n l ++ drop (n + 1) l
202 notebookRemovePage n cp
204 reactiveValueRead pageChanRV >>=
205 reactiveValueWrite pageChanRV . rmIndex cp
207 reactiveValueRead notebookPageNumber >>=
208 reactiveValueWrite notebookPageNumber . subtract 1
210 reactiveValueRead chanMapRV >>=
211 reactiveValueWrite chanMapRV . M.delete oldCP
213 reactiveValueRead layerMapRV >>=
214 reactiveValueWrite layerMapRV . M.delete oldCP
219 reactiveValueOnCanRead curChanRV $ do
220 cp <- reactiveValueRead curChanRV
222 takeMVar dynHidMVar >>= removeCallbackMCBMVar dynMCBMVar
223 takeMVar statHidMVar >>= removeCallbackMCBMVar statMCBMVar
224 takeMVar synthHidMVar >>= removeCallbackMCBMVar synthMCBMVar
225 layerMap <- reactiveValueRead layerMapRV
226 let mSelLayer = M.lookup cp layerMap
227 when (isNothing mSelLayer) $ error "Not found selected layer!"
228 let selLayer = fromJust mSelLayer
229 reactiveValueWrite dynMCBMVar (dynConf selLayer)
230 installCallbackMCBMVar dynMCBMVar (updateDynLayer cp) >>=
232 reactiveValueWrite statMCBMVar (staticConf selLayer)
233 installCallbackMCBMVar statMCBMVar (updateStatLayer cp) >>=
235 reactiveValueWrite synthMCBMVar (synthConf selLayer)
236 installCallbackMCBMVar synthMCBMVar (updateSynth cp) >>=
240 oldCurChanRV <- newCBMVarRW =<< reactiveValueRead curChanRV
241 reactiveValueOnCanRead curChanRV $ do
242 oldC <- reactiveValueRead oldCurChanRV
243 newC <- reactiveValueRead curChanRV
244 when (oldC /= newC) $ do
245 reactiveValueWrite oldCurChanRV newC
246 tryTakeMVar guiCellHidMVar >>=
247 fromMaybeM_ . fmap (removeCallbackMCBMVar guiCellMCBMVar)
248 reactiveValueWrite guiCellMCBMVar inertCell
250 ------------------------------------------------------------------------------
252 ------------------------------------------------------------------------------
253 let phMapRV :: ReactiveFieldRead IO (M.IntMap (ReactiveFieldWrite IO [PlayHead]))
254 phMapRV = liftR (M.map (\(_,_,b) -> b)) chanMapRV
256 boardMapRV :: ReactiveFieldRead IO (M.IntMap Board)
257 boardMapRV = ReactiveFieldRead getter notifier
258 where notifier io = do
259 chanMap <- reactiveValueRead chanMapRV
260 intMapMapM_ ((`reactiveValueOnCanRead` io) . \(b,_,_) -> b) chanMap
262 chanMap <- reactiveValueRead chanMapRV
263 intMapMapM (reactiveValueRead . \(b,_,_) -> b) chanMap
265 return (n, boardMapRV, layerMapRV, phMapRV)
267 ------------------------------------------------------------------------------
268 -- IntMap versions of mapM etc. to make code work with GHC 7.8.3
269 ------------------------------------------------------------------------------
271 intMapMapM_ :: (Functor m, Monad m) => (a -> m b) -> M.IntMap a -> m ()
272 intMapMapM_ f im = mapM_ f (M.elems im)
274 intMapMapM :: (Functor m, Monad m) => (a -> m b) -> M.IntMap a -> m (M.IntMap b)
275 intMapMapM f im = fmap (M.fromList . zip ks) (mapM f es)
277 (ks, es) = unzip (M.toList im)