module Graphics.X11.Turtle.Layers( -- * types Layers, Layer, Character, -- * initialize newLayers, makeLayer, makeCharacter, -- * draws redrawLayers, addDraw, setBackground, undoLayer, clearLayer, setCharacter ) where import Control.Monad(when, unless) import Data.IORef(IORef, newIORef, readIORef, atomicModifyIORef) import Data.IORef.Tools(atomicModifyIORef_) import Data.List.Tools(setAt, modifyAt) -------------------------------------------------------------------------------- data Layers = Layers{ undoNum :: Int, clearLayersAction :: IO (), undoLayersAction :: IO (), clearCharactersAction :: IO (), background :: [IO ()], buffed :: [IO ()], layers :: [[(IO (), IO ())]], characters :: [IO ()] } data Layer = Layer{ layerId :: Int, layerLayers :: IORef Layers } data Character = Character{ characterId :: Int, characterLayers :: IORef Layers } -------------------------------------------------------------------------------- newLayers :: Int -> IO () -> IO () -> IO () -> IO (IORef Layers) newLayers un cla ula cca = newIORef Layers{ undoNum = un, clearLayersAction = cla, undoLayersAction = ula, clearCharactersAction = cca, background = [], buffed = [], layers = [], characters = [] } makeLayer :: IORef Layers -> IO Layer makeLayer rls = atomicModifyIORef rls $ \ls -> (ls{ layers = layers ls ++ [[]], buffed = buffed ls ++ [return ()], background = background ls ++[return ()]}, Layer{layerId = length $ layers ls, layerLayers = rls}) makeCharacter :: IORef Layers -> IO Character makeCharacter rls = atomicModifyIORef rls $ \ls -> (ls{characters = characters ls ++ [return ()]}, Character{ characterId = length $ characters ls, characterLayers = rls}) -------------------------------------------------------------------------------- redrawLayers :: IORef Layers -> IO () redrawLayers rls = readIORef rls >>= \ls -> do sequence_ $ background ls clearLayersAction ls >> sequence_ (buffed ls) undoLayersAction ls >> mapM_ snd (concat $ layers ls) clearCharactersAction ls >> sequence_ (characters ls) addDraw :: Layer -> (IO (), IO ()) -> IO () addDraw Layer{layerId = lid, layerLayers = rls} acts@(_, act) = do readIORef rls >>= \ls -> do act >> clearCharactersAction ls >> sequence_ (characters ls) unless (length (layers ls !! lid) < undoNum ls) $ fst $ head $ layers ls !! lid atomicModifyIORef_ rls $ \ls -> if length (layers ls !! lid) < undoNum ls then ls{layers = modifyAt (layers ls) lid (++ [acts])} else let (a, _) : as = layers ls !! lid in ls{ layers = setAt (layers ls) lid $ as ++ [acts], buffed = modifyAt (buffed ls) lid (>> a)} setBackground :: Layer -> IO () -> IO () setBackground Layer{layerId = lid, layerLayers = rls} act = do atomicModifyIORef_ rls $ \ls -> ls{background = setAt (background ls) lid act} redrawLayers rls undoLayer :: Layer -> IO Bool undoLayer Layer{layerId = lid, layerLayers = rls} = do done <- atomicModifyIORef rls $ \ls -> if null $ layers ls !! lid then (ls, False) else (ls{layers = modifyAt (layers ls) lid init}, True) when done $ readIORef rls >>= \ls -> do undoLayersAction ls >> mapM_ snd (concat $ layers ls) clearCharactersAction ls >> sequence_ (characters ls) return done clearLayer :: Layer -> IO () clearLayer Layer{layerId = lid, layerLayers = rls} = do atomicModifyIORef_ rls $ \ls -> ls{ layers = setAt (layers ls) lid [], buffed = setAt (buffed ls) lid $ return ()} redrawLayers rls setCharacter :: Character -> IO () -> IO () setCharacter Character{characterId = cid, characterLayers = rls} act = do atomicModifyIORef_ rls $ \ls -> ls{characters = setAt (characters ls) cid act} readIORef rls >>= \ls -> clearCharactersAction ls >> sequence_ (characters ls)