|
FG | Portability | non-portable (many GHC extensions) | Stability | experimental | Maintainer | kevina@cs.utah.edu |
|
|
|
|
|
Description |
This module is a first attempt of using Arrows to create a GUI Library
based on GTK+. A good understanding of how Arrows work is required in
order to understand the interface. For more information on Arrows see
http://www.haskell.org/arrows/.
It uses many ideas from Fruit (http://haskell.org/fruit/). However
it is based on discrete events rather than a continuous signal. The
interface is only updated during an Event. It also ideas from
Fudgets (http://www.md.chalmers.se/Cs/Research/Functional/Fudgets/),
some of which were also used by Fruit.
Here is a complete working example to give you an idea of how to use FG:
import FG
-- A Widget with three buttons "Inc", "Dec" and "Reset". "Dec" is
-- disabled when the count is 0. Does not actually display the count.
-- The output value is the current value of the counter.
counter :: Widget WidgetP Int
counter = proc p -> hbox [] (proc _ -> do
rec inc <- tag (+1) <<< button [text "Inc"] -< def
dec <- tag (+(-1)) <<< button [text "Dec"] -< [enabled (c > 0)]
reset <- tag (const 0) <<< button [text "Reset"] -< def
cs@(_,c) <- hold 0 -< onEvent (\f -> Just $ f c) Nothing
(inc >< dec >< reset)
returnA -< cs) -< (p, ())
-- The main FG. Connects the value of the counter to a Label.
mainFG :: Container () ()
mainFG = vbox [spacing 2] $ proc _ -> do
(_,c) <- counter -< def
label [] -< [text $ show c]
returnA -< ()
main :: IO ()
main = runFG mainFG
|
|
Synopsis |
|
data FG a b | | | | init :: a -> FG a a | | guard :: a -> FG a a | | (><) :: (Event, a) -> (Event, a) -> (Event, a) | | vmap :: AbstractFunction f => (a -> b) -> f (Event, a) (Event, b) | | value :: (Event, a) -> a | | tag :: AbstractFunction f => b -> f (Event, a) (Event, b) | | arrST :: (a -> s -> (b, s)) -> s -> FG a b | | arrST_ :: (a -> s -> s) -> s -> FG a s | | arrIO :: (a -> IO b) -> FG a b | | hold :: Show s => s -> FG (Maybe s) (Event, s) | | onEvent :: AbstractFunction f => (a -> b) -> b -> f (Event, a) b | | class ArrowDef a | | def :: ArrowDef a => a | | class AbstractFunction f | | runFG :: Container () () -> IO () | | runFG' :: Container a b -> a -> IO () | | type Widget p v = FG [p] (Event, v) | | type Label = Widget LabelP () | | label :: [LabelP] -> Label | | data LabelP | | type Button = Widget ButtonP () | | button :: [ButtonP] -> Button | | data ButtonP | | type ToggleButton = Widget ToggleButtonP Bool | | toggleButton :: [ToggleButtonP] -> ToggleButton | | data ToggleButtonP | | type CheckButton = Widget CheckButtonP Bool | | checkButton :: [CheckButtonP] -> CheckButton | | data CheckButtonP | | type Entry = Widget EntryP String | | entry :: [EntryP] -> Entry | | data EntryP | | type HSeparator = Widget () () | | hSeparator :: HSeparator | | type VSeparator = Widget () () | | vSeparator :: VSeparator | | type Container a b = FG ([WidgetP], a) b | | hbox :: [BoxP] -> FG a b -> Container a b | | vbox :: [BoxP] -> FG a b -> Container a b | | data BoxP | | homogeneous :: Bool -> BoxP | | spacing :: Int -> BoxP | | packRepel :: BoxP | | packGrow :: BoxP | | packNatural :: BoxP | | class Text a | | text :: Text a => String -> a | | class Markup a | | markup :: Markup a => String -> a | | class Enabled a | | enabled :: Enabled a => Bool -> a | | class Visible a | | visible :: Visible a => Bool -> a | | class Active a v | | active :: Active a v => v -> a | | data WidgetP |
|
|
|
Basic Types |
|
data FG a b |
|
|
data Event |
|
|
Arrow Utilities |
|
init :: a -> FG a a |
In a loop context (ie when rec is used) some arrows are not well
defined as they may receive undefined as a value during the first
iteration. Guard those arrows by giving them a default value
during the initial value, by using one of init, guard, or def,
during the first iteration.
Note: init is also defined by the Prelude and List, and guard
is defined in Monad.
|
|
guard :: a -> FG a a |
|
(><) :: (Event, a) -> (Event, a) -> (Event, a) |
>< merges two events, taking the value from the signal with an Event,
if none of the signals have an event than the value is taken from
the first signal. The case where more than one signal have an event
should't happen but if it does the value of the first signal is taken
|
|
vmap :: AbstractFunction f => (a -> b) -> f (Event, a) (Event, b) |
vmap maps the value of a (Event, value) pair with a new value
based on the old
It can either be used as a function or an arrow, when used as a
function it is also a Functor.
|
|
value :: (Event, a) -> a |
value returns a value part of an (Event, value) pair
|
|
tag :: AbstractFunction f => b -> f (Event, a) (Event, b) |
tag tages an event with a value, throwing away the old value.
can either be used as a function or an arrow
|
|
arrST :: (a -> s -> (b, s)) -> s -> FG a b |
arrST is like arr except with state, that is the function takes
two paramaters with the second one represents some sort of internal
state
|
|
arrST_ :: (a -> s -> s) -> s -> FG a s |
|
arrIO :: (a -> IO b) -> FG a b |
arrIO is like arr except that the function may perform IO
This may be called multiple times during a single event, so be
careful. It is best only to perform actions with side effects
during the actual occurrence of the event of interest.
|
|
hold :: Show s => s -> FG (Maybe s) (Event, s) |
hold creates a value that will hold onto a value until instructed
to change it. hold is safe to use in a loop context
|
|
onEvent :: AbstractFunction f => (a -> b) -> b -> f (Event, a) b |
onEvent will call a function on the value of the event when there
is any sort of event otherwise it will return a default value. It is
also safe to in a loop context when used as an arrow.
|
|
class ArrowDef a |
|
|
def :: ArrowDef a => a |
Evaluates to a sensible default value. When used as an Arrow,
ie on the RHS of a -<, evaluates to init which takes a
paramater for the default value, if this parameter is ommited
the default value is def. |
|
class AbstractFunction f |
An AbstractFunction is either a true function or an Arrow | | Instances | |
|
|
runFG :: Container () () -> IO () |
Runs a FG Arrow |
|
runFG' :: Container a b -> a -> IO () |
Runs an FG Arrow with the given input and throws away the return value |
|
Widgets |
|
A Widget is an Arrow corresponding to GUI element. A widget
constructor is generally of the form [p] -> Widget p v where p
is a property type. A property is created using a, possible
overloaded, property function, common propery function include
text, markup, enabled and, visible.
A widget is of the type FG [p] (Event, v). The arrow input is
a list of properties to change. The arrow output is an Event and
the current value associated with the Widget, if any.
The event value is either NoEvent if no event is emitted or Event.
Future versions will have a more specific mechanism to distinguish
between different types of events.
|
|
type Widget p v = FG [p] (Event, v) |
|
type Label = Widget LabelP () |
- doesn't emit any events
- doesn't have any readable properties
|
|
label :: [LabelP] -> Label |
|
data LabelP |
|
|
type Button = Widget ButtonP () |
- emits an Event when pressed
- doesn't have any readable properties
|
|
button :: [ButtonP] -> Button |
|
data ButtonP |
|
|
type ToggleButton = Widget ToggleButtonP Bool |
- emits an Event when pressed
- it's readable property is its current value as a Bool
|
|
toggleButton :: [ToggleButtonP] -> ToggleButton |
|
data ToggleButtonP |
|
|
type CheckButton = Widget CheckButtonP Bool |
- emits an Event when pressed
- it's readable property is its current value as a Bool
|
|
checkButton :: [CheckButtonP] -> CheckButton |
|
data CheckButtonP |
|
|
type Entry = Widget EntryP String |
- doesn't ement any events
- it's readable property is its current value as a String
|
|
entry :: [EntryP] -> Entry |
|
data EntryP |
|
|
type HSeparator = Widget () () |
- desn't emit any events
- desn't have any readable properties
|
|
hSeparator :: HSeparator |
|
type VSeparator = Widget () () |
- doesn't emit any events
- doesn't have any readable properties
|
|
vSeparator :: VSeparator |
|
Container Widgets |
|
type Container a b = FG ([WidgetP], a) b |
A container simply arranges the widgets of the underlying arrow in
a fixed fashion. The first input of an arrow is for dynamically
changing the properties of a container. The second input is passed
to underlying arrow. The output is the same as the underlying
arrow.
|
|
hbox :: [BoxP] -> FG a b -> Container a b |
|
vbox :: [BoxP] -> FG a b -> Container a b |
|
data BoxP |
|
|
homogeneous :: Bool -> BoxP |
|
spacing :: Int -> BoxP |
|
packRepel :: BoxP |
|
packGrow :: BoxP |
|
packNatural :: BoxP |
|
Properties |
|
class Text a |
|
|
text :: Text a => String -> a |
The widget label |
|
class Markup a |
|
|
markup :: Markup a => String -> a |
Like text but is encoded in the Pango Text Attribute Markup language |
|
class Enabled a |
|
|
enabled :: Enabled a => Bool -> a |
If the Widget is enabled, ie can receive user events |
|
class Visible a |
|
|
visible :: Visible a => Bool -> a |
If the Widget is visible |
|
class Active a v |
|
|
active :: Active a v => v -> a |
The active value of the Widget |
|
data WidgetP |
|
|
Implementation Notes |
|
Arrows essentially build up a huge tree like data structure
representing the control flow between arrows. In the current
implementation most of top-level structure has to be traversed when
ever an event is fired -- even if absolutely no actions need to be
taken. When a loop is used parts of this structure may be traversed
multiple times. In particular the inner most loop where an event was
fired from will be traversed "d + 1" times where d is the depth of
the loop. If an event was not fired inside a loop (or any of the sub
loops) than the loop will only be traversed once.
Avoiding this problem of having to traverse most of tree for every
event requires information that I'm not sure the compiler can give me.
For example I need to know the difference between arr (\x -> x) and
arr (\_ -> 10). The first passes the input to the output the second
throws the value away. All I am able to know is that arr was used.
What exactly the function does is a black box.
|
|
Requirements |
|
FG is based on gtk2hs and uses several GHC extensions. It was tested
with GHC 6.2.2 and gtk2hs 0.9.7.
|
|
Produced by Haddock version 0.6 |