1 {-# LANGUAGE FlexibleContexts, MultiParamTypeClasses, TupleSections #-}
3 module RMCA.GUI.MultiBoard where
5 import Control.Concurrent.MVar
7 import Control.Monad.IO.Class
10 import qualified Data.IntMap as M
13 import Data.ReactiveValue
14 import Graphics.UI.Gtk
15 import Graphics.UI.Gtk.Board.TiledBoard hiding (Board)
16 import Graphics.UI.Gtk.Layout.BackgroundContainer
17 import Graphics.UI.Gtk.Reactive.Gtk2
20 import RMCA.IOClockworks
21 import RMCA.Layer.LayerConf
23 import RMCA.ReactiveValueAtomicUpdate
25 import RMCA.Translator.Message
30 createNotebook :: ( ReactiveValueRead addLayer () IO
31 , ReactiveValueRead rmLayer () IO
32 , ReactiveValueAtomicUpdate board (M.IntMap ([Note],[Message])) IO
38 -> MCBMVar StaticLayerConf
39 -> MCBMVar DynLayerConf
43 , ReactiveFieldRead IO (M.IntMap Board)
44 , CBRef (M.IntMap LayerConf)
45 , ReactiveFieldRead IO
46 (M.IntMap (ReactiveFieldWrite IO [PlayHead]))
48 createNotebook boardQueue tc addLayerRV rmLayerRV
49 statMCBMVar dynMCBMVar synthMCBMVar guiCellMCBMVar = do
51 let curPageRV = ReactiveFieldReadWrite setter getter notifier
52 where (ReactiveFieldRead getter _) = notebookGetCurrentPagePassive n
53 -- afterSwitchPage is deprecated but switchPage gets us
54 -- the old page number and not the new one and using
55 -- afterSwitchPage doesn't trigger a warning so…
56 setter = postGUIAsync . notebookSetCurrentPage n
57 notifier io = void $ afterSwitchPage n (const io)
59 pageChanRV <- newCBMVarRW []
60 let foundHole = let foundHole' [] = 0
61 foundHole' [x] = x + 1
62 foundHole' (x:y:xs) = if x + 1 /= y then x + 1 else foundHole (y:xs)
66 let curChanRV = liftR2 (!!) pageChanRV curPageRV
67 ------------------------------------------------------------------------------
69 ------------------------------------------------------------------------------
71 chanMapRV <- newCBMVarRW M.empty
72 guiCellHidMVar <- newEmptyMVar
73 let clickHandler ioBoard = do
76 (\iPos' -> liftIO $ do
77 let iPos = actualTile iPos'
78 postGUIAsync $ void $ tryPutMVar state iPos
81 boardOnRelease ioBoard
83 let fPos = actualTile fPos'
85 liftIO $ postGUIAsync $ do
86 mp <- boardGetPiece fPos ioBoard
87 mstate <- tryTakeMVar state
88 when (fPos `elem` validArea && isJust mp) $ do
89 let piece = snd $ fromJust mp
90 when (button == RightButton && maybe False (== fPos) mstate) $ do
91 let nCell = rotateGUICell piece
92 boardSetPiece fPos (Player,nCell) ioBoard
93 nmp <- boardGetPiece fPos ioBoard
94 when (button == LeftButton && isJust nmp) $ do
95 let nCell = snd $ fromJust nmp
96 mOHid <- tryTakeMVar guiCellHidMVar
97 maybe (return ()) (removeCallbackMCBMVar guiCellMCBMVar) mOHid
98 reactiveValueWrite guiCellMCBMVar nCell
99 nHid <- installCallbackMCBMVar guiCellMCBMVar $ do
100 cp <- reactiveValueRead curChanRV
101 guiVal <- reactiveValueRead guiCellMCBMVar
102 mChanRV <- fmap (M.lookup cp)
103 (reactiveValueRead chanMapRV)
104 when (isNothing mChanRV) $ error "Can't get piece array!"
105 let (_,pieceArrRV,_) = fromJust mChanRV
106 reactiveValueWrite (pieceArrRV ! fPos) guiVal
107 putMVar guiCellHidMVar nHid
111 boardCont <- backgroundContainerNew
112 guiBoard <- attachGameRules =<< initGame
113 clickHandler guiBoard
114 centerBoard <- alignmentNew 0.5 0.5 0 0
115 containerAdd centerBoard guiBoard
116 containerAdd boardCont centerBoard
118 fstP <- notebookAppendPage n boardCont ""
119 notebookPageNumber <- newCBMVarRW (1 :: Int)
121 initBoardRV tc guiBoard >>=
122 \(boardRV, pieceArrRV, phRV) -> reactiveValueRead chanMapRV >>=
123 reactiveValueWrite chanMapRV . M.insert fstP (boardRV,pieceArrRV,phRV)
125 reactiveValueRead pageChanRV >>=
126 reactiveValueWrite pageChanRV . (\pc -> pc ++ [foundHole pc])
128 layerMapRV <- newCBRef $ M.insert fstP defaultLayerConf M.empty
129 reactiveValueOnCanRead layerMapRV $ do
130 synth <- fmap (fmap (\(_,_,s) -> s)) (reactiveValueRead layerMapRV)
131 sequence_ $ M.elems $ M.mapWithKey
132 (\chan mess -> reactiveValueAppend boardQueue $
133 M.singleton chan $ ([],) $ synthMessage chan mess) synth
135 let updateDynLayer cp = do
136 nDyn <- reactiveValueRead dynMCBMVar
137 reactiveValueUpdate_ layerMapRV
138 (M.adjust (\(stat,_,synth) -> (stat,nDyn,synth)) cp)
140 nSynth <- reactiveValueRead synthMCBMVar
141 reactiveValueUpdate_ layerMapRV
142 (M.adjust (\(stat,dyn,_) -> (stat,dyn,nSynth)) cp)
143 reactiveValueAppend boardQueue $
144 M.singleton cp $ ([],) $ synthMessage cp nSynth
145 updateStatLayer cp = do
146 nStat <- reactiveValueRead statMCBMVar
147 reactiveValueUpdate_ layerMapRV
148 (M.adjust (\(_,dyn,synth) -> (nStat,dyn,synth)) cp)
150 statHidMVar <- newEmptyMVar
151 dynHidMVar <- newEmptyMVar
152 synthHidMVar <- newEmptyMVar
154 installCallbackMCBMVar statMCBMVar
155 (reactiveValueRead curChanRV >>= updateStatLayer) >>= putMVar statHidMVar
156 installCallbackMCBMVar dynMCBMVar
157 (reactiveValueRead curChanRV >>= updateDynLayer) >>= putMVar dynHidMVar
158 installCallbackMCBMVar synthMCBMVar
159 (reactiveValueRead curChanRV >>= updateSynth) >>= putMVar synthHidMVar
161 ------------------------------------------------------------------------------
163 ------------------------------------------------------------------------------
165 reactiveValueOnCanRead addLayerRV $ postGUIAsync $ do
166 np <- reactiveValueRead notebookPageNumber
167 unless (np >= maxLayers) $ do
168 reactiveValueWrite notebookPageNumber (np + 1)
169 nBoardCont <- backgroundContainerNew
171 nGuiBoard <- attachGameRules =<< initGame
172 clickHandler nGuiBoard
173 nCenterBoard <- alignmentNew 0.5 0.5 0 0
174 containerAdd nCenterBoard nGuiBoard
175 containerAdd nBoardCont nCenterBoard
177 notebookAppendPage n nBoardCont $ show np
178 pChan <- reactiveValueRead pageChanRV
179 let newCP = foundHole pChan
180 (nBoardRV, nPieceArrRV, nPhRV) <- initBoardRV tc nGuiBoard
182 reactiveValueRead chanMapRV >>=
183 reactiveValueWrite chanMapRV . M.insert newCP (nBoardRV,nPieceArrRV,nPhRV)
184 reactiveValueRead layerMapRV >>=
185 reactiveValueWrite layerMapRV . M.insert newCP defaultLayerConf
187 --reactiveValueWrite curPageRV newP
188 reactiveValueWrite pageChanRV (pChan ++ [newCP])
191 reactiveValueOnCanRead rmLayerRV $ postGUIAsync $ do
192 np <- reactiveValueRead notebookPageNumber
194 cp <- reactiveValueRead curPageRV
195 oldCP <- reactiveValueRead curChanRV
196 let rmIndex :: Int -> [a] -> [a]
197 rmIndex n l = take n l ++ drop (n + 1) l
198 notebookRemovePage n cp
200 reactiveValueRead pageChanRV >>=
201 reactiveValueWrite pageChanRV . rmIndex cp
203 reactiveValueRead notebookPageNumber >>=
204 reactiveValueWrite notebookPageNumber . subtract 1
206 reactiveValueRead chanMapRV >>=
207 reactiveValueWrite chanMapRV . M.delete oldCP
209 reactiveValueRead layerMapRV >>=
210 reactiveValueWrite layerMapRV . M.delete oldCP
215 reactiveValueOnCanRead curChanRV $ do
216 cp <- reactiveValueRead curChanRV
218 takeMVar dynHidMVar >>= removeCallbackMCBMVar dynMCBMVar
219 takeMVar statHidMVar >>= removeCallbackMCBMVar statMCBMVar
220 takeMVar synthHidMVar >>= removeCallbackMCBMVar synthMCBMVar
221 layerMap <- reactiveValueRead layerMapRV
222 let mSelLayer = M.lookup cp layerMap
223 when (isNothing mSelLayer) $ error "Not found selected layer!"
224 let selLayer = fromJust mSelLayer
225 reactiveValueWrite dynMCBMVar (dynConf selLayer)
226 installCallbackMCBMVar dynMCBMVar (updateDynLayer cp) >>=
228 reactiveValueWrite statMCBMVar (staticConf selLayer)
229 installCallbackMCBMVar statMCBMVar (updateStatLayer cp) >>=
231 reactiveValueWrite synthMCBMVar (synthConf selLayer)
232 installCallbackMCBMVar synthMCBMVar (updateSynth cp) >>=
236 oldCurChanRV <- newCBMVarRW =<< reactiveValueRead curChanRV
237 reactiveValueOnCanRead curChanRV $ do
238 oldC <- reactiveValueRead oldCurChanRV
239 newC <- reactiveValueRead curChanRV
240 when (oldC /= newC) $ do
241 reactiveValueWrite oldCurChanRV newC
242 tryTakeMVar guiCellHidMVar >>=
243 fromMaybeM_ . fmap (removeCallbackMCBMVar guiCellMCBMVar)
244 reactiveValueWrite guiCellMCBMVar inertCell
246 ------------------------------------------------------------------------------
248 ------------------------------------------------------------------------------
249 let phMapRV :: ReactiveFieldRead IO (M.IntMap (ReactiveFieldWrite IO [PlayHead]))
250 phMapRV = liftR (M.map (\(_,_,b) -> b)) chanMapRV
252 boardMapRV :: ReactiveFieldRead IO (M.IntMap Board)
253 boardMapRV = ReactiveFieldRead getter notifier
254 where notifier io = do
255 chanMap <- reactiveValueRead chanMapRV
256 intMapMapM_ ((`reactiveValueOnCanRead` io) . \(b,_,_) -> b) chanMap
258 chanMap <- reactiveValueRead chanMapRV
259 intMapMapM (reactiveValueRead . \(b,_,_) -> b) chanMap
261 return (n, boardMapRV, layerMapRV, phMapRV)
263 ------------------------------------------------------------------------------
264 -- IntMap versions of mapM etc. to make code work with GHC 7.8.3
265 ------------------------------------------------------------------------------
267 intMapMapM_ :: (Functor m, Monad m) => (a -> m b) -> M.IntMap a -> m ()
268 intMapMapM_ f im = mapM_ f (M.elems im)
270 intMapMapM :: (Functor m, Monad m) => (a -> m b) -> M.IntMap a -> m (M.IntMap b)
271 intMapMapM f im = fmap (M.fromList . zip ks) (mapM f es)
273 (ks, es) = unzip (M.toList im)