ContentsIndex
FG
Portability non-portable (many GHC extensions)
Stability experimental
Maintainer kevina@cs.utah.edu
Contents
Basic Types
Arrow Utilities
Widgets
Container Widgets
Properties
Implementation Notes
Requirements
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
data Event
= NoEvent
| Event
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
Instances
Arrow FG
ArrowLoop FG
AbstractFunction FG
ArrowDef (a -> FG a a)
ArrowDef a => ArrowDef (FG a a)
data Event
Constructors
NoEvent
Event
Instances
ArrowDef Event
Functor ((,) 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
Instances
ArrowDef ()
ArrowDef [a]
ArrowDef (Maybe a)
ArrowDef Event
(ArrowDef a, ArrowDef b) => ArrowDef (a, b)
ArrowDef (a -> FG a a)
ArrowDef a => ArrowDef (FG a 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
AbstractFunction (->)
AbstractFunction FG
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
Instances
Enabled LabelP
Visible LabelP
Text LabelP
Markup LabelP
type Button = Widget ButtonP ()
  • emits an Event when pressed
  • doesn't have any readable properties
button :: [ButtonP] -> Button
data ButtonP
Instances
Enabled ButtonP
Visible ButtonP
Text ButtonP
Markup 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
Instances
Enabled ToggleButtonP
Visible ToggleButtonP
Text ToggleButtonP
Markup ToggleButtonP
Active ToggleButtonP Bool
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
Instances
Enabled CheckButtonP
Visible CheckButtonP
Text CheckButtonP
Markup CheckButtonP
Active CheckButtonP Bool
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
Instances
Enabled EntryP
Visible EntryP
Text 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
Instances
Enabled BoxP
Visible BoxP
homogeneous :: Bool -> BoxP
spacing :: Int -> BoxP
packRepel :: BoxP
packGrow :: BoxP
packNatural :: BoxP
Properties
class Text a
Instances
Text LabelP
Text ButtonP
Text ToggleButtonP
Text CheckButtonP
Text EntryP
text :: Text a => String -> a
The widget label
class Markup a
Instances
Markup LabelP
Markup ButtonP
Markup ToggleButtonP
Markup CheckButtonP
markup :: Markup a => String -> a
Like text but is encoded in the Pango Text Attribute Markup language
class Enabled a
Instances
Enabled LabelP
Enabled ButtonP
Enabled ToggleButtonP
Enabled CheckButtonP
Enabled EntryP
Enabled BoxP
Enabled WidgetP
enabled :: Enabled a => Bool -> a
If the Widget is enabled, ie can receive user events
class Visible a
Instances
Visible LabelP
Visible ButtonP
Visible ToggleButtonP
Visible CheckButtonP
Visible EntryP
Visible BoxP
Visible WidgetP
visible :: Visible a => Bool -> a
If the Widget is visible
class Active a v
Instances
Active ToggleButtonP Bool
Active CheckButtonP Bool
active :: Active a v => v -> a
The active value of the Widget
data WidgetP
Instances
Enabled WidgetP
Visible 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