Display abstract panel widgets \begin{code} module DisplayPItem where import Widget import WH import FranCore import List import Maybe import PileArray import Monad import FM import IOExts import Identify import PrimWidget import MapG import DisplaySimple import BVar import GUIDef import GUI(csetAllB') import WidgetExports(validPanelConfigs) \end{code} The exported display functions. display a widget in a window, and the more primitive display a widget in a panel with some input listener (for windows in canvases). \begin{code} displayWidget :: WW -> WidgetB -> GUIDef -> IO () displayWidget (WW ww) w gui = do p <- getWinPanel ww displayWidgetWith Nothing p w gui displayWidgetWith :: Maybe Bind -> Panel -> WidgetB -> GUIDef -> IO () displayWidgetWith mb p w gui = do wr <- newWire die <- fireListener (input wr) addFinaliserW p (die ()) di <- initDisplayInfo' (currentStyle gui) gui (event wr) pi <- initPackDisplayInfo p let di' = maybe di (\a -> di {bindEvents = [a]}) mb displayW displayWidget' groupPItem di' pi w \end{code} An abstract panel widget has some packing info in scope; a current packing mode (ie above or beside); a current box we're packing into; a pile array for that box; a map to get back unique pile names from widget pile names. \begin{code} data PackDisplayInfo = PackDisplayInfo { packInfo :: ! (Behavior [PackInfo]), packMode :: ! (Maybe PackMode), packBox :: ! Box, packArray :: ! PileArray, packFM :: ! (IORef (FiniteMap Ident EltName)) } initPackDisplayInfo :: Panel -> IO PackDisplayInfo initPackDisplayInfo p = do bx <- setBoxLayout p pa <- pileArray 30 ref <- newIORef emptyFM return $! PackDisplayInfo (lift0 []) Nothing bx pa ref \end{code} Display an individual item. To show it we use dispPItem. this returns a hide action, that we hang onto in an IORef. to hide we then fire the function in the IORef. \begin{code} displayPItem :: (Bindable w,PanelItem w) => w -> PackDisplayInfo -> DisplayInfo -> IO (EltName,IORef Remover) displayPItem w (PackDisplayInfo {packInfo = ps,packBox = frm,packArray = pa}) di = do rmvref <- newIORef (return ()) let showIt elt ps = dispPItem pa elt ps w frm rmvref hideIt = do readIORef rmvref >>= id writeIORef rmvref (return ()) elt <- displayPAItem pa w (\disp b -> lift2 disp b ps) showIt hideIt di [] return (elt,rmvref) \end{code} how to map a widget into a box. we lookup in the pilearray which element to place the widget after. \begin{code} dispPItem :: PanelItem w => PileArray -- the pile for the current box -> EltName -- the pile name of the widget -> [PackInfo] -- the current packing info for the widget -> w -- the widget -> Box -- the current box -> IORef Remover -- the ioref to stick the hide action in -> IO () dispPItem pa elt ps w box rmvref = do mbe <- findEltAbove pa elt rm <- boxAdd w box (maybe PlaceBottom (PlaceBefore . identElt) mbe) ps writeIORef rmvref rm \end{code} how to make a group of widgets for restacking (from left to right, or top to bottom). we make a box for the pile, individual boxes for each pile element and just restack that box \begin{code} groupPItem :: DisplayInfo -> PackDisplayInfo -> IO (MkMoveOp PackDisplayInfo) groupPItem di pi = do (_,di,pi) <- newBox'' pi di return (di,pi,newBox' ) \end{code} make a pile element box. we make a new box, and then make a mover, that just remaps the panel that formed the box after altering its position in the pile. \begin{code} newBox' :: MkMoveOp' PackDisplayInfo newBox' di@(DisplayInfo {visible = vis,dieE = dieE}) pd@(PackDisplayInfo {packBox = b,packInfo =i,packFM = fmv, packArray = pa}) ident pos = do ((elt,rmvref,p),di,pd) <- newBox'' pd di moveInPile <- mkMover pa fmv ident elt raisePileElt lowerPileElt dieE moveInPile pos let move = mkL $ \pos -> do moveInPile pos t <- getFranTime (ps,vis) <- pairB i vis `at` t when vis $ dispPItem pa elt ps p b rmvref return (move,di,pd) \end{code} make a box to hold a group. this returns the eltname of the box; the remover action for the box (to hide it); and the panel that forms the box (in order to remap it on movement). as we can just unmap and map this new panel on visibility changes, we don't need to pass on visibility changes to the elements in it. (If we try to reuse the same widget in two places this optimisation causes us to screw up, but its useful the rest of the time. Rethink?) \begin{code} newBox'' :: PackDisplayInfo -> DisplayInfo -> IO ((EltName,IORef Remover,Panel), DisplayInfo,PackDisplayInfo) newBox'' pd@(PackDisplayInfo {packMode = m, packBox = b@(Box bb ref _),packArray = pa}) di@(DisplayInfo {guiDef_ = g,visible = vis,dieE = dieE,style =s}) = do p' <- mkSubPanel bb runGUI g $ csetAllB' validPanelConfigs p' [] s p <- setBoxLayout p' addListener (onceE' dieE) $ mkL_ $ destroy p' boxSetMode p (maybe PackAbove id m) (elt,rmvref) <- displayPItem p' pd di pa' <- pileArray 20 ref <- newIORef emptyFM return ((elt,rmvref,p'), di {visible = trueB}, pd {packFM = ref,packBox = p,packArray = pa', packInfo = lift0 []}) \end{code} make a new box when the mode changes, ie when we start packing above instead of beside, and place it in the new pack display info. \begin{code} newBox :: PackDisplayInfo -> DisplayInfo -> PackMode -> IO (PackDisplayInfo,DisplayInfo) newBox pd@(PackDisplayInfo {packInfo =i,packMode = m, packBox = b@(Box bb ref _),packArray = pa}) di@(DisplayInfo {visible = vis}) pm = do case m of Nothing -> do boxSetMode b pm return (pd {packMode = Just pm},di) Just m -> if m == pm then return (pd,di) else do (_,di,pd) <- newBox'' (pd {packMode = Just pm}) di return (pd,di) \end{code} How to display an abstract panel widget. it is either made of packing info; a mode change (above/beside); or an actual panel widget. \begin{code} displayWidget' :: DisplayInfo -> PackDisplayInfo -> PW -> IO () displayWidget' di pd@(PackDisplayInfo {packInfo = ps}) (WithPack pinfo w) = do displayW displayWidget' groupPItem di (pd {packInfo = lift0 pinfo}) w displayWidget' di pd@(PackDisplayInfo {packInfo = ps}) (WithPackB pinfo w) = do displayW displayWidget' groupPItem di (pd {packInfo = pinfo}) w displayWidget' di pd (WithPackMode pm w) = do (pd,di) <- newBox pd di pm displayW displayWidget' groupPItem di pd w displayWidget' di pd (PW w) = do displayPItem w pd di return () \end{code}