phooey-0.1: src/Graphics/UI/Phooey.hs
{-# OPTIONS -fglasgow-exts #-}
{- |
Module : Graphics.UI.Phooey
Copyright : (c) Conal Elliott 2006
License : LGPL
Maintainer : conal@conal.net
Stability : provisional
Portability : portable
Phooey presents a simple, functional, and arrow-based interface to UI
construction.
This module simply re-exports most of "Graphics.UI.Phooey.ArrowUI" (hiding
the implementation and extensibility).
GUIs are usually programmed in an \"unnatural\" style, in that
implementation dependencies are inverted, relative to logical
dependencies. This reversal results directly from the imperative
orientation of most GUI libraries. While outputs depend on inputs from a
user and semantic point of view, the imperative approach imposes an
implementation dependence of inputs on outputs.
Phooey (\"/ph/unctional /oo/s/e/r /y/nterfaces\") retains the functional
style, in which outputs are expressed in terms of inputs. In addition,
Phooey supports dynamic input bounds, flexible layout, and
mutually-referential widgets.
As a first example, here is a simple shopping list GUI. The /total/
displayed at the bottom of the window always shows the sum of the values
of the /apples/ and /bananas/ input sliders. When a user changes the
inputs, the output updates accordingly.
@ <<http://haskell.org/sitewiki/images/2/2f/Ui1.png>>@
@
ui1 :: 'UI' () ()
ui1 = 'title' \"Shopping List\" $
proc () -> do
a <- 'title' \"apples\" ('islider' 3) -< (0,10)
b <- 'title' \"bananas\" ('islider' 7) -< (0,10)
'title' \"total\" 'showDisplay' -< a+b
@
To run this example (found in @src\/Examples.hs@ in the source release),
do @'runUI' ui1@.
A @UI@ value (widget, complete interactive application, or anything in
between) represents both functionality /and/ user interface. Each UI has
information flowing in and information flowing out. For this reason, @UI@
has two type parameters, the input and output types. For instance, a
slider has its (potentially varying) bounds as input and its (varying)
value as output, while a display has the value to be displayed as input
and a @()@ output. Any UI may be wrapped with a title and automatically
routes input and output to and from the wrapped UI. (See below for the
types of 'islider', 'showDisplay', and 'title', as used in defining @u1@
above.)
The slider bounds in @ui1@ are all static. In the following example, the
first two sliders determine the bounds of the third slider.
@ <<http://haskell.org/sitewiki/images/1/1b/Ui2.png>>@
@
ui2 = proc () -> do
lo <- 'title' \"lo\" ('islider' 3) -< (0,10)
hi <- 'title' \"hi\" ('islider' 8) -< (0,10)
val <- 'title' \"val\" ('islider' 5) -< (lo,hi)
'title' \"factorial\" 'showDisplay' -< fact val
where
fact n = 'product' [1 .. n]
@
By default, UI layout follows the order of the specification, with
earlier-specified components above later-specified ones. This layout may
be overridden by explicit layout functions. For instance, the following
definitions form variations of @u1@ laid out from bottom to top and from
left to right.
GUIs & code:
@ <<http://haskell.org/sitewiki/images/1/1b/UiB1.png>>@
@ <<http://haskell.org/sitewiki/images/f/f1/UiL1.png>>@
@
uiB1 = 'fromBottom' ui1
uiL1 = 'fromLeft' ui1
@
We can also lay out a sub-assembly, as in @ui3@ below, which uses the
arrow @&&&@ operator to feed a single input flow into two UIs and pair up
the outputs.
@ <<http://haskell.org/sitewiki/images/7/70/Ui3.png>>@
@
ui3 = 'fromBottom' $
proc () -> do
(a,b) <- 'fromRight' fruit -< (0,10)
'title' \"total\" 'showDisplay' -< a+b
where
fruit = 'title' \"apples\" ('islider' 3) &&&
'title' \"bananas\" ('islider' 7)
@
Next is a recursive example. It is like @ui2@, but the @lo@ and @hi@
sliders are used to bound each other. The specification enforces the
constraint that @lo <= hi@.
@ <<http://haskell.org/sitewiki/images/4/46/Ui4.png>>@
@
ui4 = proc () -> do
rec lo <- 'title' \"lo\" ('islider' 3) -< (0,hi)
hi <- 'title' \"hi\" ('islider' 8) -< (lo,10)
val <- 'title' \"val\" ('islider' 5) -< (lo,hi)
'title' \"factorial\" 'showDisplay' -< fact val
returnA -< ()
@
The final example is tightly recursive. A slider is used to bound
/itself/, so that the range is always the current value ±5.
@ <<http://haskell.org/sitewiki/images/7/71/Ui5.png>>@
@
ui5 = proc () -> do
rec val <- 'title' \"val\" ('islider' 6) -< (val-5,val+5)
'title' \"squared\" 'showDisplay' -< val*val
@
These last two examples just hang when run. The pictures are from a
previous implementation. Hmm.
-}
module Graphics.UI.Phooey
(
-- * The UI arrow
UI, runUI, runNamedUI
-- * High-level widgets
, stringDisplay, showDisplay, textEntry, islider
, checkBoxDisplay, checkBoxEntry, title
-- * Explicit layout
, fromTop, fromBottom, fromLeft, fromRight, flipLayout
) where
import Graphics.UI.Phooey.ArrowUI