UISF- Library for Arrowized Graphical User Interfaces.

Copyright(c) Daniel Winograd-Cort 2014
Licensesee the LICENSE file in the distribution
Safe HaskellNone






UI Types

In this module, we will declare the various types to make creating the overall UI possible. We will discuss the ideas for widgets in some detail, but for specifics on the type of a widget (the UISF type), see the UISF type in FRP.UISF.UISF, and for information on specific widgets, see FRP.UISF.Widget.

Widgets are arrows that map multiple inputs to multiple outputs. Additionally, they have a relatively static layout argument that, while it can change over time, is not dependent on any of its inputs at any given moment.

On the input end, a widget will accept:

  • a graphical context,
  • some information about which widget is in focus (for the purposes of routing key presses and mouse clicks and potentially for drawing the widget differently),
  • and the current time.
  • an event with data relating to UI actions.

On the output end, a widget will produce from these inputs:

  • an indicator of whether the widget needs to be redrawn,
  • any focus information that needs to be conveyed to future widgets,
  • the graphics to render to display this widget,
  • and a procedure to run upon termination (for proper shutdown when finished).

Additionally, as widgets are generic arrows, there will be a parameterized input and output types.

type TerminationProc = Maybe (IO ()) Source

The termination procedure is simply a potential IO action.

nullTP :: TerminationProc Source

The null termination procedure is no action.

mergeTP :: TerminationProc -> TerminationProc -> TerminationProc Source

A method for merging two termination procedures.

Rendering Context

data CTX Source

A rendering context specifies the following:




flow :: Flow

A layout direction to flow widgets.

bounds :: Rect

A rectangle bound of current drawing area to render a UI component. It specifies the max size of a widget, not the actual size. It's up to each individual widget to decide where in this bound to put itself.

isConjoined :: Bool

A flag to tell whether we are in a conjoined state or not. A conjoined context will duplicate itself for subcomponents rather than splitting. This can be useful for making compound widgets when one widget takes up space and the other performs some side effect having to do with that space.


data Flow Source

Flow determines widget ordering.


UI Layout

makeLayout Source


:: LayoutType

Horizontal Layout information

-> LayoutType

Vertical Layout information

-> Layout 

Layouts for individual widgets typically come in a few standard flavors, so we have this convenience function for their creation. This function takes layout information for first the horizontal dimension and then the vertical.

data LayoutType Source

A dimension can either be:



Stretchy with a minimum size in pixels


minSize :: Int

Fixed with a size measured in pixels


fixedSize :: Int

nullLayout :: Layout Source

The null layout is useful for "widgets" that do not appear or take up space on the screen.

data Layout Source

More complicated layouts can be manually constructed with direct access to the Layout data type.

  1. wStretch and hStretch specify how much stretching space (in comparative units) in the width and height should be allocated for this widget.
  2. wFixed and hFixed specify how much non-stretching space (in pixels) of width and height should be allocated for this widget.
  3. wMin and hMin specify minimum values (in pixels) of width and height for the widget's stretchy dimensions.
  4. lFill specifies how much expanding space (in comparative units) this widget should fill out in excess space that would otherwise be unused.




wStretch :: Int
hStretch :: Int
wFixed :: Int
hFixed :: Int
wMin :: Int
hMin :: Int
lFill :: Int


Context and Layout Functions

divideCTX :: CTX -> Layout -> Layout -> (CTX, CTX) Source

Divides the CTX among the two given layouts.

mergeLayout :: Flow -> Layout -> Layout -> Layout Source

Merge two layouts into one.


mergeGraphics :: CTX -> (Graphic, Layout) -> (Graphic, Layout) -> Graphic Source

Merging two graphics can be achieved with overGraphic, but the mergeGraphic function additionally constrains the graphics based on their layouts and the context. TODO: Make sure this works as well as it should

System State

type DirtyBit = Bool Source

The dirty bit is a bit to indicate if the widget needs to be redrawn.

type Focus = (WidgetID, FocusInfo) Source

The Focus type helps focusable widgets communicate with each other about which widget is in focus. It consists of a WidgetID and a FocusInfo.

type WidgetID = Int Source

The WidgetID for any given widget is dynamic based on how many focusable widgets are active at the moment. It is designed basically as a counter that focusable widgets will automatically (via the focusable function) increment.

data FocusInfo Source

The FocusInfo means one of the following:



Indicates that this widget is a subwidget of a widget that is in focus. Thus, this widget too is in focus, and this widget should pass HasFocus forward.


Indicates that there is no focus information to communicate between widgets.

SetFocusTo WidgetID

Indicates that the widget whose id is given should take focus. That widget should then pass NoFocus onward.


Any widget that sees this value should recognize that they are no longer in focus. This is useful for nested focus.


data UIEvent Source

The UIEvent data type captures the various types of events that the UI can produce. These are covered by regular keys, special keys, mouse button presses, and mouse movement. Any key event is accompanied by a list of Keys that were down when the given event took place.



A Key UIEvent indicates that the user has typed a regular key on his/her keyboard. These will either be upper or lowercase characters.


char :: Char
modifiers :: [Key]
isDown :: Bool

A SKey UIEvent indicates that the user has typed a special key. These are Enter, Backspace, Tab, Delete, etc. See SpecialKey for more.


skey :: SpecialKey
modifiers :: [Key]
isDown :: Bool

A Button UIEvent indicates that the user has pressed a mouse button.


pt :: Point
mbutton :: MouseButton
isDown :: Bool

Every time the mouse moves, a MouseMove UIEvent will fire.


pt :: Point

The NoUIEvent fires when nothing else is going on. It is important that this happens to allow interaction-independent processing to continue (e.g. timers, animations, etc.).


data Key Source

A Key can either be a character, a special key, or a mouse button.


data SpecialKey Source

A special key is any non-standard character key. According to GLUT, KeyUnknown should never be used, probably because it will be treated as a weird Char instead of a SpecialKey.

data MouseButton Source

The standard mouse buttons are represented, but for specialty mice, one can also use the AdditionalButton value.

Key State Checks

hasShiftModifier :: [Key] -> Bool Source

This is a convenience function that tests whether either of the right or left shift keys is in the given list.

hasCtrlModifier :: [Key] -> Bool Source

This is a convenience function that tests whether either of the right or left control keys is in the given list.

hasAltModifier :: [Key] -> Bool Source

This is a convenience function that tests whether either of the right or left alt keys is in the given list.

isKeyPressed :: Key -> IO Bool Source

Checks the global key state to determine whether the given key is currently pressed down.

Framework Connections

The updateKeyState function is for use by the GUI framework. It is not intended for use unless one wants to build their own framework.

The key state is kept around so that it is easy to check if a given key or button is currently pressed down. Unfortunately, I've coded it as a global IORef, which means I'm using unsafePerformIO.

updateKeyState Source


:: Key

The Key pressed/released.

-> Bool

True if pressed, False if released.

-> IO [Key]

The updated key state.

This should be called by the GUI engine (GLUT) whenever the user presses or releases a key/button. As long as it is called every time, it will keep an accurate key state.