barrie-0.3.1: src/Barrie/Render.lhs
> module Barrie.Render where
> import Data.List
> import Data.Maybe
> import Barrie.Gadgets
> import Barrie.Style
> import Barrie.Trace
> import Barrie.Widgets
RenderF is the type of rendering functions. A rendering
function should be implmeneted for each backend. Currently,
we have Gtk and IO.
> type RenderF a b = String -- name of the gui element type
> -> [RunnableGUI a b] -- children of this gui element
> -> Style
> -> Maybe (Gadget b) -- corresponding implementation
> -> Maybe Widget -- possible popup
> -> IO (RunnableGUI a b)
RunnableGUI is a type representing a rendered set of gadgets,
widgets, and styles.
A rendering function retuns a single RunnableGUI, which
represents the top level gui element. Each of the inner
elements is also a RunnableGUI, and they are rendered in the
same way. Anything that can be rendered can be used as a top
level element, either in the main application, or in a
pop-up. Of course, this isn't enforced by this module, but
the RenderF functions should make sure it's true, because we
rely on it.
A RunnableGUI contains three fields. First is the GUI, which
is of a type chosen by the renderer (e.g. Gtk uses
Gtk.Widget). Second is an update from state function. When
the state changes, this is called for each widget, causing it
to display according to the latest state. Finally comes a
function which, give a function to extract state from IO
should call when the state is updated, which allows everything
else to be kept up to date. The final field is a function
which, given a way of extracting the current state from IO,
and a way of putting it back, can update it via its gadget
functionality.
One will rightly point out that this is all very ugly and
procedural, but luckily, anybody using Barrie doesn't have to
worry about it. It's the sewer of side-effects beneath the
cathedral of functional gui-ness.
> type RunnableGUI gui state =
> (gui, -- the GUI
> state -> IO (), -- update from state
> IO state -> (state -> IO ()) -> IO ()) -- state update function
The updates function extracts all the update functions from a
runnable gui. This is convenient for the implementation of
renderers.
> updates :: [RunnableGUI gui state] -> [state -> IO ()]
> updates = map (\ (_, update, _) -> update)
A behaviour is just a flat list of gadgets. Gadgets are defined
in a hierarchical way, but are flattened for rendering.
Note that the gadget hierarchy is only tangentially related to the
widget layout. The hierarchy is intended to be a functional
grouping, although in practice it will often look similar to the
widget layout.
> renderWidget :: Behaviour w -> Widget -> RenderF r w
> -> IO (RunnableGUI r w)
> renderWidget behaviour widget render = go widget
> where -- go :: StyleSheet -> Layout -> RunnableGUI r w
> go w = do let elmt = widgetElementName w
> style = widgetStyle w
> gadget = findGadget (uiElementName w) behaviour
> traceMessage ("rendering element: " ++ elmt);
> traceMessage ("ui name = " ++
> intercalate "." (uiElementName w))
> traceMessage ("gadget = " ++
> maybe "Nothing" (gadgetName) gadget)
> ws <- mapM go $ widgetChildren w
> render elmt ws style gadget (widgetPopupChild w)