Definition of the GUI monad. \begin{code} module GUIDef where import WidgetExports (Config,GUIInfo,WindowPrim,CanvasPrim) import MutSet import IOExts import Weak import FranCore import WH import IO import Identify newtype GUI a = GUI (GUIDef -> IO (GUIDef,a)) instance Monad GUI where return a = GUI $ \info -> return (info,a) (GUI a) >>= f = GUI $ \info -> do (info,v) <- a info case f v of GUI a -> a info fail err = failGUI (userError err) instance Functor GUI where fmap f a = do v <- a return $ f v failGUI :: IOError -> GUI a failGUI err = GUI (\_ -> ioError err) tryGUI :: GUI a -> GUI (Either IOError a) tryGUI (GUI f) = GUI (\st -> do res <- try (f st) case res of Left err -> return (st,Left err) Right (i,v) -> return (i,Right v)) catchGUI :: GUI a -> (IOError -> GUI a) -> GUI a catchGUI (GUI gui) hdl = GUI gui' where gui' st = catch (gui st) (hdl' st) hdl' st err = case hdl err of GUI hdl_gui -> hdl_gui st \end{code} The gui monad contains GUIInfo, which is explicit state for the underlying widget system (handled abstractly), a unique name supply, a mutable set of remove actions for active widgets (see PrimWidget below for why), the current window, the root window, the since variable and perhaps a current canvas if we're in one. The since variable is used in GUI.lhs. \begin{code} data GUIDef = GUIDef { guiInfo :: GUIInfo, nameSupply :: ! (IORef Int), activeWidgets :: ! (MutSet RemRef), currentWindow :: Window, rootWindowRef :: Window, since :: IORef Double, currentCanvas :: (Maybe Canvas), currentStyle :: Conf Style } mkGUIDef :: GUIInfo -> Window -> IO GUIDef mkGUIDef gui win = do ref <- newIORef 0 mset <- newMutSet tm <- newIORef 0 return $! GUIDef gui ref mset win win tm Nothing emptyConf \end{code} The type Config is widget system (such as TclHaskell) dependent and is imported abstractly. All other configuration options are functions from the widget to a GUI action. \begin{code} emptyConf :: Conf w emptyConf = Conf [] newtype Conf w = Conf [ConfItem w] instance Show (Conf w) where show c = "Conf" data ConfItem w = Config Config | ConfGUI ConfigName (w -> GUI ()) type ConfigName = Ident instance Identifiable (ConfItem w) where identify (Config c) = identify c identify (ConfGUI c _) = c identify _ = nullId instance Eq (ConfItem w) where c1 == c2 = let i1 = identify c1 i2 = identify c2 in i1 == i2 && i1 /= nullId && i2 /= nullId \end{code} Style is a generic configuration type for global options in scope, such as background color \begin{code} data Style = forall w . (WidgetItem w) => Style w instance WidgetItem Style where cset (Style w) cs = cset w cs destroy (Style w) = destroy w uniqueId (Style w) = uniqueId w addFinaliserW (Style w) act = addFinaliserW w act \end{code} A window and canvas are imported from the underlying toolkit, but are wrapped in a PrimWidget. See PrimWidget.lhs for more on prim widgets (we really need mutually recursive modules here). \begin{code} type RemRef = IORef (Maybe Remover) type Window = PrimWidget WindowPrim type Canvas = PrimWidget CanvasPrim data PrimWidget w = PrimWidget { addingRef :: IORef Int, guiDef :: ! GUIDef, widgetPrim :: ! w, hideRef :: ! RemRef, hideRefWP :: ! (Weak RemRef), actionPE :: Event () } \end{code} Some GUI funtions to access GUIDef. \begin{code} getUniqueName :: GUI Int getUniqueName = withGUIDef $ \(GUIDef {nameSupply = ref}) -> do v <- readIORef ref writeIORef ref (v+1) return $! v liftIO :: IO a -> GUI a liftIO io = GUI $ \info -> do v <- io;return (info,v) getGUIDef :: GUI GUIDef getGUIDef = GUI $ \info -> return (info,info) updGUIDef :: (GUIDef -> GUIDef) -> GUI () updGUIDef f = GUI $ \info -> return (f info,()) runGUI :: GUIDef -> GUI a -> IO a runGUI gd (GUI f) = fmap snd $ f gd fromGUI :: GUI a -> GUI (IO a) fromGUI (GUI a) = GUI $ \info -> return (info,fmap snd $ a info) withGUIInfo :: (GUIInfo -> IO a) -> GUI a withGUIInfo f = GUI $ \i -> do v <- f (guiInfo i);return (i,v) withGUIDef :: (GUIDef -> IO a) -> GUI a withGUIDef f = GUI $ \i -> do v <- f i;return (i,v) setGUIInfo :: GUIInfo -> GUI () setGUIInfo gi = updGUIDef (\g -> g {guiInfo = gi}) setCurrentWindow :: Window -> GUI () setCurrentWindow win = updGUIDef (\g -> g {currentWindow = win}) getCurrentWindow :: GUI Window getCurrentWindow = fmap currentWindow $ getGUIDef getRootWindow :: GUI Window getRootWindow = fmap rootWindowRef $ getGUIDef setInCanvas :: Canvas -> GUI () clearFromCanvas :: GUI () setInCanvas c = updGUIDef (\g -> g {currentCanvas = Just c}) clearFromCanvas = updGUIDef (\g -> g {currentCanvas = Nothing}) addGUIB :: Behavior (GUI ()) -> GUI (GUI ()) addGUIB bh = do info <- getGUIDef rm <- liftIO $ addIOB $ lift1 (runGUI info) bh return $ liftIO rm \end{code}