packages feed

UISF 0.3.0.2 → 0.4.0.0

raw patch · 23 files changed

+2838/−1761 lines, 23 filesdep +GLUTdep +arraydep −GLFWPVP ok

version bump matches the API change (PVP)

Dependencies added: GLUT, array

Dependencies removed: GLFW

API changes (from Hackage documentation)

- FRP.UISF: UIParams :: IO () -> IO () -> String -> Dimension -> Flow -> Double -> UIParams
- FRP.UISF: asyncUISFE :: NFData b => Automaton (->) a b -> UISF (SEvent a) (SEvent b)
- FRP.UISF: asyncUISFV :: NFData b => Double -> Double -> Automaton (->) a b -> UISF a [(b, Time)]
- FRP.UISF.AuxFunctions: Automaton :: a b (c, Automaton a b c) -> Automaton b c
- FRP.UISF.AuxFunctions: asyncC :: (ArrowIO a, NFData c) => (ThreadId -> a () ()) -> (Automaton (->) b c) -> a b [c]
- FRP.UISF.AuxFunctions: asyncC' :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData b) => (ThreadId -> a () ()) -> (b -> IO d, e -> IO ()) -> (Automaton (->) (b, d) (c, e)) -> a b [c]
- FRP.UISF.AuxFunctions: asyncE :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => (ThreadId -> a () ()) -> (Automaton (->) b c) -> a (SEvent b) (SEvent c)
- FRP.UISF.AuxFunctions: asyncV :: (ArrowIO a, NFData c) => Double -> DeltaT -> (ThreadId -> a () ()) -> (Automaton (->) b c) -> a (b, Time) [(c, Time)]
- FRP.UISF.AuxFunctions: class Arrow a => ArrowIO a
- FRP.UISF.AuxFunctions: class ArrowTime a
- FRP.UISF.AuxFunctions: initialAIO :: ArrowIO a => IO d -> (d -> a b c) -> a b c
- FRP.UISF.AuxFunctions: liftAIO :: ArrowIO a => (b -> IO c) -> a b c
- FRP.UISF.AuxFunctions: newtype Automaton (a :: * -> * -> *) b c :: (* -> * -> *) -> * -> * -> *
- FRP.UISF.AuxFunctions: time :: ArrowTime a => a () Time
- FRP.UISF.SOE: BACKSPACE :: SpecialKey
- FRP.UISF.SOE: Black :: Color
- FRP.UISF.SOE: Blue :: Color
- FRP.UISF.SOE: Button :: Point -> Bool -> Bool -> UIEvent
- FRP.UISF.SOE: CharKey :: Char -> Key
- FRP.UISF.SOE: Closed :: UIEvent
- FRP.UISF.SOE: Cyan :: Color
- FRP.UISF.SOE: DEL :: SpecialKey
- FRP.UISF.SOE: DOWN :: SpecialKey
- FRP.UISF.SOE: END :: SpecialKey
- FRP.UISF.SOE: ENTER :: SpecialKey
- FRP.UISF.SOE: ESC :: SpecialKey
- FRP.UISF.SOE: F1 :: SpecialKey
- FRP.UISF.SOE: F10 :: SpecialKey
- FRP.UISF.SOE: F11 :: SpecialKey
- FRP.UISF.SOE: F12 :: SpecialKey
- FRP.UISF.SOE: F13 :: SpecialKey
- FRP.UISF.SOE: F14 :: SpecialKey
- FRP.UISF.SOE: F15 :: SpecialKey
- FRP.UISF.SOE: F16 :: SpecialKey
- FRP.UISF.SOE: F17 :: SpecialKey
- FRP.UISF.SOE: F18 :: SpecialKey
- FRP.UISF.SOE: F19 :: SpecialKey
- FRP.UISF.SOE: F2 :: SpecialKey
- FRP.UISF.SOE: F20 :: SpecialKey
- FRP.UISF.SOE: F21 :: SpecialKey
- FRP.UISF.SOE: F22 :: SpecialKey
- FRP.UISF.SOE: F23 :: SpecialKey
- FRP.UISF.SOE: F24 :: SpecialKey
- FRP.UISF.SOE: F25 :: SpecialKey
- FRP.UISF.SOE: F3 :: SpecialKey
- FRP.UISF.SOE: F4 :: SpecialKey
- FRP.UISF.SOE: F5 :: SpecialKey
- FRP.UISF.SOE: F6 :: SpecialKey
- FRP.UISF.SOE: F7 :: SpecialKey
- FRP.UISF.SOE: F8 :: SpecialKey
- FRP.UISF.SOE: F9 :: SpecialKey
- FRP.UISF.SOE: Green :: Color
- FRP.UISF.SOE: HOME :: SpecialKey
- FRP.UISF.SOE: INSERT :: SpecialKey
- FRP.UISF.SOE: KP_0 :: SpecialKey
- FRP.UISF.SOE: KP_1 :: SpecialKey
- FRP.UISF.SOE: KP_2 :: SpecialKey
- FRP.UISF.SOE: KP_3 :: SpecialKey
- FRP.UISF.SOE: KP_4 :: SpecialKey
- FRP.UISF.SOE: KP_5 :: SpecialKey
- FRP.UISF.SOE: KP_6 :: SpecialKey
- FRP.UISF.SOE: KP_7 :: SpecialKey
- FRP.UISF.SOE: KP_8 :: SpecialKey
- FRP.UISF.SOE: KP_9 :: SpecialKey
- FRP.UISF.SOE: KP_ADD :: SpecialKey
- FRP.UISF.SOE: KP_DECIMAL :: SpecialKey
- FRP.UISF.SOE: KP_DIVIDE :: SpecialKey
- FRP.UISF.SOE: KP_ENTER :: SpecialKey
- FRP.UISF.SOE: KP_EQUAL :: SpecialKey
- FRP.UISF.SOE: KP_MULTIPLY :: SpecialKey
- FRP.UISF.SOE: KP_SUBTRACT :: SpecialKey
- FRP.UISF.SOE: Key :: Char -> ([Char], [SpecialKey]) -> Bool -> UIEvent
- FRP.UISF.SOE: LALT :: SpecialKey
- FRP.UISF.SOE: LCTRL :: SpecialKey
- FRP.UISF.SOE: LEFT :: SpecialKey
- FRP.UISF.SOE: LSHIFT :: SpecialKey
- FRP.UISF.SOE: Magenta :: Color
- FRP.UISF.SOE: MouseMove :: Point -> UIEvent
- FRP.UISF.SOE: NoUIEvent :: UIEvent
- FRP.UISF.SOE: PAGEDOWN :: SpecialKey
- FRP.UISF.SOE: PAGEUP :: SpecialKey
- FRP.UISF.SOE: RALT :: SpecialKey
- FRP.UISF.SOE: RCTRL :: SpecialKey
- FRP.UISF.SOE: RIGHT :: SpecialKey
- FRP.UISF.SOE: RSHIFT :: SpecialKey
- FRP.UISF.SOE: Red :: Color
- FRP.UISF.SOE: Refresh :: UIEvent
- FRP.UISF.SOE: Resize :: Size -> UIEvent
- FRP.UISF.SOE: SKey :: SpecialKey -> ([Char], [SpecialKey]) -> Bool -> UIEvent
- FRP.UISF.SOE: SpecialKey :: SpecialKey -> Key
- FRP.UISF.SOE: TAB :: SpecialKey
- FRP.UISF.SOE: UNKNOWN :: SpecialKey
- FRP.UISF.SOE: UP :: SpecialKey
- FRP.UISF.SOE: White :: Color
- FRP.UISF.SOE: Yellow :: Color
- FRP.UISF.SOE: arc :: Point -> Point -> Angle -> Angle -> Graphic
- FRP.UISF.SOE: char :: UIEvent -> Char
- FRP.UISF.SOE: clearWindow :: Window -> IO ()
- FRP.UISF.SOE: closeWindow :: Window -> IO ()
- FRP.UISF.SOE: data Color
- FRP.UISF.SOE: data Graphic
- FRP.UISF.SOE: data Key :: *
- FRP.UISF.SOE: data RedrawMode
- FRP.UISF.SOE: data SpecialKey :: *
- FRP.UISF.SOE: data UIEvent
- FRP.UISF.SOE: data Window
- FRP.UISF.SOE: data Word32 :: *
- FRP.UISF.SOE: drawBufferedGraphic :: RedrawMode
- FRP.UISF.SOE: drawGraphic :: RedrawMode
- FRP.UISF.SOE: drawInWindow :: Window -> Graphic -> IO ()
- FRP.UISF.SOE: drawInWindowNow :: Window -> Graphic -> IO ()
- FRP.UISF.SOE: ellipse :: Point -> Point -> Graphic
- FRP.UISF.SOE: emptyGraphic :: Graphic
- FRP.UISF.SOE: getMainWindowSize :: IO Size
- FRP.UISF.SOE: getWindowEvent :: Double -> Window -> IO UIEvent
- FRP.UISF.SOE: hasAltModifier :: ([Char], [SpecialKey]) -> Bool
- FRP.UISF.SOE: hasCtrlModifier :: ([Char], [SpecialKey]) -> Bool
- FRP.UISF.SOE: hasShiftModifier :: ([Char], [SpecialKey]) -> Bool
- FRP.UISF.SOE: instance Bounded Color
- FRP.UISF.SOE: instance Enum Color
- FRP.UISF.SOE: instance Eq Color
- FRP.UISF.SOE: instance Ix Color
- FRP.UISF.SOE: instance Ord Color
- FRP.UISF.SOE: instance Read Color
- FRP.UISF.SOE: instance Show Color
- FRP.UISF.SOE: instance Show UIEvent
- FRP.UISF.SOE: isDown :: UIEvent -> Bool
- FRP.UISF.SOE: isKeyPressed :: Enum a => a -> IO Bool
- FRP.UISF.SOE: isLeft :: UIEvent -> Bool
- FRP.UISF.SOE: line :: Point -> Point -> Graphic
- FRP.UISF.SOE: maybeGetWindowEvent :: Double -> Window -> IO (Maybe UIEvent)
- FRP.UISF.SOE: modifiers :: UIEvent -> ([Char], [SpecialKey])
- FRP.UISF.SOE: nullGraphic :: Graphic
- FRP.UISF.SOE: openWindow :: Title -> Size -> IO Window
- FRP.UISF.SOE: openWindowEx :: Title -> Maybe Point -> Maybe Size -> RedrawMode -> IO Window
- FRP.UISF.SOE: overGraphic :: Graphic -> Graphic -> Graphic
- FRP.UISF.SOE: overGraphics :: [Graphic] -> Graphic
- FRP.UISF.SOE: polyBezier :: [Point] -> Graphic
- FRP.UISF.SOE: polygon :: [Point] -> Graphic
- FRP.UISF.SOE: polyline :: [Point] -> Graphic
- FRP.UISF.SOE: pt :: UIEvent -> Point
- FRP.UISF.SOE: rgb :: (Integral r, Integral g, Integral b) => r -> g -> b -> RGB
- FRP.UISF.SOE: rgba :: (Integral r, Integral g, Integral b, Integral a) => r -> g -> b -> a -> RGBA
- FRP.UISF.SOE: runGraphics :: IO () -> IO ()
- FRP.UISF.SOE: scissorGraphic :: (Point, Size) -> Graphic -> Graphic
- FRP.UISF.SOE: setDirty :: Window -> IO ()
- FRP.UISF.SOE: setGraphic :: Window -> Graphic -> IO ()
- FRP.UISF.SOE: setGraphic' :: Window -> Graphic -> IO ()
- FRP.UISF.SOE: shearEllipse :: Point -> Point -> Point -> Graphic
- FRP.UISF.SOE: skey :: UIEvent -> SpecialKey
- FRP.UISF.SOE: text :: Point -> String -> Graphic
- FRP.UISF.SOE: timeGetTime :: IO Double
- FRP.UISF.SOE: translateGraphic :: (Int, Int) -> Graphic -> Graphic
- FRP.UISF.SOE: type Angle = GLfloat
- FRP.UISF.SOE: type Point = (Int, Int)
- FRP.UISF.SOE: type RGB = Color3 GLfloat
- FRP.UISF.SOE: type RGBA = Color4 GLfloat
- FRP.UISF.SOE: type Size = (Int, Int)
- FRP.UISF.SOE: type Title = String
- FRP.UISF.SOE: withColor :: Color -> Graphic -> Graphic
- FRP.UISF.SOE: withColor' :: Color a => a -> Graphic -> Graphic
- FRP.UISF.SOE: word32ToInt :: Word32 -> Int
- FRP.UISF.UISF: UIParams :: IO () -> IO () -> String -> Dimension -> Flow -> Double -> UIParams
- FRP.UISF.UISF: asyncUISFE :: NFData b => Automaton (->) a b -> UISF (SEvent a) (SEvent b)
- FRP.UISF.UISF: asyncUISFV :: NFData b => Double -> Double -> Automaton (->) a b -> UISF a [(b, Time)]
- FRP.UISF.UISF: getTime :: UISF () Time
- FRP.UISF.UISF: instance [overlap ok] ArrowTime UISF
- FRP.UISF.UITypes: hFill :: Layout -> Int
- FRP.UISF.UITypes: minH :: Layout -> Int
- FRP.UISF.UITypes: minW :: Layout -> Int
- FRP.UISF.UITypes: type Dimension = (Int, Int)
- FRP.UISF.UITypes: type Rect = (Point, Dimension)
- FRP.UISF.UITypes: vFill :: Layout -> Int
- FRP.UISF.UITypes: vFixed :: Layout -> Int
- FRP.UISF.Widget: cyclebox :: Layout -> [(Rect -> Bool -> Graphic, b)] -> Int -> UISF () b
- FRP.UISF.Widget: cyclebox' :: Layout -> [(Rect -> Bool -> Graphic, b)] -> Int -> UISF (SEvent Int) b
- FRP.UISF.Widget: focusable :: UISF a b -> UISF a b
- FRP.UISF.Widget: isInFocus :: UISF () Bool
- FRP.UISF.Widget: mkBasicWidget :: Layout -> (Rect -> Graphic) -> UISF a a
- FRP.UISF.Widget: mkSlider :: Eq a => Bool -> (a -> Int -> Int) -> (Int -> Int -> a) -> (Int -> Int -> a -> a) -> a -> UISF () a
- FRP.UISF.Widget: mkWidget :: s -> Layout -> (a -> s -> Rect -> UIEvent -> (b, s, DirtyBit)) -> (Rect -> Bool -> s -> Graphic) -> UISF a b
- FRP.UISF.Widget: textboxE :: String -> UISF (SEvent String) String
- FRP.UISF.Widget: toggle :: Eq s => s -> Layout -> (Rect -> Bool -> s -> Graphic) -> UISF s Bool
+ FRP.UISF: CharWrap :: WrapSetting
+ FRP.UISF: DarkBeige :: Color
+ FRP.UISF: Gray :: Color
+ FRP.UISF: LightBeige :: Color
+ FRP.UISF: MediumBeige :: Color
+ FRP.UISF: NoWrap :: WrapSetting
+ FRP.UISF: VLightBeige :: Color
+ FRP.UISF: WordWrap :: WrapSetting
+ FRP.UISF: asyncC :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => PureAuto b c -> a b [c]
+ FRP.UISF: asyncE :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => PureAuto b c -> a (SEvent b) (SEvent c)
+ FRP.UISF: asyncVT :: (ArrowIO a, ArrowCircuit a, ArrowReader DeltaT a, NFData c) => Double -> DeltaT -> PureAuto b c -> a b [(c, Time)]
+ FRP.UISF: class Arrow a => ArrowIO a
+ FRP.UISF: data Layout
+ FRP.UISF: data WrapSetting
+ FRP.UISF: displayField :: UITexty s => WrapSetting -> UISF s ()
+ FRP.UISF: getDeltaTime :: UISF b DeltaT
+ FRP.UISF: initialAIO :: ArrowIO a => IO d -> (d -> a b c) -> a b c
+ FRP.UISF: liftAIO :: ArrowIO a => (b -> IO c) -> a b c
+ FRP.UISF: listbox' :: (Eq a, Show a) => UISF ([a], Int) Int
+ FRP.UISF: scrollable :: Layout -> Dimension -> UISF a b -> UISF a b
+ FRP.UISF: spacer :: UISF a a
+ FRP.UISF: terminalAIO :: ArrowIO a => IO () -> a b b
+ FRP.UISF: textField :: UITexty s => WrapSetting -> s -> UISF (SEvent s) UIText
+ FRP.UISF: uiBackground :: UIParams -> RGB
+ FRP.UISF: uiCloseOnEsc :: UIParams -> Bool
+ FRP.UISF.Asynchrony: Automaton :: a b (c, Automaton a b c) -> Automaton b c
+ FRP.UISF.Asynchrony: actionToIOAuto :: (b -> IO c) -> IOAuto b c
+ FRP.UISF.Asynchrony: asyncC :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => PureAuto b c -> a b [c]
+ FRP.UISF.Asynchrony: asyncCIO :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => (IO d, d -> IO ()) -> (d -> IOAuto b c) -> a b [c]
+ FRP.UISF.Asynchrony: asyncCIOOn :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => Int -> (IO d, d -> IO ()) -> (d -> IOAuto b c) -> a b [c]
+ FRP.UISF.Asynchrony: asyncCOn :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => Int -> PureAuto b c -> a b [c]
+ FRP.UISF.Asynchrony: asyncE :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => PureAuto b c -> a (SEvent b) (SEvent c)
+ FRP.UISF.Asynchrony: asyncEIO :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => (IO d, d -> IO ()) -> (d -> IOAuto b c) -> a (SEvent b) (SEvent c)
+ FRP.UISF.Asynchrony: asyncEIOOn :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => Int -> (IO d, d -> IO ()) -> (d -> IOAuto b c) -> a (SEvent b) (SEvent c)
+ FRP.UISF.Asynchrony: asyncEOn :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => Int -> PureAuto b c -> a (SEvent b) (SEvent c)
+ FRP.UISF.Asynchrony: asyncV :: (ArrowIO a, NFData c) => Double -> DeltaT -> PureAuto b c -> a (b, Time) [(c, Time)]
+ FRP.UISF.Asynchrony: asyncVOn :: (ArrowIO a, NFData c) => Int -> Double -> DeltaT -> PureAuto b c -> a (b, Time) [(c, Time)]
+ FRP.UISF.Asynchrony: asyncVT :: (ArrowIO a, ArrowCircuit a, ArrowReader DeltaT a, NFData c) => Double -> DeltaT -> PureAuto b c -> a b [(c, Time)]
+ FRP.UISF.Asynchrony: asyncVTOn :: (ArrowIO a, ArrowCircuit a, ArrowReader DeltaT a, NFData c) => Int -> Double -> DeltaT -> PureAuto b c -> a b [(c, Time)]
+ FRP.UISF.Asynchrony: class Arrow a => ArrowIO a
+ FRP.UISF.Asynchrony: initialAIO :: ArrowIO a => IO d -> (d -> a b c) -> a b c
+ FRP.UISF.Asynchrony: liftAIO :: ArrowIO a => (b -> IO c) -> a b c
+ FRP.UISF.Asynchrony: newtype Automaton (a :: * -> * -> *) b c :: (* -> * -> *) -> * -> * -> *
+ FRP.UISF.Asynchrony: pureAutoToIOAuto :: PureAuto b c -> IOAuto b c
+ FRP.UISF.Asynchrony: statefulActionToIOAuto :: s -> ((b, s) -> IO (c, s)) -> IOAuto b c
+ FRP.UISF.Asynchrony: statefulFunctionToAutomaton :: ArrowLoop a => s -> ((b, s) -> (c, s)) -> Automaton a b c
+ FRP.UISF.Asynchrony: terminalAIO :: ArrowIO a => IO () -> a b b
+ FRP.UISF.AuxFunctions: accumTime :: (ArrowCircuit a, ArrowReader DeltaT a) => a b Time
+ FRP.UISF.AuxFunctions: class ArrowLoop a => ArrowCircuit (a :: * -> * -> *)
+ FRP.UISF.AuxFunctions: getDeltaT :: ArrowReader DeltaT a => a b DeltaT
+ FRP.UISF.Examples.Crud: deleteFromDB :: (a -> Bool) -> Int -> Database a -> Database a
+ FRP.UISF.Examples.Crud: updateDB :: (a -> Bool) -> Int -> a -> Database a -> Database a
+ FRP.UISF.Examples.Examples: linesWith :: [Char] -> [[Char]]
+ FRP.UISF.Examples.Examples: uitext :: UIText
+ FRP.UISF.Examples.Examples: uitext' :: UIText
+ FRP.UISF.Examples.Examples: uitextdemo :: UISF a ()
+ FRP.UISF.Graphics: Black :: Color
+ FRP.UISF.Graphics: Blue :: Color
+ FRP.UISF.Graphics: CharWrap :: WrapSetting
+ FRP.UISF.Graphics: Cyan :: Color
+ FRP.UISF.Graphics: DarkBeige :: Color
+ FRP.UISF.Graphics: Fixed8By13 :: BitmapFont
+ FRP.UISF.Graphics: Fixed9By15 :: BitmapFont
+ FRP.UISF.Graphics: Gray :: Color
+ FRP.UISF.Graphics: Green :: Color
+ FRP.UISF.Graphics: Helvetica10 :: BitmapFont
+ FRP.UISF.Graphics: Helvetica12 :: BitmapFont
+ FRP.UISF.Graphics: Helvetica18 :: BitmapFont
+ FRP.UISF.Graphics: LightBeige :: Color
+ FRP.UISF.Graphics: Magenta :: Color
+ FRP.UISF.Graphics: MediumBeige :: Color
+ FRP.UISF.Graphics: NoWrap :: WrapSetting
+ FRP.UISF.Graphics: Red :: Color
+ FRP.UISF.Graphics: TimesRoman10 :: BitmapFont
+ FRP.UISF.Graphics: TimesRoman24 :: BitmapFont
+ FRP.UISF.Graphics: UIText :: [(Maybe RGB, BitmapFont, String)] -> UIText
+ FRP.UISF.Graphics: VLightBeige :: Color
+ FRP.UISF.Graphics: White :: Color
+ FRP.UISF.Graphics: WordWrap :: WrapSetting
+ FRP.UISF.Graphics: Yellow :: Color
+ FRP.UISF.Graphics: appendUIText :: (UITexty s1, UITexty s2) => s1 -> s2 -> UIText
+ FRP.UISF.Graphics: arc :: Rect -> Angle -> Angle -> Graphic
+ FRP.UISF.Graphics: boundGraphic :: Rect -> Graphic -> Graphic
+ FRP.UISF.Graphics: circleFilled :: Point -> Int -> Graphic
+ FRP.UISF.Graphics: circleOutline :: Point -> Int -> Graphic
+ FRP.UISF.Graphics: class UITexty a
+ FRP.UISF.Graphics: colorToRGB :: Color -> RGB
+ FRP.UISF.Graphics: coloredUIText :: UITexty s => Color -> s -> UIText
+ FRP.UISF.Graphics: data BitmapFont :: *
+ FRP.UISF.Graphics: data Color
+ FRP.UISF.Graphics: data Graphic
+ FRP.UISF.Graphics: data RGB
+ FRP.UISF.Graphics: data WrapSetting
+ FRP.UISF.Graphics: dropUIText :: Int -> UIText -> UIText
+ FRP.UISF.Graphics: ellipse :: Rect -> Graphic
+ FRP.UISF.Graphics: extractRGB :: (Integral r, Integral g, Integral b) => RGB -> (r, g, b)
+ FRP.UISF.Graphics: fontUIText :: UITexty s => BitmapFont -> s -> UIText
+ FRP.UISF.Graphics: line :: Point -> Point -> Graphic
+ FRP.UISF.Graphics: newtype UIText
+ FRP.UISF.Graphics: nullGraphic :: Graphic
+ FRP.UISF.Graphics: overGraphic :: Graphic -> Graphic -> Graphic
+ FRP.UISF.Graphics: polybezier :: [Point] -> Graphic
+ FRP.UISF.Graphics: polygon :: [Point] -> Graphic
+ FRP.UISF.Graphics: polyline :: [Point] -> Graphic
+ FRP.UISF.Graphics: prepText :: UITexty s => WrapSetting -> Double -> Rect -> s -> ([Point], [UIText])
+ FRP.UISF.Graphics: pureUIText :: String -> UIText
+ FRP.UISF.Graphics: rectangleFilled :: Rect -> Graphic
+ FRP.UISF.Graphics: rectangleOutline :: Rect -> Graphic
+ FRP.UISF.Graphics: rgb :: (Integral r, Integral g, Integral b) => r -> g -> b -> Maybe RGB
+ FRP.UISF.Graphics: rgbE :: (Integral r, Integral g, Integral b, Show r, Show g, Show b) => r -> g -> b -> RGB
+ FRP.UISF.Graphics: rgbUIText :: UITexty s => Maybe RGB -> s -> UIText
+ FRP.UISF.Graphics: rotateGraphic :: Point -> Angle -> Graphic -> Graphic
+ FRP.UISF.Graphics: scaleGraphic :: Double -> Double -> Graphic -> Graphic
+ FRP.UISF.Graphics: shearEllipse :: Point -> Rect -> Graphic
+ FRP.UISF.Graphics: splitUIText :: Int -> UIText -> (UIText, UIText)
+ FRP.UISF.Graphics: takeUIText :: Int -> UIText -> UIText
+ FRP.UISF.Graphics: text :: UITexty s => Point -> s -> Graphic
+ FRP.UISF.Graphics: textHeight :: UITexty s => s -> Int
+ FRP.UISF.Graphics: textHeight' :: BitmapFont -> String -> Int
+ FRP.UISF.Graphics: textLines :: UITexty s => [(Point, s)] -> Graphic
+ FRP.UISF.Graphics: textWidth :: UITexty s => s -> Int
+ FRP.UISF.Graphics: textWidth' :: BitmapFont -> String -> Int
+ FRP.UISF.Graphics: textWithinPixels :: UITexty s => Int -> s -> (UIText, UIText)
+ FRP.UISF.Graphics: textWithinPixels' :: BitmapFont -> Int -> String -> (String, String)
+ FRP.UISF.Graphics: toUIText :: UITexty a => a -> UIText
+ FRP.UISF.Graphics: translateGraphic :: Point -> Graphic -> Graphic
+ FRP.UISF.Graphics: type Angle = Double
+ FRP.UISF.Graphics: type Dimension = (Int, Int)
+ FRP.UISF.Graphics: type Point = (Int, Int)
+ FRP.UISF.Graphics: type Rect = (Point, Dimension)
+ FRP.UISF.Graphics: uitextLen :: UIText -> Int
+ FRP.UISF.Graphics: uitextToString :: UIText -> String
+ FRP.UISF.Graphics: unwrapUIT :: UIText -> [(Maybe RGB, BitmapFont, String)]
+ FRP.UISF.Graphics: withColor :: Color -> Graphic -> Graphic
+ FRP.UISF.Graphics: withColor' :: RGB -> Graphic -> Graphic
+ FRP.UISF.Graphics.Color: Black :: Color
+ FRP.UISF.Graphics.Color: Blue :: Color
+ FRP.UISF.Graphics.Color: Cyan :: Color
+ FRP.UISF.Graphics.Color: DarkBeige :: Color
+ FRP.UISF.Graphics.Color: Gray :: Color
+ FRP.UISF.Graphics.Color: Green :: Color
+ FRP.UISF.Graphics.Color: LightBeige :: Color
+ FRP.UISF.Graphics.Color: Magenta :: Color
+ FRP.UISF.Graphics.Color: MediumBeige :: Color
+ FRP.UISF.Graphics.Color: Red :: Color
+ FRP.UISF.Graphics.Color: VLightBeige :: Color
+ FRP.UISF.Graphics.Color: White :: Color
+ FRP.UISF.Graphics.Color: Yellow :: Color
+ FRP.UISF.Graphics.Color: colorToRGB :: Color -> RGB
+ FRP.UISF.Graphics.Color: data Color
+ FRP.UISF.Graphics.Color: data RGB
+ FRP.UISF.Graphics.Color: extractRGB :: (Integral r, Integral g, Integral b) => RGB -> (r, g, b)
+ FRP.UISF.Graphics.Color: instance Bounded Color
+ FRP.UISF.Graphics.Color: instance Enum Color
+ FRP.UISF.Graphics.Color: instance Eq Color
+ FRP.UISF.Graphics.Color: instance Eq RGB
+ FRP.UISF.Graphics.Color: instance Ix Color
+ FRP.UISF.Graphics.Color: instance NFData Color
+ FRP.UISF.Graphics.Color: instance NFData RGB
+ FRP.UISF.Graphics.Color: instance Ord Color
+ FRP.UISF.Graphics.Color: instance Read Color
+ FRP.UISF.Graphics.Color: instance Show Color
+ FRP.UISF.Graphics.Color: instance Show RGB
+ FRP.UISF.Graphics.Color: rgb :: (Integral r, Integral g, Integral b) => r -> g -> b -> Maybe RGB
+ FRP.UISF.Graphics.Color: rgbE :: (Integral r, Integral g, Integral b, Show r, Show g, Show b) => r -> g -> b -> RGB
+ FRP.UISF.Graphics.Graphic: GArc :: Rect -> Angle -> Angle -> Graphic
+ FRP.UISF.Graphics.Graphic: GBezier :: [Point] -> Graphic
+ FRP.UISF.Graphics.Graphic: GBounded :: Rect -> Graphic -> Graphic
+ FRP.UISF.Graphics.Graphic: GColor :: RGB -> Graphic -> Graphic
+ FRP.UISF.Graphics.Graphic: GEllipse :: Rect -> Graphic
+ FRP.UISF.Graphics.Graphic: GPolyLine :: [Point] -> Graphic
+ FRP.UISF.Graphics.Graphic: GPolygon :: [Point] -> Graphic
+ FRP.UISF.Graphics.Graphic: GRotate :: Point -> Angle -> Graphic -> Graphic
+ FRP.UISF.Graphics.Graphic: GScale :: Double -> Double -> Graphic -> Graphic
+ FRP.UISF.Graphics.Graphic: GText :: Point -> UIText -> Graphic
+ FRP.UISF.Graphics.Graphic: GTranslate :: Point -> Graphic -> Graphic
+ FRP.UISF.Graphics.Graphic: NoGraphic :: Graphic
+ FRP.UISF.Graphics.Graphic: OverGraphic :: Graphic -> Graphic -> Graphic
+ FRP.UISF.Graphics.Graphic: arc :: Rect -> Angle -> Angle -> Graphic
+ FRP.UISF.Graphics.Graphic: boundGraphic :: Rect -> Graphic -> Graphic
+ FRP.UISF.Graphics.Graphic: circleFilled :: Point -> Int -> Graphic
+ FRP.UISF.Graphics.Graphic: circleOutline :: Point -> Int -> Graphic
+ FRP.UISF.Graphics.Graphic: data Graphic
+ FRP.UISF.Graphics.Graphic: ellipse :: Rect -> Graphic
+ FRP.UISF.Graphics.Graphic: instance Eq Graphic
+ FRP.UISF.Graphics.Graphic: instance NFData Graphic
+ FRP.UISF.Graphics.Graphic: instance Show Graphic
+ FRP.UISF.Graphics.Graphic: line :: Point -> Point -> Graphic
+ FRP.UISF.Graphics.Graphic: nullGraphic :: Graphic
+ FRP.UISF.Graphics.Graphic: overGraphic :: Graphic -> Graphic -> Graphic
+ FRP.UISF.Graphics.Graphic: polybezier :: [Point] -> Graphic
+ FRP.UISF.Graphics.Graphic: polygon :: [Point] -> Graphic
+ FRP.UISF.Graphics.Graphic: polyline :: [Point] -> Graphic
+ FRP.UISF.Graphics.Graphic: rectangleFilled :: Rect -> Graphic
+ FRP.UISF.Graphics.Graphic: rectangleOutline :: Rect -> Graphic
+ FRP.UISF.Graphics.Graphic: rotateGraphic :: Point -> Angle -> Graphic -> Graphic
+ FRP.UISF.Graphics.Graphic: scaleGraphic :: Double -> Double -> Graphic -> Graphic
+ FRP.UISF.Graphics.Graphic: shearEllipse :: Point -> Rect -> Graphic
+ FRP.UISF.Graphics.Graphic: text :: UITexty s => Point -> s -> Graphic
+ FRP.UISF.Graphics.Graphic: textLines :: UITexty s => [(Point, s)] -> Graphic
+ FRP.UISF.Graphics.Graphic: translateGraphic :: Point -> Graphic -> Graphic
+ FRP.UISF.Graphics.Graphic: withColor :: Color -> Graphic -> Graphic
+ FRP.UISF.Graphics.Graphic: withColor' :: RGB -> Graphic -> Graphic
+ FRP.UISF.Graphics.Text: CharWrap :: WrapSetting
+ FRP.UISF.Graphics.Text: Fixed8By13 :: BitmapFont
+ FRP.UISF.Graphics.Text: Fixed9By15 :: BitmapFont
+ FRP.UISF.Graphics.Text: Helvetica10 :: BitmapFont
+ FRP.UISF.Graphics.Text: Helvetica12 :: BitmapFont
+ FRP.UISF.Graphics.Text: Helvetica18 :: BitmapFont
+ FRP.UISF.Graphics.Text: NoWrap :: WrapSetting
+ FRP.UISF.Graphics.Text: TimesRoman10 :: BitmapFont
+ FRP.UISF.Graphics.Text: TimesRoman24 :: BitmapFont
+ FRP.UISF.Graphics.Text: UIText :: [(Maybe RGB, BitmapFont, String)] -> UIText
+ FRP.UISF.Graphics.Text: WordWrap :: WrapSetting
+ FRP.UISF.Graphics.Text: appendUIText :: (UITexty s1, UITexty s2) => s1 -> s2 -> UIText
+ FRP.UISF.Graphics.Text: class UITexty a
+ FRP.UISF.Graphics.Text: coloredUIText :: UITexty s => Color -> s -> UIText
+ FRP.UISF.Graphics.Text: data BitmapFont :: *
+ FRP.UISF.Graphics.Text: data WrapSetting
+ FRP.UISF.Graphics.Text: dropUIText :: Int -> UIText -> UIText
+ FRP.UISF.Graphics.Text: fontUIText :: UITexty s => BitmapFont -> s -> UIText
+ FRP.UISF.Graphics.Text: instance Eq UIText
+ FRP.UISF.Graphics.Text: instance Eq WrapSetting
+ FRP.UISF.Graphics.Text: instance IsString UIText
+ FRP.UISF.Graphics.Text: instance NFData BitmapFont
+ FRP.UISF.Graphics.Text: instance NFData UIText
+ FRP.UISF.Graphics.Text: instance Show UIText
+ FRP.UISF.Graphics.Text: instance Show WrapSetting
+ FRP.UISF.Graphics.Text: instance UITexty String
+ FRP.UISF.Graphics.Text: instance UITexty UIText
+ FRP.UISF.Graphics.Text: newtype UIText
+ FRP.UISF.Graphics.Text: prepText :: UITexty s => WrapSetting -> Double -> Rect -> s -> ([Point], [UIText])
+ FRP.UISF.Graphics.Text: pureUIText :: String -> UIText
+ FRP.UISF.Graphics.Text: rgbUIText :: UITexty s => Maybe RGB -> s -> UIText
+ FRP.UISF.Graphics.Text: splitUIText :: Int -> UIText -> (UIText, UIText)
+ FRP.UISF.Graphics.Text: takeUIText :: Int -> UIText -> UIText
+ FRP.UISF.Graphics.Text: textHeight :: UITexty s => s -> Int
+ FRP.UISF.Graphics.Text: textHeight' :: BitmapFont -> String -> Int
+ FRP.UISF.Graphics.Text: textWidth :: UITexty s => s -> Int
+ FRP.UISF.Graphics.Text: textWidth' :: BitmapFont -> String -> Int
+ FRP.UISF.Graphics.Text: textWithinPixels :: UITexty s => Int -> s -> (UIText, UIText)
+ FRP.UISF.Graphics.Text: textWithinPixels' :: BitmapFont -> Int -> String -> (String, String)
+ FRP.UISF.Graphics.Text: toUIText :: UITexty a => a -> UIText
+ FRP.UISF.Graphics.Text: uitextLen :: UIText -> Int
+ FRP.UISF.Graphics.Text: uitextLines :: UIText -> [UIText]
+ FRP.UISF.Graphics.Text: uitextToString :: UIText -> String
+ FRP.UISF.Graphics.Text: uitextWords :: UIText -> [UIText]
+ FRP.UISF.Graphics.Text: unwrapUIT :: UIText -> [(Maybe RGB, BitmapFont, String)]
+ FRP.UISF.Graphics.Types: type Angle = Double
+ FRP.UISF.Graphics.Types: type Dimension = (Int, Int)
+ FRP.UISF.Graphics.Types: type Point = (Int, Int)
+ FRP.UISF.Graphics.Types: type Rect = (Point, Dimension)
+ FRP.UISF.Render.GLUT: WindowData :: ((Graphic, DirtyBit) -> IO ()) -> IO (Maybe Window) -> IO Dimension -> IO UIEvent -> IO UIEvent -> IO Double -> WindowData
+ FRP.UISF.Render.GLUT: closeWindow :: Window -> IO ()
+ FRP.UISF.Render.GLUT: data Window :: *
+ FRP.UISF.Render.GLUT: data WindowData
+ FRP.UISF.Render.GLUT: getElapsedGUITime :: WindowData -> IO Double
+ FRP.UISF.Render.GLUT: getNextEvent :: WindowData -> IO UIEvent
+ FRP.UISF.Render.GLUT: getWindow :: WindowData -> IO (Maybe Window)
+ FRP.UISF.Render.GLUT: getWindowDim :: WindowData -> IO Dimension
+ FRP.UISF.Render.GLUT: glutKeyToKey :: Key -> Key
+ FRP.UISF.Render.GLUT: openWindow :: RGB -> String -> Dimension -> IO WindowData
+ FRP.UISF.Render.GLUT: peekNextEvent :: WindowData -> IO UIEvent
+ FRP.UISF.Render.GLUT: renderGraphicInOpenGL :: Dimension -> Graphic -> IO ()
+ FRP.UISF.Render.GLUT: setGraphics :: WindowData -> (Graphic, DirtyBit) -> IO ()
+ FRP.UISF.UISF: getDeltaTime :: UISF b DeltaT
+ FRP.UISF.UISF: instance [overlap ok] ArrowReader DeltaT UISF
+ FRP.UISF.UISF: instance [overlap ok] Show UIParams
+ FRP.UISF.UISF: uiBackground :: UIParams -> RGB
+ FRP.UISF.UISF: uiCloseOnEsc :: UIParams -> Bool
+ FRP.UISF.UISF: withCTX :: UISF (CTX, a) b -> UISF a b
+ FRP.UISF.UITypes: AdditionalButton :: Int -> MouseButton
+ FRP.UISF.UITypes: Button :: Point -> MouseButton -> Bool -> UIEvent
+ FRP.UISF.UITypes: Char :: Char -> Key
+ FRP.UISF.UITypes: Key :: Char -> [Key] -> Bool -> UIEvent
+ FRP.UISF.UITypes: KeyAltL :: SpecialKey
+ FRP.UISF.UITypes: KeyAltR :: SpecialKey
+ FRP.UISF.UITypes: KeyBackspace :: SpecialKey
+ FRP.UISF.UITypes: KeyBegin :: SpecialKey
+ FRP.UISF.UITypes: KeyCtrlL :: SpecialKey
+ FRP.UISF.UITypes: KeyCtrlR :: SpecialKey
+ FRP.UISF.UITypes: KeyDelete :: SpecialKey
+ FRP.UISF.UITypes: KeyDown :: SpecialKey
+ FRP.UISF.UITypes: KeyEnd :: SpecialKey
+ FRP.UISF.UITypes: KeyEnter :: SpecialKey
+ FRP.UISF.UITypes: KeyEsc :: SpecialKey
+ FRP.UISF.UITypes: KeyF1 :: SpecialKey
+ FRP.UISF.UITypes: KeyF10 :: SpecialKey
+ FRP.UISF.UITypes: KeyF11 :: SpecialKey
+ FRP.UISF.UITypes: KeyF12 :: SpecialKey
+ FRP.UISF.UITypes: KeyF2 :: SpecialKey
+ FRP.UISF.UITypes: KeyF3 :: SpecialKey
+ FRP.UISF.UITypes: KeyF4 :: SpecialKey
+ FRP.UISF.UITypes: KeyF5 :: SpecialKey
+ FRP.UISF.UITypes: KeyF6 :: SpecialKey
+ FRP.UISF.UITypes: KeyF7 :: SpecialKey
+ FRP.UISF.UITypes: KeyF8 :: SpecialKey
+ FRP.UISF.UITypes: KeyF9 :: SpecialKey
+ FRP.UISF.UITypes: KeyHome :: SpecialKey
+ FRP.UISF.UITypes: KeyInsert :: SpecialKey
+ FRP.UISF.UITypes: KeyLeft :: SpecialKey
+ FRP.UISF.UITypes: KeyNumLock :: SpecialKey
+ FRP.UISF.UITypes: KeyPageDown :: SpecialKey
+ FRP.UISF.UITypes: KeyPageUp :: SpecialKey
+ FRP.UISF.UITypes: KeyRight :: SpecialKey
+ FRP.UISF.UITypes: KeyShiftL :: SpecialKey
+ FRP.UISF.UITypes: KeyShiftR :: SpecialKey
+ FRP.UISF.UITypes: KeyTab :: SpecialKey
+ FRP.UISF.UITypes: KeyUnknown :: Int -> SpecialKey
+ FRP.UISF.UITypes: KeyUp :: SpecialKey
+ FRP.UISF.UITypes: LeftButton :: MouseButton
+ FRP.UISF.UITypes: MiddleButton :: MouseButton
+ FRP.UISF.UITypes: MouseButton :: MouseButton -> Key
+ FRP.UISF.UITypes: MouseMove :: Point -> UIEvent
+ FRP.UISF.UITypes: NoUIEvent :: UIEvent
+ FRP.UISF.UITypes: NullLayout :: Layout
+ FRP.UISF.UITypes: RightButton :: MouseButton
+ FRP.UISF.UITypes: SKey :: SpecialKey -> [Key] -> Bool -> UIEvent
+ FRP.UISF.UITypes: SpecialKey :: SpecialKey -> Key
+ FRP.UISF.UITypes: WheelDown :: MouseButton
+ FRP.UISF.UITypes: WheelUp :: MouseButton
+ FRP.UISF.UITypes: char :: UIEvent -> Char
+ FRP.UISF.UITypes: data Key
+ FRP.UISF.UITypes: data MouseButton
+ FRP.UISF.UITypes: data SpecialKey
+ FRP.UISF.UITypes: data UIEvent
+ FRP.UISF.UITypes: hMin :: Layout -> Int
+ FRP.UISF.UITypes: hStretch :: Layout -> Int
+ FRP.UISF.UITypes: hasAltModifier :: [Key] -> Bool
+ FRP.UISF.UITypes: hasCtrlModifier :: [Key] -> Bool
+ FRP.UISF.UITypes: hasShiftModifier :: [Key] -> Bool
+ FRP.UISF.UITypes: instance Eq Key
+ FRP.UISF.UITypes: instance Eq MouseButton
+ FRP.UISF.UITypes: instance Eq SpecialKey
+ FRP.UISF.UITypes: instance Eq UIEvent
+ FRP.UISF.UITypes: instance Ord Key
+ FRP.UISF.UITypes: instance Ord MouseButton
+ FRP.UISF.UITypes: instance Ord SpecialKey
+ FRP.UISF.UITypes: instance Show Key
+ FRP.UISF.UITypes: instance Show MouseButton
+ FRP.UISF.UITypes: instance Show SpecialKey
+ FRP.UISF.UITypes: instance Show UIEvent
+ FRP.UISF.UITypes: isDown :: UIEvent -> Bool
+ FRP.UISF.UITypes: isKeyPressed :: Key -> IO Bool
+ FRP.UISF.UITypes: lFill :: Layout -> Int
+ FRP.UISF.UITypes: mbutton :: UIEvent -> MouseButton
+ FRP.UISF.UITypes: modifiers :: UIEvent -> [Key]
+ FRP.UISF.UITypes: pt :: UIEvent -> Point
+ FRP.UISF.UITypes: skey :: UIEvent -> SpecialKey
+ FRP.UISF.UITypes: updateKeyState :: Key -> Bool -> IO [Key]
+ FRP.UISF.UITypes: wFixed :: Layout -> Int
+ FRP.UISF.UITypes: wMin :: Layout -> Int
+ FRP.UISF.UITypes: wStretch :: Layout -> Int
+ FRP.UISF.Widget: checkboxS :: UITexty s => s -> Bool -> UISF (SEvent Bool) Bool
+ FRP.UISF.Widget: displayField :: UITexty s => WrapSetting -> UISF s ()
+ FRP.UISF.Widget: hSliderS :: RealFrac a => (a, a) -> a -> UISF (SEvent a) a
+ FRP.UISF.Widget: hiSliderS :: Integral a => a -> (a, a) -> a -> UISF (SEvent a) a
+ FRP.UISF.Widget: listbox' :: (Eq a, Show a) => UISF ([a], Int) Int
+ FRP.UISF.Widget: radioS :: UITexty s => [s] -> Int -> UISF (SEvent Int) Int
+ FRP.UISF.Widget: scrollable :: Layout -> Dimension -> UISF a b -> UISF a b
+ FRP.UISF.Widget: spacer :: UISF a a
+ FRP.UISF.Widget: stickyButtonS :: UITexty s => s -> UISF (SEvent Bool) Bool
+ FRP.UISF.Widget: textField :: UITexty s => WrapSetting -> s -> UISF (SEvent s) UIText
+ FRP.UISF.Widget: textField' :: UITexty s => WrapSetting -> UISF s UIText
+ FRP.UISF.Widget: textbox' :: UITexty s => UISF s UIText
+ FRP.UISF.Widget: vSliderS :: RealFrac a => (a, a) -> a -> UISF (SEvent a) a
+ FRP.UISF.Widget: viSliderS :: Integral a => a -> (a, a) -> a -> UISF (SEvent a) a
+ FRP.UISF.Widget.Construction: (//) :: Graphic -> Graphic -> Graphic
+ FRP.UISF.Widget.Construction: bg :: Color
+ FRP.UISF.Widget.Construction: cyclebox :: Layout -> [(Rect -> Bool -> Graphic, b)] -> Int -> UISF () b
+ FRP.UISF.Widget.Construction: cycleboxS :: Layout -> [(Rect -> Bool -> Graphic, b)] -> Int -> UISF (SEvent Int) b
+ FRP.UISF.Widget.Construction: focusable :: UISF a b -> UISF a b
+ FRP.UISF.Widget.Construction: inside :: Point -> Rect -> Bool
+ FRP.UISF.Widget.Construction: isInFocus :: UISF () Bool
+ FRP.UISF.Widget.Construction: marked :: (Color, Color, Color, Color)
+ FRP.UISF.Widget.Construction: mkBasicWidget :: Layout -> (Rect -> Graphic) -> UISF a a
+ FRP.UISF.Widget.Construction: mkSlider :: Eq a => Bool -> (a -> Int -> Int) -> (Int -> Int -> a) -> (Int -> Int -> a -> a) -> a -> UISF (SEvent a) a
+ FRP.UISF.Widget.Construction: mkWidget :: s -> Layout -> (a -> s -> Rect -> UIEvent -> (b, s, DirtyBit)) -> (Rect -> Bool -> s -> Graphic) -> UISF a b
+ FRP.UISF.Widget.Construction: padding :: Int
+ FRP.UISF.Widget.Construction: popped :: (Color, Color, Color, Color)
+ FRP.UISF.Widget.Construction: pushed :: (Color, Color, Color, Color)
+ FRP.UISF.Widget.Construction: shadowBox :: (Color, Color, Color, Color) -> Rect -> Graphic
+ FRP.UISF.Widget.Construction: toggle :: Eq s => s -> Layout -> (Rect -> Bool -> s -> Graphic) -> UISF s Bool
+ FRP.UISF.Widget.Construction: whenG :: Bool -> Graphic -> Graphic
- FRP.UISF: button :: String -> UISF () Bool
+ FRP.UISF: button :: UITexty s => s -> UISF () Bool
- FRP.UISF: checkbox :: String -> Bool -> UISF () Bool
+ FRP.UISF: checkbox :: UITexty s => s -> Bool -> UISF () Bool
- FRP.UISF: label :: String -> UISF a a
+ FRP.UISF: label :: UITexty s => s -> UISF a a
- FRP.UISF: listbox :: (Eq a, Show a) => UISF ([a], Int) Int
+ FRP.UISF: listbox :: (Eq a, Show a) => [a] -> Int -> UISF (SEvent [a], SEvent Int) Int
- FRP.UISF: radio :: [String] -> Int -> UISF () Int
+ FRP.UISF: radio :: UITexty s => [s] -> Int -> UISF () Int
- FRP.UISF: realtimeGraph :: RealFrac a => Layout -> Time -> Color -> UISF [(a, Time)] ()
+ FRP.UISF: realtimeGraph :: RealFrac a => Layout -> DeltaT -> Color -> UISF [(a, Time)] ()
- FRP.UISF: stickyButton :: String -> UISF () Bool
+ FRP.UISF: stickyButton :: UITexty s => s -> UISF () Bool
- FRP.UISF: textbox :: UISF String String
+ FRP.UISF: textbox :: UITexty s => s -> UISF (SEvent s) String
- FRP.UISF: textboxE :: String -> UISF (SEvent String) String
+ FRP.UISF: textboxE :: UITexty s => s -> UISF (SEvent s) String
- FRP.UISF: title :: String -> UISF a b -> UISF a b
+ FRP.UISF: title :: UITexty s => s -> UISF a b -> UISF a b
- FRP.UISF: uiTickDelay :: UIParams -> Double
+ FRP.UISF: uiTickDelay :: UIParams -> DeltaT
- FRP.UISF.AuxFunctions: delay :: ArrowCircuit a => forall b. b -> a b b
+ FRP.UISF.AuxFunctions: delay :: ArrowCircuit a => b -> a b b
- FRP.UISF.AuxFunctions: eventBuffer :: (ArrowTime a, ArrowCircuit a) => a (BufferOperation b) (SEvent [b], Bool)
+ FRP.UISF.AuxFunctions: eventBuffer :: (ArrowReader DeltaT a, ArrowCircuit a) => a (BufferOperation b) (SEvent [b], Bool)
- FRP.UISF.AuxFunctions: eventBuffer' :: ArrowCircuit a => a (BufferOperation b, Time) (SEvent [b], Bool)
+ FRP.UISF.AuxFunctions: eventBuffer' :: ArrowCircuit a => a (BufferOperation b, DeltaT) (SEvent [b], Bool)
- FRP.UISF.AuxFunctions: fcdelay :: (ArrowTime a, ArrowCircuit a) => b -> DeltaT -> a b b
+ FRP.UISF.AuxFunctions: fcdelay :: (ArrowReader DeltaT a, ArrowCircuit a) => b -> DeltaT -> a b b
- FRP.UISF.AuxFunctions: fdelay :: (ArrowTime a, ArrowCircuit a) => DeltaT -> a (SEvent b) (SEvent b)
+ FRP.UISF.AuxFunctions: fdelay :: (ArrowReader DeltaT a, ArrowCircuit a) => DeltaT -> a (SEvent b) (SEvent b)
- FRP.UISF.AuxFunctions: genEvents :: (ArrowTime a, ArrowCircuit a) => [b] -> a DeltaT (SEvent b)
+ FRP.UISF.AuxFunctions: genEvents :: (ArrowReader DeltaT a, ArrowCircuit a) => [b] -> a DeltaT (SEvent b)
- FRP.UISF.AuxFunctions: timer :: (ArrowTime a, ArrowCircuit a) => a DeltaT (SEvent ())
+ FRP.UISF.AuxFunctions: timer :: (ArrowReader DeltaT a, ArrowCircuit a) => a DeltaT (SEvent ())
- FRP.UISF.AuxFunctions: vcdelay :: (ArrowTime a, ArrowCircuit a) => DeltaT -> b -> a (DeltaT, b) b
+ FRP.UISF.AuxFunctions: vcdelay :: (ArrowReader DeltaT a, ArrowCircuit a) => DeltaT -> b -> a (DeltaT, b) b
- FRP.UISF.AuxFunctions: vdelay :: (ArrowTime a, ArrowCircuit a) => a (DeltaT, SEvent b) (SEvent b)
+ FRP.UISF.AuxFunctions: vdelay :: (ArrowReader DeltaT a, ArrowCircuit a) => a (DeltaT, SEvent b) (SEvent b)
- FRP.UISF.UISF: UISF :: (Flow -> Layout) -> ((CTX, Focus, Time, UIEvent, b) -> IO (DirtyBit, Focus, Graphic, TerminationProc, c, UISF b c)) -> UISF b c
+ FRP.UISF.UISF: UISF :: (Flow -> Layout) -> ((CTX, Focus, DeltaT, UIEvent, b) -> IO (DirtyBit, Focus, Graphic, TerminationProc, c, UISF b c)) -> UISF b c
- FRP.UISF.UISF: mkUISF :: Layout -> ((CTX, Focus, Time, UIEvent, a) -> (DirtyBit, Focus, Graphic, TerminationProc, b)) -> UISF a b
+ FRP.UISF.UISF: mkUISF :: Layout -> ((CTX, Focus, DeltaT, UIEvent, a) -> (DirtyBit, Focus, Graphic, TerminationProc, b)) -> UISF a b
- FRP.UISF.UISF: uiTickDelay :: UIParams -> Double
+ FRP.UISF.UISF: uiTickDelay :: UIParams -> DeltaT
- FRP.UISF.UISF: uisfFun :: UISF b c -> (CTX, Focus, Time, UIEvent, b) -> IO (DirtyBit, Focus, Graphic, TerminationProc, c, UISF b c)
+ FRP.UISF.UISF: uisfFun :: UISF b c -> (CTX, Focus, DeltaT, UIEvent, b) -> IO (DirtyBit, Focus, Graphic, TerminationProc, c, UISF b c)
- FRP.UISF.UITypes: Layout :: Int -> Int -> Int -> Int -> Int -> Int -> Layout
+ FRP.UISF.UITypes: Layout :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> Layout
- FRP.UISF.Widget: button :: String -> UISF () Bool
+ FRP.UISF.Widget: button :: UITexty s => s -> UISF () Bool
- FRP.UISF.Widget: checkbox :: String -> Bool -> UISF () Bool
+ FRP.UISF.Widget: checkbox :: UITexty s => s -> Bool -> UISF () Bool
- FRP.UISF.Widget: label :: String -> UISF a a
+ FRP.UISF.Widget: label :: UITexty s => s -> UISF a a
- FRP.UISF.Widget: listbox :: (Eq a, Show a) => UISF ([a], Int) Int
+ FRP.UISF.Widget: listbox :: (Eq a, Show a) => [a] -> Int -> UISF (SEvent [a], SEvent Int) Int
- FRP.UISF.Widget: radio :: [String] -> Int -> UISF () Int
+ FRP.UISF.Widget: radio :: UITexty s => [s] -> Int -> UISF () Int
- FRP.UISF.Widget: realtimeGraph :: RealFrac a => Layout -> Time -> Color -> UISF [(a, Time)] ()
+ FRP.UISF.Widget: realtimeGraph :: RealFrac a => Layout -> DeltaT -> Color -> UISF [(a, Time)] ()
- FRP.UISF.Widget: stickyButton :: String -> UISF () Bool
+ FRP.UISF.Widget: stickyButton :: UITexty s => s -> UISF () Bool
- FRP.UISF.Widget: textbox :: UISF String String
+ FRP.UISF.Widget: textbox :: UITexty s => s -> UISF (SEvent s) String
- FRP.UISF.Widget: title :: String -> UISF a b -> UISF a b
+ FRP.UISF.Widget: title :: UITexty s => s -> UISF a b -> UISF a b

Files

FRP/UISF.hs view
@@ -3,17 +3,21 @@     UISF 
   , runUI'              -- :: UISF () () -> IO ()
   , runUI               -- :: UIParams -> UISF () () -> IO ()
-  , UIParams (..)       -- data UIParams = UIParams { ... }
-  , defaultUIParams     -- :: UIParams
+  , UIParams, defaultUIParams
+  , uiInitialize, uiClose, uiTitle, uiSize, uiInitFlow, uiTickDelay, uiCloseOnEsc, uiBackground
   , Dimension           -- type Dimension = (Int, Int)
     -- Widgets
   , label               -- :: String -> UISF a a
   , displayStr          -- :: UISF String ()
   , display             -- :: Show a => UISF a ()
   , withDisplay         -- :: Show b => UISF a b -> UISF a b
-  , textbox             -- :: UISF String String
+  , WrapSetting(..)     -- data WrapSetting = NoWrap | CharWrap | WordWrap
+  , displayField        -- :: WrapSetting -> UISF String ()
+  , textbox             -- :: String -> UISF (Event String) String
+  , textField           -- :: WrapSetting -> String -> UISF (Event String) String
   , textboxE            -- :: String -> UISF (Event String) String
   , title               -- :: String -> UISF a b -> UISF a b
+  , spacer              -- :: UISF a a
   , button              -- :: String -> UISF () Bool
   , stickyButton        -- :: String -> UISF () Bool
   , checkbox            -- :: String -> Bool -> UISF () Bool
@@ -22,10 +26,12 @@   , hSlider, vSlider    -- :: RealFrac a => (a, a) -> a -> UISF () a
   , hiSlider, viSlider  -- :: Integral a => a -> (a, a) -> a -> UISF () a
   , realtimeGraph       -- :: RealFrac a => Layout -> Time -> Color -> UISF [(a,Time)] ()
-  , Color (..)          -- data Color = Black | Blue | Green | Cyan | Red | Magenta | Yellow | White
+  , Color (..)          -- data Color = Black | Blue | Green | Cyan | Red | Magenta | Yellow | White ...
   , histogram           -- :: RealFrac a => Layout -> UISF (Event [a]) ()
   , histogramWithScale  -- :: RealFrac a => Layout -> UISF (SEvent [(a,String)]) ()
-  , listbox             -- :: (Eq a, Show a) => UISF ([a], Int) Int
+  , scrollable          -- :: Layout -> Dimension -> UISF a b -> UISF a b
+  , listbox             -- :: (Eq a, Show a) => [a] -> Int -> UISF (SEvent [a], SEvent Int) Int
+  , listbox'            -- :: (Eq a, Show a) => UISF ([a], Int) Int
   , canvas              -- :: Dimension -> UISF (Event Graphic) ()
   , canvas'             -- :: Layout -> (a -> Dimension -> Graphic) -> UISF (Event a) ()
   -- Widget Utilities
@@ -35,9 +41,14 @@   , setLayout           -- :: Layout -> UISF a b -> UISF a b
   , makeLayout          -- :: LayoutType -> LayoutType -> Layout
   , LayoutType (..)     -- data LayoutType = Stretchy { minSize :: Int } | Fixed { fixedSize :: Int }
+  , Layout              -- data Layout = Layout {..}
+  -- Time, effects, and asynchrony
   , getTime             -- :: UISF () Time
-  , asyncUISFV          -- :: NFData b => Double -> Double -> Automaton a b -> UISF a ([b], Bool)
-  , asyncUISFE          -- :: NFData b => Automaton a b -> UISF (SEvent a) (SEvent b)
+  , getDeltaTime        -- :: UISF () DeltaT
+  , ArrowIO(..)
+  , asyncVT
+  , asyncE
+  , asyncC
   , module FRP.UISF.AuxFunctions
   , module Control.Arrow
   ) where
@@ -45,7 +56,12 @@ import FRP.UISF.UITypes
 import FRP.UISF.UISF
 import FRP.UISF.Widget
-import FRP.UISF.SOE (Color (..))
+import FRP.UISF.Graphics (Color (..), WrapSetting(..),Dimension)
 
 import FRP.UISF.AuxFunctions
+import FRP.UISF.Asynchrony
 import Control.Arrow
+
+{-# DEPRECATED getTime "As of UISF-0.4.0.0, use accumTime instead, which is a little different but should work fine" #-}
+getTime :: UISF () Time
+getTime = accumTime
+ FRP/UISF/Asynchrony.hs view
@@ -0,0 +1,430 @@+-----------------------------------------------------------------------------
+-- |
+-- Module      :  FRP.UISF.Asynchrony
+-- Copyright   :  (c) Daniel Winograd-Cort 2015
+-- License     :  see the LICENSE file in the distribution
+--
+-- Maintainer  :  dwc@cs.yale.edu
+-- Stability   :  experimental
+--
+-- This module provides functionality to allow UISF to perform 
+-- asynchronous computations.
+
+{-# LANGUAGE Arrows, TupleSections, FlexibleContexts #-}
+
+module FRP.UISF.Asynchrony (
+    -- * ArrowIO
+    -- $arrowio
+    ArrowIO(..),
+    -- * Signal Function Asynchrony
+    -- $automaton
+    Automaton(..), 
+    statefulFunctionToAutomaton,
+    actionToIOAuto,
+    statefulActionToIOAuto,
+    pureAutoToIOAuto,
+    -- $asynchrony
+    asyncV, asyncVOn,
+    asyncVT, asyncVTOn,
+    asyncE, asyncEOn,
+    asyncEIO, asyncEIOOn,
+    asyncC, asyncCOn,
+    asyncCIO, asyncCIOOn
+) where
+
+import Prelude hiding ((.), id)
+import Control.Category
+import Control.Arrow
+import Control.Arrow.Operations
+import Control.Arrow.Transformer.Automaton
+
+import Data.Sequence (empty, (<|), (|>), (><), 
+                      viewl, ViewL(..), viewr, ViewR(..))
+import qualified Data.Sequence as Seq
+
+import Control.Concurrent
+import Control.Exception (bracket)
+import Data.IORef
+import Data.Foldable (toList)
+import Control.DeepSeq
+
+import FRP.UISF.AuxFunctions (DeltaT, Time, SEvent, accumTime)
+
+{- $arrowio
+Programming with UISF is not pure functional reactive programming.  
+Indeed, the GUI nature demands a certain amount of effectful computation 
+to really allow for an interface at all.  That said, widgets allow only 
+very specific effects, and they are handled in such a way that the 
+programming feels functional and reactive.
+
+To allow for more generic needs, we introduce the ArrowIO class below, 
+which UISF instantiates.  Note that using the ArrowIO functions should 
+be done very carefully; if a lifted IO action blocks within the UISF, 
+it will very likely block the entire GUI.  Thus, when possible, one is 
+advised to use 'initialAIO' or 'terminalAIO', which are guaranteed to 
+perform their 'IO' action only once.
+-}
+
+-- | Instances of the ArrowIO class have an arrowized ability to 
+--   perform IO actions.
+class Arrow a => ArrowIO a where
+  -- | The liftAIO function lifts an IO action into an arrow.
+  liftAIO :: (b -> IO c) -> a b c
+  -- | The initialAIO function performs an IO action once when the 
+  --   arrow is first initialized and then uses the result of that 
+  --   action to generate the arrow itself.
+  initialAIO :: IO d -> (d -> a b c) -> a b c
+  -- | The terminalAIO function stores an IO action to be performed 
+  --   once when the arrow terminates.  This will typically be some 
+  --   sort of clean up behavior.
+  terminalAIO :: IO () -> a b b
+
+
+{- $automaton
+The functions we want to perform asynchronously may be pure, but it 
+is quite possible that they are somewhat more complex.  Thus, in 
+keeping with the general arrowized nature of UISF, we allow the 
+asynchronized functions to be Automatons.
+
+Use the 'Arrow' 'arr' function to convert a pure function to an 
+Automaton of any type.
+-}
+
+-- | Pure automatons from b to c are functions that take a b value 
+--  and return a c value along with a new pure automaton from b to c.
+type PureAuto = Automaton (->)
+
+-- | IO automatons from b to c are functions that take a b value and 
+--  perform an IO action that returns a c value along with a new IO 
+--  automaton from b to c.
+type IOAuto = Automaton (Kleisli IO)
+
+-- | A convenience function for converting stateful functions to 
+--  'Automaton's.
+statefulFunctionToAutomaton :: ArrowLoop a => s -> ((b,s) -> (c,s)) -> Automaton a b c
+statefulFunctionToAutomaton s f = loop $ second (delay s) >>> arr f
+
+-- | A convenience function for converting IO actions to 'IOAuto's.
+actionToIOAuto :: (b -> IO c) -> IOAuto b c
+actionToIOAuto f = Automaton (Kleisli g) where
+  g b = f b >>= (\c -> return (c, Automaton (Kleisli g)))
+
+-- | A convenience function for converting stateful IO actions to 
+--  'IOAuto's.
+statefulActionToIOAuto :: s -> ((b,s) -> IO (c,s)) -> IOAuto b c
+statefulActionToIOAuto s f = loop $ second (delay s) >>> actionToIOAuto f
+
+-- | A convenience function for lifting a 'PureAuto' into an 'IOAuto'.
+pureAutoToIOAuto :: PureAuto b c -> IOAuto b c
+pureAutoToIOAuto (Automaton f) = Automaton (Kleisli g) where
+  g b = let (c,f') = f b in return (c, pureAutoToIOAuto f')
+
+
+{- $asynchrony
+There are times when we want to perform some behavior during the 
+course of running an arrow, but that behavior doesn't temporally 
+align nicely with the main GUI.  For example:
+
+- We have an FRP program that needs to be run at a fixed time rate, 
+  with each tick through time well specified.
+
+- We have a costly computation or potentially blocking action whose 
+  result is not immediately 
+  relevant, and we do not want to sacrifice the GUI's response time 
+  by making it part of the main loop.  That is, we are okay with it 
+  taking multiple ticks to finish, but we don't want to slow down 
+  any current ticks.
+
+- We want to perform an action in a private, tight loop.  For instance, 
+  we need to poll a device, and we do not want the GUI's framerate tied 
+  to the polling frequency in any way.
+
+The following functions implement these different forms of asynchrony.
+-}
+
+
+
+-- | The asyncV functions is for \"Virtual time\" asynchrony.  The 
+--   embedded signal function is given along with an expected 
+--   clockrate, and the output conforms to that clockrate as well as it 
+--   can.
+--   
+--   The clockrate is the simulated rate of the input signal function.
+--   The buffer is the amount of time the given signal function is 
+--   allowed to get ahead of real time.
+--
+--   The output signal function takes and returns values in real time.  
+--   The input must be paired with time, and the return values are the 
+--   list of bs generated in the given time step, each time stamped.  
+--   Note that the returned list may be long if the clockrate is much 
+--   faster than real time and potentially empty if it's slower.
+--   Note also that the caller can check the time stamp on the element 
+--   at the end of the list to see if the inner, \"simulated\" signal 
+--   function is performing as fast as it should.
+asyncV :: (ArrowIO a, NFData c) => 
+          Double            -- ^ Clockrate
+       -> DeltaT            -- ^ Amount of time to buffer
+       -> PureAuto b c      -- ^ The automaton to run virtually
+       -> a (b, Time) [(c, Time)]
+asyncV = asyncVHelper forkIO
+
+-- | A variant of 'asyncV' that uses 'forkOn' internally and thus takes 
+--  a core ID to fork on.
+asyncVOn :: (ArrowIO a, NFData c) => 
+            Int             -- ^ Core to fork on
+         -> Double          -- ^ Clockrate
+         -> DeltaT          -- ^ Amount of time to buffer
+         -> PureAuto b c    -- ^ The automaton to run virtually
+         -> a (b, Time) [(c, Time)]
+asyncVOn = asyncVHelper . forkOn
+
+asyncVHelper :: (ArrowIO a, NFData c) => 
+            (IO () -> IO ThreadId) -- ^ fork
+         -> Double          -- ^ Clockrate
+         -> DeltaT          -- ^ Amount of time to buffer
+         -> PureAuto b c    -- ^ The automaton to convert to realtime
+         -> a (b, Time) [(c, Time)]
+asyncVHelper frk clockrate buffer sf = initialAIO iod darr where
+  iod = do
+    inp <- newIORef undefined
+    start <- newEmptyMVar
+    out <- newIORef empty
+    timevar <- newEmptyMVar
+    tid <- frk $ takeMVar start >> worker inp out timevar 1 1 sf
+    return (tid, inp, out, timevar, start)
+  darr (tid, inp, out, timevar, start) = proc (b,t) -> do
+    _ <- terminalAIO (killThread tid) -< ()
+    _ <- liftAIO (writeIORef inp) -< b -- send the worker the new input
+    _ <- initialAIO (putMVar start ()) (const $ liftAIO $ tryPutMVar timevar) -< t  -- update the time for the worker
+    c <- liftAIO (atomicModifyIORef out) -< Seq.spanl (\(_,t0) -> t >= t0) --collect ready results
+    returnA -< toList c
+  -- worker processes the inner, "simulated" signal function.
+  worker inp out timevar t count (Automaton sf) = do
+    b <- readIORef inp     -- get the latest input
+    let (c, sf') = sf b   -- do the calculation
+    s <- deepseq c $ atomicModifyIORef out (\s -> (s |> (c, fromIntegral count/clockrate), s))
+    t' <- if Seq.length s > 0 && snd (seqLastElem s) >= t+buffer then takeMVar timevar else return t
+    worker inp out timevar t' (count+1) sf'
+  seqLastElem s = Seq.index s (Seq.length s - 1)
+
+
+-- | A variant of 'asyncV' that uses a built-in time step of the arrow 
+--  to accumulate and use the current time.
+asyncVT :: (ArrowIO a, ArrowCircuit a, ArrowReader DeltaT a, NFData c) => 
+           Double            -- ^ Clockrate
+        -> DeltaT            -- ^ Amount of time to buffer
+        -> PureAuto b c      -- ^ The automaton to run virtually
+        -> a b [(c, Time)]
+asyncVT clockrate buffer sf = (id &&& accumTime) >>> asyncV clockrate buffer sf
+
+-- | A variant of 'asyncVT' that uses 'forkOn' internally and thus takes 
+--  a core ID to fork on.
+asyncVTOn :: (ArrowIO a, ArrowCircuit a, ArrowReader DeltaT a, NFData c) => 
+             Int             -- ^ Core to fork on
+          -> Double          -- ^ Clockrate
+          -> DeltaT            -- ^ Amount of time to buffer
+          -> PureAuto b c      -- ^ The automaton to run virtually
+          -> a b [(c, Time)]
+asyncVTOn i clockrate buffer sf = (id &&& accumTime) >>> asyncVOn i clockrate buffer sf
+
+
+
+-- | The asyncE (E for \"Event\") function takes a signal function (an Automaton) and converts 
+--   it into an asynchronous event-based signal function usable in a ArrowIO signal 
+--   function context.  The output arrow takes events of type a, feeds them to 
+--   the asynchronously running input signal function, and returns events with the output 
+--   b whenever they are ready.  The input signal function is expected to run slowly 
+--   compared to the output one, but it is capable of running just as fast.
+asyncE :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => 
+          PureAuto b c  -- ^ The automaton to convert to asynchronize
+       -> a (SEvent b) (SEvent c)
+asyncE = asyncEHelper forkIO
+
+-- | A variant of 'asyncE' that uses 'forkOn' internally and thus takes 
+--  a core ID to fork on.
+asyncEOn :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => 
+            Int             -- ^ Core to fork on
+         -> PureAuto b c    -- ^ The automaton to asynchronize
+         -> a (SEvent b) (SEvent c)
+asyncEOn = asyncEHelper . forkOn
+
+asyncEHelper :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => 
+          (IO () -> IO ThreadId)    -- ^ fork
+       -> PureAuto b c              -- ^ The automaton to asynchronize
+       -> a (SEvent b) (SEvent c)
+asyncEHelper frk sf = initialAIO iod darr where
+  iod = do
+    inp <- newIORef empty
+    out <- newIORef empty
+    proceed <- newEmptyMVar
+    tid <- frk $ worker proceed inp out sf
+    return (tid, proceed, inp, out)
+  -- count should start at 0
+  darr (tid, proceed, inp, out) = proc eb -> do
+    _ <- terminalAIO (killThread tid) -< ()
+    case eb of
+      Just b -> 
+        liftAIO (\b -> atomicModifyIORef inp (\s -> (s |> b, ())) >> tryPutMVar proceed ()) -< b
+      Nothing -> returnA -< False
+    c <- liftAIO (const $ atomicModifyIORef out seqRestHead) -< ()
+    returnA -< c
+  -- worker processes the inner, "simulated" signal function.
+  -- worker :: MVar () -> IORef (Seq a) -> IORef (Seq b) -> Automaton a b -> IO ()
+  worker proceed inp out (Automaton sf) = do
+    eb <- atomicModifyIORef inp seqRestHead
+    case eb of
+      Nothing -> takeMVar proceed >> worker proceed inp out (Automaton sf)
+      Just b -> do
+        let (c, sf') = sf b     -- do the calculation
+        deepseq c $ atomicModifyIORef out (\s -> (s |> c, ()))
+        worker proceed inp out sf'
+  seqRestHead s = case viewl s of
+      EmptyL  -> (s,  Nothing)
+      a :< s' -> (s', Just a)
+
+
+-- | A variant of 'asyncE' that takes an 'IOAuto' and can thus perform 
+--  'IO' actions.
+asyncEIO :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => 
+          (IO d, d -> IO ())    -- ^ Initialization and termination procedures
+       -> (d -> IOAuto b c)     -- ^ The automaton to asynchronize
+       -> a (SEvent b) (SEvent c)
+asyncEIO = asyncEIOHelper forkIO
+
+-- | A variant of 'asyncEIO' that uses 'forkOn' internally and thus takes 
+--  a core ID to fork on.
+asyncEIOOn :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => 
+              Int                   -- ^ Core to fork on
+           -> (IO d, d -> IO ())    -- ^ Initialization and termination procedures
+           -> (d -> IOAuto b c)     -- ^ The automaton to asynchronize
+           -> a (SEvent b) (SEvent c)
+asyncEIOOn = asyncEIOHelper . forkOn
+
+asyncEIOHelper :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => 
+          (IO () -> IO ThreadId)    -- ^ fork
+       -> (IO d, d -> IO ())        -- ^ Initialization and termination procedures
+       -> (d -> IOAuto b c)         -- ^ The automaton to asynchronize
+       -> a (SEvent b) (SEvent c)
+asyncEIOHelper frk (ini, term) sf = initialAIO iod darr where
+  iod = do
+    inp <- newIORef empty
+    out <- newIORef empty
+    proceed <- newEmptyMVar
+    tid <- frk $ bracket ini term $ worker proceed inp out . sf
+    return (tid, proceed, inp, out)
+  -- count should start at 0
+  darr (tid, proceed, inp, out) = proc eb -> do
+    _ <- terminalAIO (killThread tid) -< ()
+    case eb of
+      Just b -> 
+        liftAIO (\b -> atomicModifyIORef inp (\s -> (s |> b, ())) >> tryPutMVar proceed ()) -< b
+      Nothing -> returnA -< False
+    c <- liftAIO (const $ atomicModifyIORef out seqRestHead) -< ()
+    returnA -< c
+  -- worker processes the inner, "simulated" signal function.
+  -- worker :: MVar () -> IORef (Seq a) -> IORef (Seq b) -> Automaton a b -> IO ()
+  worker proceed inp out (Automaton sf) = do
+    eb <- atomicModifyIORef inp seqRestHead
+    case eb of
+      Nothing -> takeMVar proceed >> worker proceed inp out (Automaton sf)
+      Just b -> do
+        (c, sf') <- runKleisli sf $ b     -- do the action
+        deepseq c $ atomicModifyIORef out (\s -> (s |> c, ()))
+        worker proceed inp out sf'
+  seqRestHead s = case viewl s of
+      EmptyL  -> (s,  Nothing)
+      a :< s' -> (s', Just a)
+
+
+
+-- | The asyncC (C for \"Continuous time\") function allows a continuous 
+--   signal function to run as fast as it can asynchronously.  There are 
+--   no guarantees that all input data make it to the asynchronous signal 
+--   function; if this is required, asyncE should be used instead.  
+--   Rather, the embedded signal function runs as fast as it can on 
+--   whatever value it has most recently seen.  Its results are 
+--   bundled together in a list to be returned to the main signal 
+--   function.
+asyncC :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => 
+          PureAuto b c  -- ^ The automaton to convert to realtime
+       -> a b [c]
+asyncC = asyncCHelper forkIO
+
+-- | A variant of 'asyncC' that uses 'forkOn' internally and thus takes 
+--  a core ID to fork on.
+asyncCOn :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => 
+            Int           -- ^ Core to fork on
+         -> PureAuto b c  -- ^ The automaton to convert to realtime
+         -> a b [c]
+asyncCOn = asyncCHelper . forkOn
+
+asyncCHelper :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => 
+            (IO () -> IO ThreadId)  -- ^ fork
+         -> PureAuto b c            -- ^ The automaton to convert to realtime
+         -> a b [c]
+asyncCHelper frk sf = initialAIO iod darr where
+  iod = do
+    inp <- newIORef undefined
+    start <- newEmptyMVar
+    out <- newIORef empty
+    tid <- frk $ takeMVar start >> worker inp out sf
+    return (tid, inp, out, start)
+  darr (tid, inp, out, start) = proc b -> do
+    _ <- terminalAIO (killThread tid) -< ()
+    _ <- liftAIO (writeIORef inp) -< b -- send the worker the new input
+    c <- initialAIO (putMVar start ()) (const $ liftAIO (\_ -> atomicModifyIORef out (\s -> (empty,s)))) -< () --collect ready results
+    returnA -< toList c
+  -- worker processes the inner, "simulated" signal function.
+  worker inp out (Automaton sf) = do
+    b <- readIORef inp    -- get the latest input
+    let (c, sf') = sf b   -- do the calculation
+    deepseq c $ atomicModifyIORef out (\s -> (s |> c, ()))
+    worker inp out sf'
+
+
+
+
+
+
+-- | A variant of 'asyncC' that takes an 'IOAuto' and can thus perform 
+--  'IO' actions.
+asyncCIO :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => 
+          (IO d, d -> IO ())    -- ^ Initialization and termination procedures
+       -> (d -> IOAuto b c)     -- ^ The automaton to convert to realtime
+       -> a b [c]
+asyncCIO = asyncCIOHelper forkIO
+
+-- | A variant of 'asyncCIO' that uses 'forkOn' internally and thus takes 
+--  a core ID to fork on.
+asyncCIOOn :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => 
+              Int                   -- ^ Core to fork on
+           -> (IO d, d -> IO ())    -- ^ Initialization and termination procedures
+           -> (d -> IOAuto b c)     -- ^ The automaton to convert to realtime
+           -> a b [c]
+asyncCIOOn = asyncCIOHelper . forkOn
+
+asyncCIOHelper :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => 
+          (IO () -> IO ThreadId)    -- ^ fork
+       -> (IO d, d -> IO ())        -- ^ Initialization and termination procedures
+       -> (d -> IOAuto b c)         -- ^ The automaton to convert to realtime
+       -> a b [c]
+asyncCIOHelper frk (ini, term) sf = initialAIO iod darr where
+  iod = do
+    inp <- newIORef undefined
+    start <- newEmptyMVar
+    out <- newIORef empty
+    tid <- frk $ takeMVar start >> bracket ini term (worker inp out . sf)
+    return (tid, inp, out, start)
+  darr (tid, inp, out, start) = proc b -> do
+    _ <- terminalAIO (killThread tid) -< ()
+    _ <- liftAIO (writeIORef inp) -< b -- send the worker the new input
+    c <- initialAIO (putMVar start ()) (const $ liftAIO (\_ -> atomicModifyIORef out (\s -> (empty,s)))) -< () --collect ready results
+    returnA -< toList c
+  -- worker processes the inner, "simulated" signal function.
+  worker inp out (Automaton sf) = do
+    b <- readIORef inp    -- get the latest input
+    (c, sf') <- runKleisli sf $ b   -- do the action
+    deepseq c $ atomicModifyIORef out (\s -> (s |> c, ()))
+    worker inp out sf'
+
+
FRP/UISF/AuxFunctions.hs view
@@ -9,13 +9,12 @@ --
 -- Auxiliary functions for use with UISF or other arrows.
 
-{-# LANGUAGE Arrows, ScopedTypeVariables, TupleSections #-}
+{-# LANGUAGE Arrows, TupleSections, FlexibleContexts #-}
 
 module FRP.UISF.AuxFunctions (
     -- * Types
     SEvent, Time, DeltaT, 
-    ArrowTime(..), 
-    ArrowIO(..),
+    getDeltaT, accumTime, 
     -- * Useful SF Utilities (Mediators)
     constA, constSF, 
     edge, 
@@ -25,8 +24,7 @@     concatA, runDynamic, foldA, foldSF, 
     maybeA, evMap, 
     -- * Delays and Timers
-    delay, 
-    -- | delay is a unit delay.  It is exactly the delay from ArrowCircuit.
+    ArrowCircuit(..), 
     vdelay, fdelay, 
     vcdelay, fcdelay, 
     timer, genEvents, 
@@ -35,27 +33,17 @@     
 --    (=>>), (->>), (.|.),
 --    snapshot, snapshot_,
-
-    -- * Signal Function Asynchrony
-    -- $asynchrony
-    Automaton(..), 
-    asyncV, asyncE, asyncC, asyncC'
 ) where
 
 import Prelude hiding ((.), id)
 import Control.Category
 import Control.Arrow
 import Control.Arrow.Operations
-import Control.Arrow.Transformer.Automaton
 import Data.Sequence (empty, (<|), (|>), (><), 
                       viewl, ViewL(..), viewr, ViewR(..))
 import qualified Data.Sequence as Seq
 import Data.Maybe (listToMaybe)
 
-import Control.Concurrent
-import Data.IORef
-import Data.Foldable (toList)
-import Control.DeepSeq
 
 
 --------------------------------------
@@ -71,21 +59,14 @@ -- | DeltaT is a type synonym referring to a change in Time.
 type DeltaT = Double
 
--- | Instances of this class have arrowized access to time.  This is 
---   convenient in many cases where time is necessary but we would 
---   prefer not to make it an explicit argument.
-class ArrowTime a where
-    time :: a () Time
+-- | This is a convenience function for any DeltaT ArrowReader
+getDeltaT :: ArrowReader DeltaT a => a b DeltaT
+getDeltaT = readState
 
--- | Instances of the ArrowIO class have an arrowized ability to 
---   perform IO actions.
-class Arrow a => ArrowIO a where
-  -- | The liftAIO function lifts an IO action into an arrow.
-  liftAIO :: (b -> IO c) -> a b c
-  -- | The initialAIO function performs an IO action once upon the 
-  --   initialization of the arrow and then uses the result of that 
-  --   action to generate the arrow itself.
-  initialAIO :: IO d -> (d -> a b c) -> a b c
+-- | This function returns the accumulated delta times created by 
+--  getDeltaT.  Thus, it is the "accumulated" time.
+accumTime :: (ArrowCircuit a, ArrowReader DeltaT a) => a b Time
+accumTime = getDeltaT >>> arr (Just . (+)) >>> accum 0
 
 --------------------------------------
 -- Useful SF Utilities (Mediators)
@@ -112,8 +93,9 @@ --   to get the next value, and so on.
 accum :: ArrowCircuit a => b -> a (SEvent (b -> b)) b
 accum x = proc f -> do
-    rec b <- delay x -< maybe b ($b) f
-    returnA -< b
+    rec b <- delay x -< b'
+        let b' = maybe b ($b) f
+    returnA -< b'
 
 -- | The signal function unique will produce an event each time its input 
 --   signal changes.
@@ -127,13 +109,14 @@ --   happens, at which point it changes to the value attached to that 
 --   event, which it then holds until the next event, and so on.
 hold :: ArrowCircuit a => b -> a (SEvent b) b
-hold x = arr (fmap (const $)) >>> accum x
+hold x = arr (fmap const) >>> accum x
 
 -- | Now is a signal function that produces one event and then forever 
 --   after produces nothing.  It is essentially an impulse function.
 now :: ArrowCircuit a => a () (SEvent ())
-now = arr (const Nothing) >>> delay (Just ())
+now = constA Nothing >>> delay (Just ())
 
+{-# DEPRECATED mergeE "As of UISF-0.4.0.0, mergeE is being removed as it's basically just mappend from Monoid." #-}
 -- | mergeE merges two events with the given resolution function.
 mergeE :: (a -> a -> a) -> SEvent a -> SEvent a -> SEvent a
 mergeE _       Nothing     Nothing     = Nothing
@@ -141,6 +124,7 @@ mergeE _       Nothing     re@(Just _) = re
 mergeE resolve (Just l)    (Just r)    = Just (resolve l r)
 
+{-# DEPRECATED (~++) "As of UISF-0.4.0.0, (~++) is being removed as it is equivalent to Monoid's mappend." #-}
 -- | This is an infix specialization of 'mergeE' to lists.
 (~++) :: SEvent [a] -> SEvent [a] -> SEvent [a]
 (~++) = mergeE (++)
@@ -218,9 +202,9 @@ --   if events are too densely packed in the signal (compared to the 
 --   clock rate of the underlying arrow), then some events may be 
 --   over delayed.
-fdelay :: (ArrowTime a, ArrowCircuit a) => DeltaT -> a (SEvent b) (SEvent b)
+fdelay :: (ArrowReader DeltaT a, ArrowCircuit a) => DeltaT -> a (SEvent b) (SEvent b)
 fdelay d = proc e -> do
-    t <- time -< ()
+    t <- accumTime -< ()
     rec q <- delay empty -< maybe q' (\e' -> q' |> (t+d,e')) e
         let (ret, q') = case viewl q of
                 EmptyL -> (Nothing, q)
@@ -234,9 +218,9 @@ --   same as the order of events out and that no event will be skipped.  
 --   If the events are too dense or the delay argument drops too quickly, 
 --   some events may be over delayed.
-vdelay :: (ArrowTime a, ArrowCircuit a) => a (DeltaT, SEvent b) (SEvent b)
+vdelay :: (ArrowReader DeltaT a, ArrowCircuit a) => a (DeltaT, SEvent b) (SEvent b)
 vdelay = proc (d, e) -> do
-    t <- time -< ()
+    t <- accumTime -< ()
     rec q <- delay empty -< maybe q' (\e' -> q' |> (t,e')) e
         let (ret, q') = case viewl q of 
                 EmptyL -> (Nothing, q)
@@ -248,9 +232,9 @@ --   be accurate, but some data may be ommitted entirely.  As such, it is 
 --   not advisable to use fcdelay for event streams where every event must 
 --   be processed (that's what fdelay is for).
-fcdelay :: (ArrowTime a, ArrowCircuit a) => b -> DeltaT -> a b b
+fcdelay :: (ArrowReader DeltaT a, ArrowCircuit a) => b -> DeltaT -> a b b
 fcdelay i dt = proc v -> do
-    t <- time -< ()
+    t <- accumTime -< ()
     rec q <- delay empty -< q' |> (t+dt, v) -- this list has pairs of (emission time, value)
         let (ready, rest) = Seq.spanl ((<= t) . fst) q
             (ret, q') = case viewr ready of
@@ -273,9 +257,9 @@ --   delay amount variably changes, values are moved back and forth between 
 --   these two sequences as necessary.
 --   This should provide a slight performance boost.
-vcdelay :: (ArrowTime a, ArrowCircuit a) => DeltaT -> b -> a (DeltaT, b) b
+vcdelay :: (ArrowReader DeltaT a, ArrowCircuit a) => DeltaT -> b -> a (DeltaT, b) b
 vcdelay maxDT i = proc (dt, v) -> do
-    t <- time -< ()
+    t <- accumTime -< ()
     rec (qlow, qhigh) <- delay (empty,empty) -< 
                 (dropMostWhileL ((< t-maxDT) . fst) qlow', qhigh' |> (t, v))
                     -- this is two lists with pairs of (initial time, value)
@@ -307,9 +291,9 @@ --   fast enough compared to the timer frequency, this should give accurate and 
 --   predictable output and stay synchronized with any other timer and with 
 --   time itself.
-timer :: (ArrowTime a, ArrowCircuit a) => a DeltaT (SEvent ())
+timer :: (ArrowReader DeltaT a, ArrowCircuit a) => a DeltaT (SEvent ())
 timer = proc dt -> do
-    now <- time -< ()
+    now <- accumTime -< ()
     rec last <- delay 0 -< t'
         let ret = now >= last + dt
             t'  = latestEventTime last dt now
@@ -325,7 +309,7 @@ -- | genEvents is a timer that instead of returning unit events 
 --   returns the next element of the input list.  When the input 
 --   list is empty, the output stream becomes all Nothing.
-genEvents :: (ArrowTime a, ArrowCircuit a) => [b] -> a DeltaT (SEvent b)
+genEvents :: (ArrowReader DeltaT a, ArrowCircuit a) => [b] -> a DeltaT (SEvent b)
 genEvents lst = proc dt -> do
     e <- timer -< dt
     rec l <- delay lst -< maybe l (const $ drop 1 l) e
@@ -361,22 +345,21 @@ --   at the same timestep. In addition to any events emitted, a 
 --   streaming Bool is emitted that is True if the buffer is empty and 
 --   False if the buffer is full (meaning that events will still come).
-eventBuffer :: (ArrowTime a, ArrowCircuit a) => a (BufferOperation b) (SEvent [b], Bool)
-eventBuffer = arr (,()) >>> second time >>> eventBuffer'
+eventBuffer :: (ArrowReader DeltaT a, ArrowCircuit a) => a (BufferOperation b) (SEvent [b], Bool)
+eventBuffer = arr (,()) >>> second getDeltaT >>> eventBuffer'
 
 -- | eventBuffer' is a version that takes Time explicitly rather than 
 --   with ArrowTime.
-eventBuffer' :: ArrowCircuit a => a (BufferOperation b, Time) (SEvent [b], Bool)
-eventBuffer' = proc (bo', t) -> do
+eventBuffer' :: ArrowCircuit a => a (BufferOperation b, DeltaT) (SEvent [b], Bool)
+eventBuffer' = proc (bo', dt) -> do
     let (bo, doPlay', tempo') = collapseBO bo'
     doPlay <- hold True -< doPlay'
     tempo <- hold 1 -< tempo'
-    rec tprev  <- delay 0    -< t   --used to calculate dt, the change in time
-        buffer <- delay []   -< buffer' --the buffer
-        let dt = tempo * (t-tprev) --dt will never be negative
+    rec buffer <- delay []   -< buffer' --the buffer
+        let bufdt = tempo * dt
             (nextMsgs, buffer') = if doPlay 
                 -- Subtract delta time, update the buffer, and get any events that are ready
-                then getNextEvent (update (subTime buffer dt) bo)
+                then getNextEvent (update (subTime buffer bufdt) bo)
                 -- Regardless, update the buffer based on the operation
                 else (Nothing, update buffer bo)
     returnA -< (nextMsgs, null buffer')
@@ -432,174 +415,4 @@ snapshot_ :: SEvent a -> b -> SEvent b
 snapshot_ = flip $ fmap . const -- same as ->>
 
-
-
---------------------------------------
--- Signal Function Asynchrony
---------------------------------------
-
-{- $asynchrony
-Due to the ability for ArrowIO arrows to perform IO actions, they are 
-obviously not guaranteed to be pure, and thus when we run them, we say 
-that they run \"in real time\".  This means that the time between two 
-samples can vary and is inherently unpredictable.
-
-However, there are cases when we would like more control over the timing 
-of certain arrowized computations.  For instance, sometimes we have a 
-pure computation that we would like to run on a simulated clock.  This 
-computation will expect to produce values at specific intervals, and 
-because it's pure, that expectation can sort of be satisfied.
-
-To achieve this, we allow these sub-computations to be performed 
-asynchronously.  The following functions behave subtly differently 
-to exhibit different forms of asynchrony for different use cases.
--}
-
--- | The asyncV functions is for \"Virtual time\" asynchrony.  The 
---   embedded signal function is given along with an expected 
---   clockrate, and the output conforms to that clockrate as well as it 
---   can.
---   
---   The clockrate is the simulated rate of the input signal function.
---   The buffer is the amount of time the given signal function is 
---   allowed to get ahead of real time.  The threadHandler is where the 
---   ThreadId of the forked thread is sent.
---
---   The output signal function takes and returns values in real time.  
---   The input must be paired with time, and the return values are the 
---   list of bs generated in the given time step, each time stamped.  
---   Note that the returned list may be long if the clockrate is much 
---   faster than real time and potentially empty if it's slower.
---   Note also that the caller can check the time stamp on the element 
---   at the end of the list to see if the inner, \"simulated\" signal 
---   function is performing as fast as it should.
-asyncV :: (ArrowIO a, NFData c) => 
-          Double             -- ^ Clockrate
-       -> DeltaT             -- ^ Amount of time to buffer
-       -> (ThreadId -> a () ()) -- ^ The thread handler
-       -> (Automaton (->) b c)      -- ^ The automaton to convert to realtime
-       -> a (b, Time) [(c, Time)]
-asyncV clockrate buffer threadHandler sf = initialAIO iod darr where
-  iod = do
-    inp <- newEmptyMVar
-    out <- newIORef empty
-    timevar <- newEmptyMVar
-    tid <- forkIO $ worker inp out timevar 1 1 sf
-    return (tid, inp, out, timevar)
-  darr (tid, inp, out, timevar) = proc (b,t) -> do
-    _ <- threadHandler tid -< ()
-    _ <- liftAIO (\b -> tryTakeMVar inp >> putMVar inp b) -< b -- send the worker the new input
-    _ <- liftAIO (tryPutMVar timevar) -< t  -- update the time for the worker
-    c <- liftAIO (atomicModifyIORef out) -< Seq.spanl (\(_,t0) -> t >= t0) --collect ready results
-    returnA -< toList c
-  -- worker processes the inner, "simulated" signal function.
-  worker inp out timevar t count (Automaton sf) = do
-    b <- readMVar inp     -- get the latest input
-    let (c, sf') = sf b   -- do the calculation
-    s <- deepseq c $ atomicModifyIORef out (\s -> (s |> (c, fromIntegral count/clockrate), s))
-    t' <- if Seq.length s > 0 && snd (seqLastElem s) >= t+buffer then takeMVar timevar else return t
-    worker inp out timevar t' (count+1) sf'
-  seqLastElem s = Seq.index s (Seq.length s - 1)
-
-
-
--- | The asyncE (E for \"Event\") function takes a signal function (an Automaton) and converts 
---   it into an asynchronous event-based signal function usable in a ArrowIO signal 
---   function context.  The output arrow takes events of type a, feeds them to 
---   the asynchronously running input signal function, and returns events with the output 
---   b whenever they are ready.  The input signal function is expected to run slowly 
---   compared to the output one, but it is capable of running just as fast.
-asyncE :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData c) => 
-          (ThreadId -> a () ()) -- ^ The thread handler
-       -> (Automaton (->) b c)  -- ^ The automaton to convert to asynchronize
-       -> a (SEvent b) (SEvent c)
-asyncE threadHandler sf = initialAIO iod darr where
-  iod = do
-    inp <- newIORef empty
-    out <- newIORef empty
-    proceed <- newEmptyMVar
-    tid <- forkIO $ worker proceed inp out sf
-    return (tid, proceed, inp, out)
-  -- count should start at 0
-  darr (tid, proceed, inp, out) = proc eb -> do
-    _ <- threadHandler tid -< ()
-    case eb of
-      Just b -> 
-        liftAIO (\b -> atomicModifyIORef inp (\s -> (s |> b, ())) >> tryPutMVar proceed ()) -< b
-      Nothing -> returnA -< False
-    c <- liftAIO (const $ atomicModifyIORef out seqRestHead) -< ()
-    returnA -< c
-  -- worker processes the inner, "simulated" signal function.
-  -- worker :: MVar () -> IORef (Seq a) -> IORef (Seq b) -> Automaton a b -> IO ()
-  worker proceed inp out (Automaton sf) = do
-    eb <- atomicModifyIORef inp seqRestHead
-    case eb of
-      Nothing -> takeMVar proceed >> worker proceed inp out (Automaton sf)
-      Just b -> do
-        let (c, sf') = sf b     -- do the calculation
-        deepseq c $ atomicModifyIORef out (\s -> (s |> c, ()))
-        worker proceed inp out sf'
-  seqRestHead s = case viewl s of
-      EmptyL  -> (s,  Nothing)
-      a :< s' -> (s', Just a)
-
--- | The asyncC (C for \"Continuous time\") function allows a continuous 
---   signal function to run as fast as it can asynchronously.  There are 
---   no guarantees that all input data make it to the asynchronous signal 
---   function; if this is required, asyncE should be used instead.  
---   Rather, the embedded signal function runs as fast as it can on 
---   whatever value it has most recently seen.  Its results are 
---   bundled together in a list to be returned to the main signal 
---   function.
-asyncC :: (ArrowIO a, NFData c) => 
-          (ThreadId -> a () ()) -- ^ The thread handler
-       -> (Automaton (->) b c)  -- ^ The automaton to convert to realtime
-       -> a b [c]
---asyncC th sf = asyncC' th (const . return $ (), return) (first sf)
-asyncC threadHandler sf = initialAIO iod darr where
-  iod = do
-    inp <- newEmptyMVar
-    out <- newIORef empty
-    tid <- forkIO $ worker inp out sf
-    return (tid, inp, out)
-  darr (tid, inp, out) = proc b -> do
-    _ <- threadHandler tid -< ()
-    _ <- liftAIO (\b -> tryTakeMVar inp >> putMVar inp b) -< b -- send the worker the new input
-    c <- liftAIO (\_ -> atomicModifyIORef out (\s -> (empty,s))) -< () --collect ready results
-    returnA -< toList c
-  -- worker processes the inner, "simulated" signal function.
-  worker inp out (Automaton sf) = do
-    b <- readMVar inp     -- get the latest input
-    let (c, sf') = sf b   -- do the calculation
-    deepseq c $ atomicModifyIORef out (\s -> (s |> c, ()))
-    worker inp out sf'
-
-
--- | This is a version of asyncC that does IO actions on either end of 
---   the embedded signal function.
-asyncC' :: (ArrowIO a, ArrowLoop a, ArrowCircuit a, ArrowChoice a, NFData b) => 
-           (ThreadId -> a () ()) -- ^ The thread handler
-        -> (b -> IO d, e -> IO ()) -- ^ Effectful input and output channels for the automaton
-        -> (Automaton (->) (b,d) (c,e))  -- ^ The automaton to convert to asynchronize
-        -> a b [c]
-asyncC' threadHandler (iAction, oAction) sf = initialAIO iod darr where
-  iod = do
-    inp <- newIORef undefined
-    start <- newEmptyMVar
-    out <- newIORef empty
-    tid <- forkIO $ takeMVar start >> worker inp out sf
-    return (tid, inp, out, start)
-  darr (tid, inp, out, start) = proc b -> do
-    _ <- threadHandler tid -< ()
-    _ <- liftAIO $ (\b -> deepseq b $ writeIORef inp b) -< b -- send the worker the new input
-    c <- initialAIO (putMVar start ()) (const $ liftAIO (\_ -> atomicModifyIORef' out (\s -> (empty,s)))) -< () --collect ready results
-    returnA -< toList c
-  -- worker processes the inner, "simulated" signal function.
-  worker inp out (Automaton sf) = do
-    b <- readIORef inp     -- get the latest input
-    d <- iAction b
-    let ((c,e), sf') = sf (b,d)   -- do the calculation
-    oAction e
-    atomicModifyIORef' out (\s -> (s |> c, ()))
-    worker inp out sf'
 
FRP/UISF/Examples/Crud.hs view
@@ -31,7 +31,21 @@ instance Eq NameEntry where
     (NameEntry f1 l1) == (NameEntry f2 l2) = f1 == f2 && l1 == l2
 
+deleteFromDB :: (a -> Bool) -> Int -> Database a -> Database a
+deleteFromDB _ _ [] = []
+deleteFromDB f i (x:xs) = case (f x, i == 0) of
+    (True, True)    -> xs
+    (True, False)   -> x:deleteFromDB f (i-1) xs
+    (False, _)      -> x:deleteFromDB f i xs
 
+updateDB :: (a -> Bool) -> Int -> a -> Database a -> Database a
+updateDB _ _ _ [] = []
+updateDB f i a (x:xs) = case (f x, i == 0) of
+    (True, True)    -> a:xs
+    (True, False)   -> x:updateDB f (i-1) a xs
+    (False, _)      -> x:updateDB f i a xs
+
+
 -- defaultnames is a default database for our example
 defaultnames :: Database NameEntry
 defaultnames = [
@@ -41,7 +55,7 @@ 
 
 -- | This function will run the crud GUI with the default names.
-crud = runUI (defaultUIParams {uiSize=(350, 400), uiTitle="CRUD"}) (crudUISF defaultnames)
+crud = runUI (defaultUIParams {uiSize=(450, 400), uiTitle="CRUD"}) (crudUISF defaultnames)
 -- | main = crud
 main = crud
 
@@ -51,35 +65,29 @@ crudUISF :: Database NameEntry -> UISF () ()
 crudUISF initnamesDB = proc _ -> do
   rec
-    fStr <- leftRight $ label "Filter text: " >>> textboxE "" -< Nothing
-    (i, db, fdb, nameStr, surnStr) <- (| leftRight (do
-        (i, db, fdb) <- (| topDown (do
-            rec i <- listbox -< (fdb, i')
-                db <- delay initnamesDB -< db'
-                let fdb = filter (filterFun fStr) db
-            returnA -< (i, db, fdb)) |)
-        (nameStr, surnStr) <- (| topDown (do
-            rec nameStr <- leftRight $ label "Name:    " >>> textboxE "" -< nameStr'
-                surnStr <- leftRight $ label "Surname: " >>> textboxE "" -< surnStr'
-                let nameStr' = if previ == i then Nothing else Just $ firstName ((filter (filterFun fStr) db') `at` i')
-                    surnStr' = if previ == i then Nothing else Just $ lastName  ((filter (filterFun fStr) db') `at` i')
-            returnA -< (nameStr, surnStr)) |)
-        returnA -< (i, db, fdb, nameStr, surnStr)) |)
+    fStr <- leftRight $ label "Filter text: " >>> textbox "" -< Nothing
+    let fdb = filter (filterFun fStr) db
+    (i, nameData) <- (| leftRight (do
+        i <- listbox' -< (fdb, i')
+        nameData <- (| topDown (do
+            rec nameStr <- leftRight $ label "Name:    " >>> textbox "" -< nameStr'
+                surnStr <- leftRight $ label "Surname: " >>> textbox "" -< surnStr'
+                iUpdate <- unique -< i
+                let nameStr' = fmap (const $ firstName (fdb `at` i)) iUpdate
+                    surnStr' = fmap (const $ lastName  (fdb `at` i)) iUpdate
+            returnA -< NameEntry nameStr surnStr) |)
+        returnA -< (i, nameData)) |)
     buttons <- leftRight $ (edge <<< button "Create") &&& 
+                           (edge <<< button "Update") &&& 
                            (edge <<< button "Delete") -< ()
-    previ <- delay 0 -< i
-    let (db', i') = case buttons of
-            (Just _, Nothing) -> (db ++ [NameEntry nameStr surnStr], length fdb)
-            (Nothing, Just _) -> (deleteElem (filterFun fStr) i db,
-                              if i == length fdb - 1 then length fdb - 2 else i)
+    (db,i') <- delay (initnamesDB, -1) -< case buttons of
+            (Just _, (_, _))             -> (db ++ [nameData], length fdb)
+            (Nothing, (Just _, _))       -> (updateDB (filterFun fStr) i nameData db, i)
+            (Nothing, (Nothing, Just _)) -> (deleteFromDB (filterFun fStr) i db,
+                                             if i == length fdb - 1 then i - 1 else i)
             _ -> (db, i)
   returnA -< ()
   where
-    deleteElem _ _ [] = []
-    deleteElem f i (x:xs) = case (f x, i == 0) of
-        (True, True)    -> xs
-        (True, False)   -> x:deleteElem f (i-1) xs
-        (False, _)      -> x:deleteElem f i xs
     filterFun str name = and (map (\s -> isInfixOf s (map toLower $ show name)) (words (map toLower str)))
     lst `at` index = if index >= length lst || index < 0 then NameEntry "" "" else lst!!index
 
@@ -87,27 +95,28 @@ -- If we don't care about formatting, this code simplifies a huge amount to:
 -- crudUISF initnamesDB = proc _ -> do
 --   rec
---     (fStr,fi) <- leftRight $ label "Filter text: " >>> cursoredTextbox False ("",0) -< (fStr,fi)
---     i <- listbox -< (fdb, i')
---     db <- delay initnamesDB -< db'
+--     fStr <- leftRight $ label "Filter text: " >>> textbox "" -< Nothing
 --     let fdb = filter (filterFun fStr) db
---     (nameStr, ni) <- leftRight $ label "Name:    " >>> cursoredTextbox False "" -< (nameStr', ni)
---     (surnStr, si) <- leftRight $ label "Surname: " >>> cursoredTextbox False "" -< (surnStr', si)
---     let nameStr' = if previ == i' then nameStr else firstName ((filter (filterFun fStr) db') `at` i')
---         surnStr' = if previ == i' then surnStr else lastName ((filter (filterFun fStr) db') `at` i')
+--     i <- listbox -< (fdb, i')
+--     nameStr <- leftRight $ label "Name:    " >>> textbox "" -< nameStr'
+--     surnStr <- leftRight $ label "Surname: " >>> textbox "" -< surnStr'
+--     iUpdate <- unique -< i
+--     let nameStr' = fmap (const $ firstName (fdb `at` i)) iUpdate
+--         surnStr' = fmap (const $ lastName  (fdb `at` i)) iUpdate
+--         nameData = NameEntry nameStr surnStr
 --     buttons <- leftRight $ (edge <<< button "Create") &&& 
 --                            (edge <<< button "Delete") -< ()
---     previ <- delay 0 -< i
---     let (db', i') = case buttons of
---             (True, False) -> (db ++ [NameEntry nameStr surnStr], length fdb)
---             (False, True) -> (deleteElem (filterFun fStr) i db,
---                               if i == length fdb - 1 then length fdb - 2 else i)
+--     (db,i') <- delay (initnamesDB, -1) <- case buttons of
+--            (Just _, (_, _))             -> (db ++ [nameData], length fdb)
+--            (Nothing, (Just _, _))       -> (updateDB (filterFun fStr) i nameData db, i)
+--            (Nothing, (Nothing, Just _)) -> (deleteFromDB (filterFun fStr) i db,
+--                                             if i == length fdb - 1 then length fdb - 2 else i)
 --             _ -> (db, i)
 --   returnA -< ()
 --   where
 --     ...
 -- 
--- Clearly, this is much easier to read and clearer as to what is going on. 
+-- Clearly, this is easier to read and clearer as to what is going on. 
 -- However, to keep the style entirely arrow-based, we are forced to inject 
 -- arrow transformers (here leftRight and topDown) to modify chunks of the 
 -- code.  The banana brackets (| |) allow us to refrain from retyping the 
− FRP/UISF/Examples/EnableGUI.hs
@@ -1,20 +0,0 @@-{-# LANGUAGE ForeignFunctionInterface #-}
-module EnableGUI(enableGUI) where
-
-import Data.Int
-import Foreign
-
-type ProcessSerialNumber = Int64
-
-foreign import ccall "GetCurrentProcess" getCurrentProcess :: Ptr ProcessSerialNumber -> IO Int16
-foreign import ccall "_CGSDefaultConnection" cgsDefaultConnection :: IO ()
-foreign import ccall "CPSEnableForegroundOperation" cpsEnableForegroundOperation :: Ptr ProcessSerialNumber -> IO ()
-foreign import ccall "CPSSignalAppReady" cpsSignalAppReady :: Ptr ProcessSerialNumber -> IO ()
-foreign import ccall "CPSSetFrontProcess" cpsSetFrontProcess :: Ptr ProcessSerialNumber -> IO ()
-
-enableGUI = alloca $ \psn -> do
-    getCurrentProcess psn
-    cgsDefaultConnection
-    cpsEnableForegroundOperation psn
-    cpsSignalAppReady psn
-    cpsSetFrontProcess psn
FRP/UISF/Examples/Examples.hs view
@@ -9,13 +9,13 @@ module FRP.UISF.Examples.Examples where
 
 import FRP.UISF
-import FRP.UISF.SOE (withColor', rgb, polygon)
+import FRP.UISF.Graphics
 
 import Numeric (showHex)
 
 -- | This example displays the time from the start of the GUI application.
 timeEx :: UISF () ()
-timeEx = title "Time" $ getTime >>> display
+timeEx = title "Time" $ accumTime >>> display <<< spacer
 
 -- | This example shows off 'button's and state by presenting a plus and 
 -- minus button with a counter that is adjusted by them.
@@ -30,6 +30,7 @@             (Nothing, Just _) -> v-1
             _ -> v)
   display -< v
+  spacer -< ()
 
 -- | This example shows off the 'checkbox' widgets.
 checkboxEx :: UISF () ()
@@ -39,13 +40,14 @@   z <- checkbox "Wednesday" True -< ()
   let v = bin x ++ bin y ++ bin z
   displayStr -< v
+  spacer -< ()
   where
     bin True = "1"
     bin False = "0"
 
 -- | This example shows off the 'radio' button widget.
 radioButtonEx :: UISF () ()
-radioButtonEx = title "Radio Buttons" $ topDown $ radio list 0 >>> arr (list!!) >>> displayStr
+radioButtonEx = title "Radio Buttons" $ topDown $ radio list 0 >>> arr (list!!) >>> displayStr >>> spacer
   where
     list = ["apple", "orange", "banana"]
 
@@ -53,49 +55,69 @@ --   this case).
 shoppinglist :: UISF () ()
 shoppinglist = title "Shopping List" $ topDown $ proc _ -> do
-  a <- title "apples"  $ hiSlider 1 (0,10) 3 -< ()
-  b <- title "bananas" $ hiSlider 1 (0,10) 7 -< () 
-  title "total" $ display -< (a + b)
+  a <- spacer <<< title "apples"  (hiSlider 1 (0,10) 3) -< ()
+  b <- spacer <<< title "bananas" (hiSlider 1 (0,10) 7) -< () 
+  title "total" display -< (a + b)
 
 -- | This example shows off both vertical sliders as well as the 'canvas' 
 -- widget.  The canvas widget can be used to easily create custom graphics 
 -- in the GUI.  Here, it is used to make a color swatch that is 
 -- controllable with RGB values by the sliders.
 colorDemo :: UISF () ()
-colorDemo = setSize (300, 220) $ title "Color" $ pad (4,0,4,0) $ leftRight $ proc _ -> do
-  r <- newColorSlider "R" -< ()
-  g <- newColorSlider "G" -< ()
-  b <- newColorSlider "B" -< ()
+colorDemo = title "Color" $ leftRight $ proc _ -> do
+  r <- newColorSlider (coloredUIText Red "R") -< ()
+  g <- newColorSlider (coloredUIText Green "G") -< ()
+  b <- newColorSlider (coloredUIText Blue "B") -< ()
   changed <- unique -< (r,g,b)
-  let rect = withColor' (rgb r g b) (box ((0,0),d))
-  pad (4,8,0,0) $ canvas d -< fmap (const rect) changed
+  pad (4,8,0,0) $ canvas' layout rect -< changed
   where
-    d = (170,170)
+    layout = makeLayout (Stretchy 10) (Stretchy 10)
     newColorSlider l = title l $ topDown $ proc _ -> do
       v <- viSlider 16 (0,255) 0 -< ()
-      _ <- displayStr -< showHex v ""
+      _ <- setSize (22,22) displayStr -< showHex v ""
       returnA -< v
-    box ((x,y), (w, h)) = polygon [(x, y), (x + w, y), (x + w, y + h), (x, y + h)]
+    rect (r,g,b) d = withColor' (rgbE r g b) (rectangleFilled ((0,0),d))
 
--- | This example shows off the 'textboxE' widget.  Text can be typed in, and 
+-- | This example shows off the 'textbox' widget.  Text can be typed in, and 
 -- that text is transferred to the 'display' widget below when the button 
 -- is pressed.
 textboxdemo :: UISF () ()
-textboxdemo = setLayout (makeLayout (Stretchy 150) (Fixed 100)) $ 
-                title "Saving Text" $ topDown $ proc _ -> do
-  str <- leftRight $ label "Text: " >>> textboxE "" -< Nothing
+textboxdemo = title "Saving Text" $ topDown $ proc _ -> do
+  str <- leftRight $ label "Text: " >>> textbox "" -< Nothing
   b <- button "Save text to below" -< ()
   rec str' <- delay "" -< if b then str else str'
   leftRight $ label "Saved value: " >>> displayStr -< str' 
-  returnA -< ()
 
+uitext :: UIText
+uitext = coloredUIText Red  "H" `appendUIText`
+    coloredUIText Yellow    "e" `appendUIText`
+    coloredUIText Green     "l" `appendUIText`
+    coloredUIText Cyan      "l" `appendUIText`
+    coloredUIText Blue      "o" `appendUIText`
+    coloredUIText Magenta  " W" `appendUIText`
+    coloredUIText Red       "o" `appendUIText`
+    coloredUIText Yellow    "r" `appendUIText`
+    coloredUIText Green     "l" `appendUIText`
+    coloredUIText Cyan      "d" `appendUIText`
+    coloredUIText Blue      "!"
+uitext' = fontUIText Helvetica18 uitext
+
+uitextdemo = title "Color and Fonts" $ constA Nothing >>> textField CharWrap uitext' >>> constA ()
+
 -- | This is the main demo that incorporates all of the other examples 
 -- together.  In addition to demonstrating how 
 -- different widgets can connect, it also shows off the tabbing 
 -- behavior built in to the GUI.  Pressing tab cycles through focusable 
 -- elements, and pressing shift-tab cycles in reverse.
 main :: IO ()
-main = runUI (defaultUIParams {uiSize=(500, 500)}) $ 
-  (leftRight $ (bottomUp $ timeEx >>> buttonEx) >>> (topDown $ checkboxEx) >>> radioButtonEx) >>>
-  (leftRight $ shoppinglist >>> colorDemo) >>> textboxdemo >>> arr id
+main = runUI (defaultUIParams {uiSize=(500, 520), uiCloseOnEsc=True}) $ 
+  (leftRight $ (bottomUp $ timeEx >>> buttonEx) >>> checkboxEx >>> radioButtonEx) >>>
+  (leftRight $ shoppinglist >>> colorDemo) >>> textboxdemo >>> uitextdemo
 
+linesWith s = cons (case break (== '\n') s of
+                      (l, "") -> (l,[])
+                      (l, s') -> (l++"\n", case s' of
+                                             []    -> []
+                                             _:s'' -> linesWith s''))
+  where
+    cons ~(h, t) =  h : t
FRP/UISF/Examples/Pinochle.hs view
@@ -24,8 +24,8 @@ 
 -- We make our own special type of button for inputting hand information, 
 -- so we import a few things directly from Widget and SOE.
-import FRP.UISF.Widget (cyclebox', padding, (//), whenG, box, marked, pushed, popped)
-import FRP.UISF.SOE (text, withColor)
+import FRP.UISF.Widget.Construction (cycleboxS, padding, (//), whenG, shadowBox, marked, pushed, popped)
+import FRP.UISF.Graphics (text, withColor)
 
 import Data.List (delete, foldl', group)
 import GHC.Arr (Ix(..), indexError)
@@ -113,13 +113,13 @@     --display -< shortShow hand
     leftRight $ label "Number of cards:" >>> setSize (40,22) display -< handLength hand
     leftRight $ label "Total meld =" >>> displayStr -< show (sum (map snd3 meld)) ++ ": " ++ show (map fst3 meld)
-    kittenSizeStr <- leftRight $ label "Kitty size =" >>> setSize (40,22) (textboxE "2") -< case (updateEv, handLength hand) of
+    kittenSizeStr <- leftRight $ label "Kitty size =" >>> setSize (40,22) (textbox "2") -< case (updateEv, handLength hand) of
             (Just _, 11) -> Just $ show 4
             (Just _, 15) -> Just $ show 3
             _ -> Nothing
     restr <- checkbox "Restrict trump suit?" False -< ()
     b <- edge <<< button "Calculate meld from kitty" -< ()
-    kre <- (asyncUISFE $ arr kittyResult) -< 
+    kre <- (asyncE $ arr kittyResult) -< 
             fmap (const (hand, kittenSizeStr, if restr then Just trump else Nothing)) b
     let (k,d) = case (clearEv, kre, b) of
             (Just _, _, _) -> (Just [], Just [])
@@ -243,7 +243,7 @@ -- It takes as argument the names of the cards to select and a dynamic 
 -- "clear" event.
 cardSelector :: String -> UISF (SEvent ()) Int
-cardSelector str = arr (fmap (const 0)) >>> cyclebox' d lst 0 where
+cardSelector str = arr (fmap (const 0)) >>> cycleboxS d lst 0 where
     (tw, th) = (8 * (length str + 3), 16) 
     (minw, minh) = (tw + padding * 2, th + padding * 2)
     d = makeLayout (Stretchy minw) (Fixed minh)
@@ -251,8 +251,8 @@       let x' = x + (w - tw) `div` 2 + if i>0 then 0 else -1
           y' = y + (h - th) `div` 2 + if i>0 then 0 else -1
       in withColor Black (text (x', y') s) 
-         // whenG inFocus (box marked b)
-         // box (if i>0 then pushed else popped) b
+         // whenG inFocus (shadowBox marked b)
+         // shadowBox (if i>0 then pushed else popped) b
     lst = zip (map draw [(0,"0 "++str++"s"), (1, "1 "++str), (2, "2 "++str++"s")]) [0,1,2]
 
 
@@ -262,9 +262,10 @@ -------------------------------------------------------------
 
 prepHistogramData :: Map.Map Int Int -> [(Double, String)]
-prepHistogramData m = map f [0..x] where
+prepHistogramData m = normalize $ map f [0..x] where
   x = maybe 0 (fst . fst) $ Map.maxViewWithKey m -- get max meld value (the max key in the map)
   f i = (fromIntegral $ fromMaybe 0 $ Map.lookup i m, show i) -- return pair of count and meld value (in String form)
+  normalize xs = map (\(x,s) -> (x/m,s)) xs where m = maximum (map fst xs)
 
 
 -- this only works for the ints in the list between 0 and 2 inclusive.
FRP/UISF/Examples/SevenGuis.lhs view
@@ -17,9 +17,7 @@ 
 > module FRP.UISF.Examples.SevenGuis where
 > import FRP.UISF
-> import FRP.UISF.UITypes (Layout)
 > import Text.Read (readMaybe)  -- For Temperature Converter
-> import Control.Monad (join)   -- For Temperature Converter
 > 
 > import System.Locale          -- For Flight Booker
 > --import Data.Time.Format.Locale    -- FIXME To be used with time >= 1.5
@@ -28,12 +26,13 @@ > import Data.Time.Format       -- For Flight Booker
 > import Data.Maybe             -- For Flight Booker, Circle Draw
 > 
-> import FRP.UISF.Widget        -- For Timer, Circle Draw
+> import FRP.UISF.Widget.Construction -- For Timer, Circle Draw
 > 
 > import Data.List (isInfixOf)  -- For CRUD
 > import Data.Char (toLower)    -- For CRUD
 > 
-> import FRP.UISF.SOE           -- For Circle Draw
+> import FRP.UISF.UITypes       -- For Circle Draw
+> import FRP.UISF.Graphics      -- For Circle Draw
 > import Data.List (delete)     -- For Circle Draw
 > import Control.Monad (mplus)  -- For Circle Draw
 
@@ -82,11 +81,16 @@ standard Haskell, and the second of which is simple with arrowized 
 FRP.
 
-The textboxE function takes a starting String to create a widget that 
+The textbox function takes a starting String to create a widget that 
 accepts an Event String as input and produces String as output.  We use 
 the "unique" transformer to transform this output into events that only 
 update when a change occurs.
 
+> uTextbox :: String -> UISF (SEvent String) (SEvent String)
+> uTextbox str = proc e -> do
+>   o <- unique <<< textbox str -< e
+>   returnA -< if o == e then Nothing else o
+
 The first half of the program sets up the 4 widgets (two textboxes and 
 two labels), and the second half does the text parsing and actual 
 conversion (note that this half is all pure Haskell code).
@@ -96,21 +100,21 @@ to prevent infinite recursion, we must put a "delay" into the loop, and 
 so we do this twice, once for each textbox.
 
-> tempCovertSF :: UISF () ()
-> tempCovertSF = leftRight $ proc _ -> do
->   rec c <- unique <<< textboxE "" -< updateC
+> tempConvertSF :: UISF () ()
+> tempConvertSF = leftRight $ proc _ -> do
+>   rec c <- uTextbox "" -< updateC
 >       label "degrees Celsius = " -< ()
->       f <- unique <<< textboxE "" -< updateF
+>       f <- uTextbox "" -< updateF
 >       label "degrees Fahrenheit" -< ()
->       let cNum = join $ fmap (readMaybe :: String -> Maybe Double) c
->           fNum = join $ fmap (readMaybe :: String -> Maybe Double) f
->           cNum' = if c == updateC then Nothing else cNum
->           fNum' = if f == updateF then Nothing else fNum
->       updateC <- delay Nothing -< fmap (\f -> show $ round $ (f - 32) * (5/9)) fNum'
->       updateF <- delay Nothing -< fmap (\c -> show $ round $ c * (9/5) + 32) cNum'
+>       updateC <- delay Nothing -< fmap (show . f2c) (f >>= readMaybe)
+>       updateF <- delay Nothing -< fmap (show . c2f) (c >>= readMaybe)
 >   returnA -< ()
+>   where f2c :: Double -> Int
+>         f2c f = round $ (f - 32) * (5/9)
+>         c2f :: Double -> Int
+>         c2f c = round $ c * (9/5) + 32
 >
-> tempConvert = runUI (defaultUIParams {uiSize=(400, 24), uiTitle="Temp Converter"}) tempCovertSF
+> tempConvert = runUI (defaultUIParams {uiSize=(440, 24), uiTitle="Temp Converter"}) tempConvertSF
 > gui2 = tempConvert
 
 
@@ -139,7 +143,7 @@ 
 > timeInputTextbox :: TimeLocale -> String -> String -> UISF () (SEvent UTCTime)
 > timeInputTextbox tl format start = leftRight $ proc _ -> do
->     t <- textboxE start -< Nothing
+>     t <- textbox start -< Nothing
 >     let ret = readTimeMaybe tl format t
 >     case ret of
 >       Just _ -> returnA -< ret
@@ -150,7 +154,7 @@ >                                       [(x, "")] -> Just x
 >                                       _ -> Nothing
 
-Note the use of the delay with the textboxE -- we need this because we 
+Note the use of the delay with the textbox -- we need this because we 
 will use the value t to determine whether to insert the label or not.
 
 Note the clever use of unique and resetText.  We would like the display 
@@ -164,7 +168,7 @@ >     choice <- radio ["one-way flight","return flight"] 0 -< ()
 >     t1 <- timeInputTextbox tl format (formatTime tl format currentTime) -< ()
 >     t2 <- case choice of
->             1 -> timeInputTextbox tl format (formatTime tl format currentTime) -< ()
+>             1 -> timeInputTextbox tl format (formatTime tl format nextweek) -< ()
 >             _ -> label "" -< Nothing
 >     resetText <- unique -< (choice, t1, t2)
 >     b <- if (choice == 0 && isJust t1) || (choice == 1 && verifyGreater t1 t2)
@@ -175,6 +179,7 @@ >     resultStr <- hold "" -< b
 >     displayStr -< resultStr
 >   where format ="%Y.%m.%d"
+>         nextweek = currentTime { utctDay = addDays 7 $ utctDay currentTime }
 >         -- outText formats the data for a booking confirmation
 >         outText tl format 0 (Just t1) _  = "You have booked a one-way flight on " 
 >             ++ (formatTime tl format t1) ++ "."
@@ -197,27 +202,16 @@ The timer is very straightforward with UISF even though there is no 
 built-in "gauge" widget.  We'll start by defining one by using the 
 canvas' widget builder.  The widget will take the pair of 
-(elapsed time, total duration) and draw a block of the appropriate 
+(elapsed time, total duration) and draw a rectangle of the appropriate 
 size.  To use canvas', we supply a layout argument (stretchy in the 
 horizontal direction but fixed to 30 pixels in the vertical direction).
 
 > gauge :: Layout -> UISF (DeltaT, DeltaT) ()
 > gauge l = unique >>> canvas' l draw where
->   draw (x,t) (w,h) = block ((0,padding),(min w' $ round $ x*(fromIntegral w')/t,h-2*padding))
+>   draw (x,t) (w,h) = rectangleFilled ((0,padding),(min w' $ round $ x*(fromIntegral w')/t,h-2*padding))
 >       where w' = (w - 2*padding)
 
-Next, we make a short helper function for keeping track of elapsed time.  
-UISF provides "getTime", which provides the number of seconds since the 
-GUI started; here we write getDeltaTime, which uses a simple "delay" 
-operator to find how much time has gone by in the most recent clock cycle.
-
-> getDeltaTime :: UISF () DeltaT
-> getDeltaTime = proc _ -> do
->   t <- getTime -< ()
->   pt <- delay 0 -< t
->   returnA -< t - pt
-
-With these two helpers, the program is a snap.  Note once again that 
+With this, the program is a snap.  Note once again that 
 since the elapsed time "e" is being used directly in the GUI's output, 
 we must apply a delay to it to prevent an infinite recursion.
 
@@ -303,33 +297,32 @@ in which case the banana bracketed code would not inherit its parent's 
 scope either, but still, it is less than ideal.
 
-We start by asking for the filter text and then using banana brackets 
-to define a "leftRight" layout portion.
+We start by asking for the filter text, filtering the database, and then 
+using banana brackets to define a "leftRight" layout portion.
 
 > crudSF :: Database NameEntry -> UISF () ()
 > crudSF initnamesDB = proc _ -> do
 >   rec
->     fStr <- leftRight $ label "Filter text: " >>> textboxE "" -< Nothing
->     (i, db, fdb, nameData) <- (| leftRight (do
+>     fStr <- leftRight $ label "Filter text: " >>> textbox "" -< Nothing
+>     let fdb = filter (filterFun fStr) db
+>     (i, nameData) <- (| leftRight (do
 
 This leftRight portion will have a listbox on the left and then a 
 topDown portion on the right that will be for entering name data.
 
->         rec i <- listbox -< (fdb, i')
->             db <- delay initnamesDB -< db'
->             let fdb = filter (filterFun fStr) db
+>         i <- listbox' -< (fdb, i')
 >         nameData <- (| topDown (do
 
 We add two textboxes for the first name and surname strings and 
 then set them to update whenever one of the listbox items is selected.
 
->             rec nameStr <- leftRight $ label "Name:    " >>> textboxE "" -< nameStr'
->                 surnStr <- leftRight $ label "Surname: " >>> textboxE "" -< surnStr'
+>             rec nameStr <- leftRight $ label "Name:    " >>> textbox "" -< nameStr'
+>                 surnStr <- leftRight $ label "Surname: " >>> textbox "" -< surnStr'
 >                 iUpdate <- unique -< i
->                 let nameStr' = fmap (const $ firstName ((filter (filterFun fStr) db') `at` i')) iUpdate
->                     surnStr' = fmap (const $ lastName  ((filter (filterFun fStr) db') `at` i')) iUpdate
+>                 let nameStr' = fmap (const $ firstName (fdb `at` i)) iUpdate
+>                     surnStr' = fmap (const $ lastName  (fdb `at` i)) iUpdate
 >             returnA -< NameEntry nameStr surnStr) |)
->         returnA -< (i, db, fdb, nameData)) |)
+>         returnA -< (i, nameData)) |)
 
 Finally, we make the three buttons, which we can do all at once with 
 arrow combinators.  Based on button presses, we update the database.
@@ -337,11 +330,11 @@ >     buttons <- leftRight $ (edge <<< button "Create") &&& 
 >                            (edge <<< button "Update") &&& 
 >                            (edge <<< button "Delete") -< ()
->     let (db', i') = case buttons of
+>     (db, i') <- delay (initnamesDB, -1) -< case buttons of
 >             (Just _, (_, _))             -> (db ++ [nameData], length fdb)
 >             (Nothing, (Just _, _))       -> (updateDB (filterFun fStr) i nameData db, i)
 >             (Nothing, (Nothing, Just _)) -> (deleteFromDB (filterFun fStr) i db,
->                                             if i == length fdb - 1 then length fdb - 2 else i)
+>                                             if i == length fdb - 1 then i - 1 else i)
 >             _ -> (db, i)
 >   returnA -< ()
 >   where
@@ -363,8 +356,7 @@ To start, let's write some code for circles.  We'll begin with a very 
 simple circle type, accessors for it, and a distance function for points.
 
-> -- type Point = (Int, Int) -- The Point class is imported from FRP.UISF.SOE
-> type Radius = Double
+> type Radius = Int
 > type Circle = (Point, Radius)
 > 
 > getCenter :: Circle -> Point
@@ -386,7 +378,7 @@ > getSelectedCircle p = getCircle' Nothing where
 >   getCircle' res [] = fmap snd res
 >   getCircle' res (c@(cp,cr):cs) = let d = distance p cp in
->     case (d<cr, isJust res) of
+>     case (d<fromIntegral cr, isJust res) of
 >       (True, True) -> getCircle' (if d < fst (fromJust res) then Just (d,c) else res) cs
 >       (True, False) -> getCircle' (Just (d,c)) cs
 >       _ -> getCircle' res cs
@@ -399,13 +391,6 @@   - It will send output events corresponding to mouse clicks.
   - It will display the circles with any that the cursor is in highlighted.
 
-First, we'll make two little drawing functions for making filled and open 
-circles.  UISF provides the more generic 'ellipse' and 'arc' functions, but 
-they can be easily adjusted for our purposes:
-
-> filledCircle (x,y) r' = let r = round r' in ellipse (x-r,y-r) (x+r,y+r)
-> openCircle   (x,y) r' = let r = round r' in arc     (x-r,y-r) (x+r,y+r) 0 360
-
 Now, we have the tools to make the circle canvas
 
 > type LeftClicks = SEvent Point
@@ -419,14 +404,14 @@ >       where 
 >         newLst = fromMaybe prevLst inpLst
 >         (clickEvts, focusCircle, mousePt, redraw) = case (evt, isJust inpLst) of
->           (Button pt True  True, d) -> ((Just pt, Nothing),  prevFC, prevPt, d)
->           (Button pt False True, d) -> ((Nothing, getSelectedCircle pt newLst), prevFC, prevPt, d)
+>           (Button pt LeftButton  True, d) -> ((Just pt, Nothing),  prevFC, prevPt, d)
+>           (Button pt RightButton True, d) -> ((Nothing, getSelectedCircle pt newLst), prevFC, prevPt, d)
 >           (MouseMove pt, d) -> let fc = getSelectedCircle pt newLst in ((Nothing, Nothing), fc, pt, prevFC /= fc || d)
 >           (_, d) -> ((Nothing, Nothing), getSelectedCircle prevPt newLst, prevPt, d)
 >     draw _ _ (cs,fc,_) = draw' cs fc
 >     draw' [] Nothing = nullGraphic
->     draw' [] (Just (p,r)) = withColor' gray2 $ filledCircle p r
->     draw' ((p,r):cs) fc = withColor Black (openCircle p r) // draw' cs fc
+>     draw' [] (Just (p,r)) = withColor MediumBeige $ circleFilled p r
+>     draw' ((p,r):cs) fc = withColor Black (circleOutline p r) // draw' cs fc
 
 Lastly, we'll create the undo/redo functionality.  This is all pure 
 Haskell code and has no UISF components.
@@ -498,14 +483,14 @@ The adjustment slider should only appear after a right click and before 
 the cancel or set buttons are pressed -- we use an 'accum' to achieve this.
 
->     isAdjustActive <- accum False -< fmap (const . const False) majorU 
+>     isAdjustActive <- accum False <<< delay Nothing -< fmap (const . const False) majorU 
 >                              `mplus` fmap (const . const False) cancel
 >                              `mplus` fmap (const . const True) rightClicks
 >     adjustC <- accum ((0,0),0) -< fmap const rightClicks
 >     (minorU, majorU, cancel) <- if isAdjustActive
 >                                 then do
 >                                 leftRight (label "Adjust Diameter of circle at" >>> display) -< getCenter adjustC
->                                 newR <- hSlider (2,200) defaultRadius -< () -- fmap getRadius rightClicks
+>                                 newR <- hiSlider 1 (2,200) defaultRadius -< () -- fmap getRadius rightClicks
 >                                 newRU <- unique -< newR
 >                                 (setButton, cancelButton) <- leftRight $ (edge <<< button "Set") &&& 
 >                                                                          (edge <<< button "Cancel") -< ()
FRP/UISF/Examples/fft.hs view
@@ -13,8 +13,7 @@ 
 {-# LANGUAGE Arrows #-}
 module FRP.UISF.Examples.FFT where
-import FRP.UISF hiding (delay)
-import Control.Arrow.Operations
+import FRP.UISF
 import Numeric.FFT (fft)
 import Data.Complex
 import Data.Map (Map)
@@ -116,7 +115,7 @@     _ <- leftRight (label "Freq 1: " >>> display) -< f1
     f2 <- hSlider (1, 2000) 440 -< ()
     _ <- leftRight (label "Freq 2: " >>> display) -< f2
-    d <- asyncUISFV sr 0.1 myAutomaton -< (f1, f2)
+    d <- asyncVT sr 0.1 myAutomaton -< (f1, f2)
     let fft = listToMaybe $ catMaybes $ map (snd . fst) d
         s = map (\((s, _), t) -> (s,t)) d
     _ <- histogram (makeLayout (Stretchy 10) (Fixed 150)) -< fft
+ FRP/UISF/Graphics.hs view
@@ -0,0 +1,36 @@+-----------------------------------------------------------------------------
+-- |
+-- Module      :  FRP.UISF.Graphics
+-- Copyright   :  (c) Daniel Winograd-Cort 2015
+-- License     :  see the LICENSE file in the distribution
+--
+-- Maintainer  :  dwc@cs.yale.edu
+-- Stability   :  experimental
+
+{-# LANGUAGE BangPatterns, FlexibleInstances, TypeSynonymInstances #-}
+module FRP.UISF.Graphics (
+  Point, Angle, Dimension, Rect,
+  Color(..), RGB, colorToRGB, rgb, rgbE, extractRGB,
+  Graphic,
+  nullGraphic,
+  overGraphic,
+  withColor, withColor',
+  text, textLines,
+  ellipse, shearEllipse, line, polygon, polyline, polybezier, arc,
+  circleFilled, circleOutline, rectangleFilled, rectangleOutline,
+  translateGraphic, rotateGraphic, scaleGraphic,
+  boundGraphic,
+  UIText(..), UITexty(..),
+  uitextToString, splitUIText, takeUIText, dropUIText, uitextLen,
+  pureUIText, appendUIText, coloredUIText, rgbUIText, fontUIText,
+  textWidth, textWithinPixels, textHeight,
+  WrapSetting(..), prepText,
+  textWidth', textWithinPixels', textHeight',
+  BitmapFont(..)
+  ) where
+
+
+import FRP.UISF.Graphics.Types
+import FRP.UISF.Graphics.Color
+import FRP.UISF.Graphics.Text
+import FRP.UISF.Graphics.Graphic
+ FRP/UISF/Graphics/Color.hs view
@@ -0,0 +1,97 @@+-----------------------------------------------------------------------------
+-- |
+-- Module      :  FRP.UISF.Graphics.Color
+-- Copyright   :  (c) Daniel Winograd-Cort 2015
+-- License     :  see the LICENSE file in the distribution
+--
+-- Maintainer  :  dwc@cs.yale.edu
+-- Stability   :  experimental
+
+{-# LANGUAGE BangPatterns, FlexibleInstances, TypeSynonymInstances #-}
+module FRP.UISF.Graphics.Color (
+  Color(..), RGB, colorToRGB, rgb, rgbE, extractRGB,
+  ) where
+
+import Data.Ix (Ix)
+import Control.DeepSeq
+
+
+-- | We provide a data type for colors to allow users to easily 
+--  and clearly specify common colors.  Primary and secondary 
+--  RGB colors are represented along with a few beige colors for use 
+--  in many GUI elements.
+data Color = Black
+           | Blue
+           | Green
+           | Cyan
+           | Red
+           | Magenta
+           | Yellow
+           | White
+           | Gray
+           | VLightBeige
+           | LightBeige -- ^ This is the default background color for the UI window.
+           | MediumBeige
+           | DarkBeige
+  deriving (Eq, Ord, Bounded, Enum, Ix, Show, Read)
+
+instance NFData Color where
+  rnf (!_) = ()
+
+-- | RGB can be used to specify colors more precisely.  Create them with 
+--  one of the two smart constructors 'rgb' or 'rgbE'.
+newtype RGB = RGB (Int, Int, Int)
+  deriving (Eq)
+
+instance Show RGB where
+  show (RGB (r, g, b)) = "{R="++show r++",G="++show g++",B="++show b++"}"
+
+instance NFData RGB where
+  rnf (RGB rgb) = rnf rgb
+
+-- | Generally used as an internal function for converting Color to RGB, 
+--  but can be used by anyone.
+colorToRGB :: Color -> RGB
+colorToRGB Black   = RGB (0, 0, 0)
+colorToRGB Blue    = RGB (0, 0, 255)
+colorToRGB Green   = RGB (0, 255, 0)
+colorToRGB Cyan    = RGB (0, 255, 255)
+colorToRGB Red     = RGB (255, 0, 0)
+colorToRGB Magenta = RGB (255, 0, 255)
+colorToRGB Yellow  = RGB (255, 255, 0)
+colorToRGB White   = RGB (255, 255, 255)
+colorToRGB Gray    = RGB (128, 128, 128)
+colorToRGB VLightBeige = rgbE 0xf1 0xef 0xe2
+colorToRGB LightBeige  = rgbE 0xec 0xe9 0xd8
+colorToRGB MediumBeige = rgbE 0xac 0xa8 0x99
+colorToRGB DarkBeige   = rgbE 0x71 0x6f 0x64
+-- In previous versions, there was a color called "blue3".
+-- blue3 = rgbE 0x31 0x3c 0x79 --dark slate blue
+
+
+
+-- | This function takes three integral values between 0 and 255 
+--  inclusive and create an RGB value with them.  If any of the 
+--  values fall outside the acceptable range, Nothing is returned.
+rgb :: (Integral r, Integral g, Integral b) => r -> g -> b -> Maybe RGB
+rgb r g b = do
+    r' <- bound r
+    g' <- bound g
+    b' <- bound b
+    return $ RGB (r',g',b')
+  where
+    bound :: (Integral i, Integral o) => i -> Maybe o
+    bound i = if i > 255 || i < 0 then Nothing else Just (fromIntegral i)
+
+-- | This is a version of 'rgb' that throws an error when a given 
+--  value falls outside the acceptable 0-255 range.  The error message 
+--  shows the bad input, so the extra Show constraint is necessary.
+rgbE :: (Integral r, Integral g, Integral b,
+         Show r, Show g, Show b) => r -> g -> b -> RGB
+rgbE r g b = case rgb r g b of
+  Just x  -> x
+  Nothing -> error $ "Invalid values given to rgbE: " ++ show (r,g,b)
+
+-- | Use this to extract the values from an RGB color.
+extractRGB :: (Integral r, Integral g, Integral b) => RGB -> (r,g,b)
+extractRGB (RGB (r, g, b)) = (fromIntegral r, fromIntegral g, fromIntegral b)
+ FRP/UISF/Graphics/Graphic.hs view
@@ -0,0 +1,237 @@+-----------------------------------------------------------------------------
+-- |
+-- Module      :  FRP.UISF.Graphics.Graphic
+-- Copyright   :  (c) Daniel Winograd-Cort 2015
+-- License     :  see the LICENSE file in the distribution
+--
+-- Maintainer  :  dwc@cs.yale.edu
+-- Stability   :  experimental
+
+{-# LANGUAGE BangPatterns #-}
+module FRP.UISF.Graphics.Graphic (
+  -- $graphics
+  -- * Graphics
+  Graphic(..),
+  nullGraphic,
+  overGraphic,
+  withColor, withColor',
+  text, textLines,
+  ellipse, shearEllipse, line, polygon, polyline, polybezier, arc,
+  circleFilled, circleOutline, rectangleFilled, rectangleOutline,
+  translateGraphic, rotateGraphic, scaleGraphic,
+  boundGraphic
+  ) where
+
+
+import FRP.UISF.Graphics.Color
+import FRP.UISF.Graphics.Text
+import FRP.UISF.Graphics.Types
+import Control.DeepSeq
+
+{- $graphics
+This module provides an abstract representation for graphics in the GUI 
+along with a rendering function specific to OpenGL.
+
+The Graphic data type encodes an abstract graphic, which can be created 
+and combined with smart constructors.  This means that Graphics are 
+inherently restricted to what is possible in this abstract form.  For 
+example, OpenGL may support 3D rotations, but because the abstract 
+Graphic does not, they cannot be performed in UISF.
+
+For now, we have a lean set of graphics that should satisfy most GUI 
+needs.  Additionally, this layer of abstraction should make it easier 
+to add more graphical back ends (perhaps a web back end in the future?).  
+If UISF grows to include more graphical representations (e.g. more ways 
+to render text), we can add them as necessary.
+
+-}
+
+------------------------------------------------------------
+-- Graphic
+------------------------------------------------------------
+
+-- | The main Graphic data type stores graphic information.  
+--  Constructors are not directly exposed to encourage the use of 
+--  the smart constructors.
+--
+--  If you would like to add custom rendering functions for Graphic, 
+--  you will clearly need access to the constructors to destruct the 
+--  graphics.  Please request this, and I can either export them 
+--  or we can discuss adding more rendering functions to this library.
+
+data Graphic = 
+    NoGraphic
+  | GText Point UIText
+  | GPolyLine [Point]
+  | GPolygon [Point]
+  | GArc Rect Angle Angle
+  | GEllipse Rect
+  | GBezier [Point]
+  | GTranslate Point Graphic
+  | GRotate Point Angle Graphic
+  | GScale Double Double Graphic
+  | GColor RGB Graphic
+  | GBounded Rect Graphic
+  | OverGraphic Graphic Graphic
+  deriving (Eq, Show)
+
+instance NFData Graphic where
+  rnf NoGraphic = ()
+  rnf (GText (!_,!_) str) = rnf str
+  rnf (GPolyLine !pts) = ()
+  rnf (GPolygon !pts) = ()
+  rnf (GArc ((!_,!_),(!_,!_)) !_ !_) = ()
+  rnf (GEllipse ((!_,!_),(!_,!_))) = ()
+  rnf (GBezier !pts) = ()
+  rnf (GTranslate (!_,!_) g) = rnf g
+  rnf (GRotate (!_,!_) !_ g) = rnf g
+  rnf (GScale !_ !_ g) = rnf g
+  rnf (GColor !_ g) = rnf g
+  rnf (GBounded ((!_,!_),(!_,!_)) g) = rnf g
+  rnf (OverGraphic g1 g2) = rnf g1 `seq` rnf g2
+
+-- | The absence of a graphic.
+nullGraphic :: Graphic
+nullGraphic = NoGraphic
+
+-- | The overlay of two graphics, the first over the second.
+overGraphic :: Graphic -> Graphic -> Graphic
+overGraphic g1 NoGraphic = g1
+overGraphic NoGraphic g2 = g2
+overGraphic g1 g2 = OverGraphic g1 g2
+
+----------
+-- Text --
+----------
+
+-- | Paint the given text at the given point.
+text :: UITexty s => Point -> s -> Graphic
+text p = GText p . toUIText
+
+-- | A convenience function for painting a set of (Point,String) pairs.
+textLines :: UITexty s => [(Point, s)] -> Graphic
+textLines = foldl (\g (p,s) -> overGraphic (text p s) g) nullGraphic
+
+------------
+-- Colors --
+------------
+
+-- | Use the given color to paint the given graphic.
+withColor :: Color -> Graphic -> Graphic
+withColor = withColor' . colorToRGB
+
+-- | Use the given RGB color to paint the given graphic.
+withColor' :: RGB -> Graphic -> Graphic
+withColor' _ NoGraphic = NoGraphic
+withColor' c g = GColor c g
+
+
+------------
+-- Shapes --
+------------
+
+-- | Draw an ellipse bounded by the given rectangle.
+ellipse :: Rect -> Graphic
+ellipse = GEllipse
+
+-- | Draw a shear ellipse bounded by the given rectangle.  This code 
+--  was written originally by Paul Liu.
+shearEllipse :: Point -> Rect -> Graphic
+shearEllipse (x0,y0) r = 
+  let ((x1,y1), (w, h)) = normaliseRect r
+      (x2,y2) = (x1 + w, y1 + h)
+      x =  (x1 + x2) / 2  -- centre of parallelogram
+      y =  (y1 + y2) / 2
+      dx1 = (x1 - fromIntegral x0) / 2 -- distance to corners from centre
+      dy1 = (y1 - fromIntegral y0) / 2
+      dx2 = (x2 - fromIntegral x0) / 2
+      dy2 = (y2 - fromIntegral y0) / 2
+      pts = [ (round $ x + c*dx1 + s*dx2, round $ y + c*dy1 + s*dy2)
+            | (c,s) <- cos'n'sins ]
+      cos'n'sins = [ (cos a, sin a) | a <- segment 0 (2 * pi) (40 / (w + h))]
+  in GPolygon pts
+
+-- | Draw a line segment connecting the given two points.
+line :: Point -> Point -> Graphic
+line p q = GPolyLine [p,q]
+
+-- | Draw a filled polygon with corners defined by the given points.
+polygon :: [Point] -> Graphic
+polygon = GPolygon
+
+-- | Draw a sequence of line segments defined by the given points.
+polyline :: [Point] -> Graphic
+polyline = GPolyLine
+
+-- | Draw a Bezier curve defined by the given points.
+polybezier :: [Point] -> Graphic
+polybezier = GBezier
+
+-- | Draw an arc of the ellipse bounded by the given rectangle that 
+--  starts at the first angle measure and ends at the second.
+arc :: Rect -> Angle -> Angle -> Graphic
+arc = GArc
+
+-- | Draw a filled circle with given center and radius.
+circleFilled :: Point -> Int -> Graphic
+circleFilled (x,y) r = GEllipse ((x-r,y-r),(2*r,2*r))
+
+-- | Draw the outline of a circle with given center and radius.
+circleOutline :: Point -> Int -> Graphic
+circleOutline (x,y) r = GArc ((x-r,y-r),(2*r,2*r)) 0 360
+
+-- | Draw a filled rectangle.
+rectangleFilled :: Rect -> Graphic
+rectangleFilled ((x,y), (w, h)) = GPolygon [(x, y), (x + w, y), (x + w, y + h), (x, y + h)]
+
+-- | Draw the outline of a rectangle.
+rectangleOutline :: Rect -> Graphic
+rectangleOutline ((x,y), (w, h)) = GPolyLine [(x, y), (x + w, y), (x + w, y + h), (x, y + h)]
+
+
+---------------------
+-- Transformations --
+---------------------
+
+-- | Translate the given graphic so that its origin is at the given 
+--  point.
+translateGraphic :: Point -> Graphic -> Graphic
+translateGraphic _ NoGraphic = NoGraphic
+translateGraphic p g = GTranslate p g
+
+-- | Rotate the given graphic around the given point by the given 
+--  number of degrees.
+rotateGraphic :: Point -> Angle -> Graphic -> Graphic
+rotateGraphic _ _ NoGraphic = NoGraphic
+rotateGraphic p a g = GRotate p a g
+
+-- | Scale the given graphic in the X and Y dimension respectively.
+scaleGraphic :: Double -> Double -> Graphic -> Graphic
+scaleGraphic _ _ NoGraphic = NoGraphic
+scaleGraphic x y g = GScale x y g
+
+
+--------------
+-- Bounding --
+--------------
+
+-- | Cut the given graphic so that nothing outside of the given 
+--  rectangle is visible.
+boundGraphic :: Rect -> Graphic -> Graphic
+boundGraphic _ NoGraphic = NoGraphic
+boundGraphic r g = GBounded r g
+
+
+
+------------------------------------------------------------
+-- Helper functions
+------------------------------------------------------------
+normaliseRect :: Rect -> ((Double, Double),(Double, Double))
+normaliseRect ((x, y), (w, h)) = ((fromIntegral x', fromIntegral y'), (fromIntegral w', fromIntegral h'))
+  where (x',w') = if w < 0 then (x+w, 0-w) else (x, w)
+        (y',h') = if h < 0 then (y+h, 0-h) else (y, h)
+
+segment :: (Num t, Ord t) => t -> t -> t -> [t]
+segment start stop step = ts start
+  where ts i = if i >= stop then [stop] else i : ts (i + step)
+
+ FRP/UISF/Graphics/Text.hs view
@@ -0,0 +1,287 @@+-----------------------------------------------------------------------------
+-- |
+-- Module      :  FRP.UISF.Graphics.Text
+-- Copyright   :  (c) Daniel Winograd-Cort 2015
+-- License     :  see the LICENSE file in the distribution
+--
+-- Maintainer  :  dwc@cs.yale.edu
+-- Stability   :  experimental
+
+{-# LANGUAGE BangPatterns, FlexibleInstances, TypeSynonymInstances #-}
+module FRP.UISF.Graphics.Text (
+  UIText(..), UITexty(..),
+  uitextToString, splitUIText, takeUIText, dropUIText, uitextLen,
+  pureUIText, appendUIText, coloredUIText, rgbUIText, fontUIText,
+  textWidth, textWithinPixels, textHeight,
+  WrapSetting(..), prepText,
+  textWidth', textWithinPixels', textHeight',
+  uitextLines, uitextWords,
+  BitmapFont(..),
+  ) where
+
+import Graphics.UI.GLUT.Fonts
+import Data.Array.IArray
+import Data.List (foldl')
+import Data.Char (isSpace)
+import Data.String (IsString(..))
+
+import FRP.UISF.Graphics.Color
+import FRP.UISF.Graphics.Types
+
+import Control.DeepSeq
+
+-- FIXME: I hate having an orphan instance here, but I'm not sure what to do about it.
+instance NFData BitmapFont where
+  rnf f = seq f ()
+
+defaultFont = Fixed9By15
+
+------------------------------------------------------------
+-- UI Text
+------------------------------------------------------------
+
+-- | Text in UISF can be rendered in multiple fonts and colors, 
+--  so we need a more powerful data type to encode it.  The UIText 
+--  data type does this.
+newtype UIText = UIText {unwrapUIT :: [(Maybe RGB, BitmapFont, String)]}
+  deriving (Eq, Show)
+instance NFData UIText where
+  rnf (UIText lst) = rnf lst
+instance IsString UIText where
+  fromString = pureUIText
+
+-- | To retain easy compatibility with Strings (or other text 
+--  representations), we also provide the UITexty class, which is 
+--  how all widgets that accept UIText should do so.
+class UITexty a where
+  toUIText :: a -> UIText
+
+instance UITexty UIText where
+  toUIText = id
+instance UITexty String where
+  toUIText = pureUIText
+--instance UITexty Text where
+--  toUIText = PureText . unpack
+
+-- | The empty string in UIText.
+emptyUIText :: UIText
+emptyUIText = UIText []
+
+-- | Returns True when given an empty string and False otherwise.
+isEmptyUIText :: UIText -> Bool
+isEmptyUIText uit = go (unwrapUIT uit) where
+  go [] = True
+  go ((_,_,[]):rest) = go rest
+  go _ = False
+
+-- | Removes all font and color formatting from a UIText, returning 
+--  its underlying String representation.
+uitextToString :: UIText -> String
+uitextToString (UIText lst) = concatMap (\(_,_,s) -> s) lst
+
+-- | Returns the number of characters in a UIText.
+uitextLen :: UIText -> Int
+uitextLen = length . uitextToString
+
+-- | Take a certain number of characters off of a UIText
+takeUIText :: Int -> UIText -> UIText
+takeUIText n = fst . splitUIText n
+--takeUIText n (UIText uit) = UIText $ go n uit where
+--  go 0 uit = uit
+--  go n [] = []
+--  go n ((c,f,s):rest) = let n' = n - length s in
+--    if n' < 0 then (c,f,drop n s):rest else go n' rest
+
+-- | Drop a certain number of characters from a UIText
+dropUIText :: Int -> UIText -> UIText
+dropUIText n = snd . splitUIText n
+
+-- | Split a UIText at the given character point.
+splitUIText :: Int -> UIText -> (UIText, UIText)
+splitUIText n (UIText uit) = let (u1,u2) = go n [] uit in (UIText u1, UIText u2) where
+  go 0 taken rest = (reverse taken, rest)
+  go n taken [] = (reverse taken, [])
+  go n taken ((c,f,s):rest) = let n' = n - length s in
+    if n' >= 0 then go n' ((c,f,s):taken) rest
+    else let (t,d) = splitAt n s in (reverse ((c,f,t):taken), (c,f,d):rest)
+
+-- | A convenience function for taking a UITexty object directly to the 
+--  underlying (RGB,Font,String) list.
+unwrapUITexty :: UITexty s => s -> [(Maybe RGB, BitmapFont, String)]
+unwrapUITexty = unwrapUIT . toUIText
+
+-- | Lifts a String to a UIText (with default color and font).
+pureUIText :: String -> UIText
+pureUIText s = UIText [(Nothing, defaultFont, s)]
+
+-- | Appends two UITexty objects together.
+appendUIText :: (UITexty s1, UITexty s2) => s1 -> s2 -> UIText
+appendUIText s1 s2 = UIText $ unwrapUITexty s1 ++ unwrapUITexty s2
+
+-- | Colors a UITexty object.
+coloredUIText :: UITexty s => Color -> s -> UIText
+coloredUIText c s = UIText $ map (\(_,f,str) -> (newC,f,str)) (unwrapUITexty s)
+ where newC = Just $ colorToRGB c
+
+-- | Colors a UITexty object with an exact RGB value.
+rgbUIText c s = UIText $ map (\(_,f,str) -> (c,f,str)) (unwrapUITexty s)
+
+-- | Converts the UITexty object to the given font.
+fontUIText f s = UIText $ map (\(c,_,str) -> (c,f,str)) (unwrapUITexty s)
+
+
+-- | Returns the width of the String in pixels as it will be rendered
+textWidth :: UITexty s => s -> Int
+textWidth s = sum $ map (\(_,f,str) -> textWidth' f str) (unwrapUITexty s)
+
+-- | Given a String and a number of pixels, returns the leading 
+--  substring that fits within the horizontal number of pixels along 
+--  with the remaining text of the String.
+textWithinPixels :: UITexty s => Int -> s -> (UIText, UIText)
+textWithinPixels i s = let (s1,s2) = go i (unwrapUITexty s) []
+  in (UIText s1, UIText s2) where
+  go i [] sofar = (reverse sofar, [])
+  go i ((c,f,str):rest) sofar = let i' = i - (textWidth' f str)
+    in if i' >= 0
+       then go i' rest ((c,f,str):sofar)
+       else let (s1,s2) = textWithinPixels' f i str
+            in (reverse $ (c,f,s1):sofar, (c,f,s2):rest)
+
+-- | Returns the height of the String in pixels as it will be rendered
+textHeight :: UITexty s => s -> Int
+textHeight s = go (unwrapUITexty s) where
+  go [] = textHeight' defaultFont ""
+  go lst = maximum $ map (\(_,f,str) -> textHeight' f str) lst
+
+-- | The Wrap Setting is used to determine how to split up a long piece 
+--  of text.
+data WrapSetting = NoWrap | CharWrap | WordWrap
+  deriving (Eq, Show)
+
+-- | Turn the given String into a list of Strings.  If the wrap setting 
+--  is NoWrap, then this is basically just the lines function.  If it 
+--  is CharWrap or WordWrap, then no string in the list will be wider 
+--  than the width of the bounding box.  The returned list of points 
+--  indicate each Point where a line should be drawn.  Note that this 
+--  list may not be the same length as the list of strings.
+--
+--  Typically, this will be used in conjunction with zip and textLines 
+--  to produce text graphics.
+prepText :: (UITexty s)
+         => WrapSetting -- ^ Whether we prefer newer or older text
+         -> Double      -- ^ Line spacing
+         -> Rect        -- ^ Bounding Box
+         -> s           -- ^ The text to print (which is allowed to have new lines)
+         -> ([Point], [UIText])
+prepText wrap spacing ((x,y),(w,h)) s = (pts, outStrs) where
+  lineHeight = round (fromIntegral (textHeight s) * spacing)
+  numLines = h `div` lineHeight
+  pts = zip (replicate numLines x) [y, y+lineHeight..]
+  outStrs = concatMap (wrapText wrap w) (uitextLines' $ toUIText s)
+
+-- | wrapText takes a wrap setting, a width, and a string, and turns 
+--  it into a list of strings representing each wrapped line.  Strings 
+--  are assumed to have no line breaks in them.  Calling unlines on the 
+--  output will create a String that is wrapped.
+wrapText :: UITexty s => WrapSetting -> Int -> s -> [UIText]
+wrapText NoWrap _ s = [toUIText s]
+wrapText CharWrap i s = if isEmptyUIText $ toUIText s
+    then []
+    else let (t,d) = textWithinPixels i s in t:wrapText CharWrap i d
+wrapText WordWrap i s = f i emptyUIText (uitextWords' $ toUIText s) where
+  f :: Int -> UIText -> [UIText] -> [UIText]
+  f _ sofar [] = if isEmptyUIText sofar then [] else [sofar]
+  f j sofar (w:ws) = case isEmptyUIText sofar of
+    True  -> if textWidth w > i 
+             then let (t,d) = textWithinPixels i w in t:f i emptyUIText (d:ws)
+             else f (i-textWidth w) w ws
+    False -> if textWidth w > j 
+             then sofar:f i emptyUIText (w:ws)
+             else f (j-textWidth w) (appendUIText sofar w) ws
+
+-- | The common String 'words' function applicable to UIText.
+uitextWords :: UIText -> [UIText]
+uitextWords = uitSplitter isSpace (\x -> ([], dropWhile isSpace x))
+
+-- | The common String 'lines' function applicable to UIText.
+uitextLines :: UIText -> [UIText]
+uitextLines = uitSplitter (== '\n') (\x -> ([], drop 1 x))
+
+-- | A variant of uitextLines that keeps the newline characters at the 
+--  ends of the output.
+uitextLines' :: UIText -> [UIText]
+uitextLines' = uitSplitter (== '\n') (splitAt 1)
+
+-- | A variant of uitextWords that keeps the whitespace characters at 
+--  the ends of the output.
+uitextWords' :: UIText -> [UIText]
+uitextWords' = uitSplitter isSpace (span isSpace)
+
+-- | A convenience function for writing functions like lines and words.
+uitSplitter :: (Char -> Bool) -> (String -> (String,String)) -> UIText -> [UIText]
+uitSplitter checker splitter (UIText lst) = map UIText $ uitSplitter' lst where
+  uitSplitter' [] = []
+  uitSplitter' uitext = go uitext [] where
+    go [] sofar = [reverse sofar]
+    go ((c,f,s):rest) sofar = case break checker s of
+      (_, "") -> go rest ((c,f,s):sofar)
+      (l,s') -> reverse ((c,f,l++s1):sofar) : uitSplitter' ((c,f,s2):rest)
+        where (s1,s2) = splitter s'
+
+
+-- | Returns the text height of a String rendered in the given bitmap font.
+textHeight' :: BitmapFont -> String -> Int
+textHeight' f _ = getFontHeight f
+
+-- | Returns the text width of a String rendered in the given bitmap font.
+textWidth' :: BitmapFont -> String -> Int
+textWidth' f str = foldl' (\acc c -> acc + (getFontArray f ! c)) 0 str
+
+-- | Splits a String based on what can fit within the given number of 
+--  pixels (the fst of the result) and what's left over (the snd).
+textWithinPixels' :: BitmapFont -> Int -> String -> (String, String)
+textWithinPixels' f i str = go f i str "" where
+  go _ _ [] sofar = (reverse sofar, "")
+  go f i (c:s) sofar = let i' = i - (getFontArray f ! c)
+    in if i' >= 0 then go f i' s (c:sofar) else (reverse sofar, c:s)
+
+-- | Returns the font height for a given font.
+getFontHeight :: BitmapFont -> Int
+getFontHeight Fixed8By13    = 14
+getFontHeight Fixed9By15    = 16
+getFontHeight TimesRoman10  = 14
+getFontHeight TimesRoman24  = 29
+getFontHeight Helvetica10   = 14
+getFontHeight Helvetica12   = 16
+getFontHeight Helvetica18   = 23
+--getFontHeight Roman         = 153
+--getFontHeight MonoRoman     = 153
+
+-- | Returns the font array for a given font.
+getFontArray :: BitmapFont -> Array Char Int
+getFontArray Fixed8By13     = fixed8By13
+getFontArray Fixed9By15     = fixed9By15
+getFontArray TimesRoman10   = timesRoman10
+getFontArray TimesRoman24   = timesRoman24
+getFontArray Helvetica10    = helvetica10
+getFontArray Helvetica12    = helvetica12
+getFontArray Helvetica18    = helvetica18
+--getFontArray Roman          = roman
+--getFontArray MonoRoman      = monoRoman
+
+
+
+-- | Makes Char width arrays for fonts.
+makeCharArray :: [(Char, Int)] -> Array Char Int
+makeCharArray = array (toEnum 0 :: Char, toEnum 255 :: Char)
+
+fixed8By13, fixed9By15, timesRoman10, timesRoman24, helvetica10, helvetica12, helvetica18, roman, monoRoman :: Array Char Int
+fixed8By13   = makeCharArray [('\NUL',0),('\SOH',8),('\STX',8),('\ETX',8),('\EOT',8),('\ENQ',8),('\ACK',8),('\a',8),('\b',8),('\t',8),('\n',0),('\v',8),('\f',8),('\r',8),('\SO',8),('\SI',8),('\DLE',8),('\DC1',8),('\DC2',8),('\DC3',8),('\DC4',8),('\NAK',8),('\SYN',8),('\ETB',8),('\CAN',8),('\EM',8),('\SUB',8),('\ESC',8),('\FS',8),('\GS',8),('\RS',8),('\US',8),(' ',8),('!',8),('"',8),('#',8),('$',8),('%',8),('&',8),('\'',8),('(',8),(')',8),('*',8),('+',8),(',',8),('-',8),('.',8),('/',8),('0',8),('1',8),('2',8),('3',8),('4',8),('5',8),('6',8),('7',8),('8',8),('9',8),(':',8),(';',8),('<',8),('=',8),('>',8),('?',8),('@',8),('A',8),('B',8),('C',8),('D',8),('E',8),('F',8),('G',8),('H',8),('I',8),('J',8),('K',8),('L',8),('M',8),('N',8),('O',8),('P',8),('Q',8),('R',8),('S',8),('T',8),('U',8),('V',8),('W',8),('X',8),('Y',8),('Z',8),('[',8),('\\',8),(']',8),('^',8),('_',8),('`',8),('a',8),('b',8),('c',8),('d',8),('e',8),('f',8),('g',8),('h',8),('i',8),('j',8),('k',8),('l',8),('m',8),('n',8),('o',8),('p',8),('q',8),('r',8),('s',8),('t',8),('u',8),('v',8),('w',8),('x',8),('y',8),('z',8),('{',8),('|',8),('}',8),('~',8),('\DEL',8),('\128',0),('\129',0),('\130',0),('\131',0),('\132',0),('\133',0),('\134',0),('\135',0),('\136',0),('\137',0),('\138',0),('\139',0),('\140',0),('\141',0),('\142',0),('\143',0),('\144',0),('\145',0),('\146',0),('\147',0),('\148',0),('\149',0),('\150',0),('\151',0),('\152',0),('\153',0),('\154',0),('\155',0),('\156',0),('\157',0),('\158',0),('\159',0),('\160',8),('\161',8),('\162',8),('\163',8),('\164',0),('\165',8),('\166',0),('\167',0),('\168',0),('\169',0),('\170',8),('\171',8),('\172',8),('\173',0),('\174',0),('\175',0),('\176',8),('\177',8),('\178',8),('\179',0),('\180',0),('\181',8),('\182',0),('\183',8),('\184',0),('\185',0),('\186',8),('\187',8),('\188',8),('\189',8),('\190',0),('\191',8),('\192',0),('\193',0),('\194',0),('\195',0),('\196',8),('\197',8),('\198',8),('\199',8),('\200',0),('\201',8),('\202',0),('\203',0),('\204',0),('\205',0),('\206',0),('\207',0),('\208',0),('\209',8),('\210',0),('\211',0),('\212',0),('\213',0),('\214',8),('\215',0),('\216',0),('\217',0),('\218',0),('\219',0),('\220',8),('\221',0),('\222',0),('\223',8),('\224',8),('\225',8),('\226',8),('\227',0),('\228',8),('\229',8),('\230',8),('\231',8),('\232',8),('\233',8),('\234',8),('\235',8),('\236',8),('\237',8),('\238',8),('\239',8),('\240',0),('\241',8),('\242',8),('\243',8),('\244',8),('\245',0),('\246',8),('\247',8),('\248',0),('\249',8),('\250',8),('\251',8),('\252',8),('\253',0),('\254',0),('\255',8)]
+fixed9By15   = makeCharArray [('\NUL',0),('\SOH',9),('\STX',9),('\ETX',9),('\EOT',9),('\ENQ',9),('\ACK',9),('\a',9),('\b',9),('\t',9),('\n',0),('\v',9),('\f',9),('\r',9),('\SO',9),('\SI',9),('\DLE',9),('\DC1',9),('\DC2',9),('\DC3',9),('\DC4',9),('\NAK',9),('\SYN',9),('\ETB',9),('\CAN',9),('\EM',9),('\SUB',9),('\ESC',9),('\FS',9),('\GS',9),('\RS',9),('\US',9),(' ',9),('!',9),('"',9),('#',9),('$',9),('%',9),('&',9),('\'',9),('(',9),(')',9),('*',9),('+',9),(',',9),('-',9),('.',9),('/',9),('0',9),('1',9),('2',9),('3',9),('4',9),('5',9),('6',9),('7',9),('8',9),('9',9),(':',9),(';',9),('<',9),('=',9),('>',9),('?',9),('@',9),('A',9),('B',9),('C',9),('D',9),('E',9),('F',9),('G',9),('H',9),('I',9),('J',9),('K',9),('L',9),('M',9),('N',9),('O',9),('P',9),('Q',9),('R',9),('S',9),('T',9),('U',9),('V',9),('W',9),('X',9),('Y',9),('Z',9),('[',9),('\\',9),(']',9),('^',9),('_',9),('`',9),('a',9),('b',9),('c',9),('d',9),('e',9),('f',9),('g',9),('h',9),('i',9),('j',9),('k',9),('l',9),('m',9),('n',9),('o',9),('p',9),('q',9),('r',9),('s',9),('t',9),('u',9),('v',9),('w',9),('x',9),('y',9),('z',9),('{',9),('|',9),('}',9),('~',9),('\DEL',9),('\128',0),('\129',0),('\130',0),('\131',0),('\132',0),('\133',0),('\134',0),('\135',0),('\136',0),('\137',0),('\138',0),('\139',0),('\140',0),('\141',0),('\142',0),('\143',0),('\144',0),('\145',0),('\146',0),('\147',0),('\148',0),('\149',0),('\150',0),('\151',0),('\152',0),('\153',0),('\154',0),('\155',0),('\156',0),('\157',0),('\158',0),('\159',0),('\160',9),('\161',9),('\162',9),('\163',9),('\164',0),('\165',9),('\166',0),('\167',0),('\168',0),('\169',0),('\170',9),('\171',9),('\172',9),('\173',0),('\174',0),('\175',0),('\176',9),('\177',9),('\178',9),('\179',0),('\180',0),('\181',9),('\182',0),('\183',9),('\184',0),('\185',0),('\186',9),('\187',9),('\188',9),('\189',9),('\190',0),('\191',9),('\192',0),('\193',0),('\194',0),('\195',0),('\196',9),('\197',9),('\198',9),('\199',9),('\200',0),('\201',9),('\202',0),('\203',0),('\204',0),('\205',0),('\206',0),('\207',0),('\208',0),('\209',9),('\210',0),('\211',0),('\212',0),('\213',0),('\214',9),('\215',0),('\216',0),('\217',0),('\218',0),('\219',0),('\220',9),('\221',0),('\222',0),('\223',9),('\224',9),('\225',9),('\226',9),('\227',0),('\228',9),('\229',9),('\230',9),('\231',9),('\232',9),('\233',9),('\234',9),('\235',9),('\236',9),('\237',9),('\238',9),('\239',9),('\240',0),('\241',9),('\242',9),('\243',9),('\244',9),('\245',0),('\246',9),('\247',9),('\248',0),('\249',9),('\250',9),('\251',9),('\252',9),('\253',0),('\254',0),('\255',9)]
+timesRoman10 = makeCharArray [('\NUL',0),('\SOH',2),('\STX',2),('\ETX',2),('\EOT',2),('\ENQ',2),('\ACK',2),('\a',2),('\b',2),('\t',2),('\n',0),('\v',2),('\f',2),('\r',2),('\SO',2),('\SI',2),('\DLE',2),('\DC1',2),('\DC2',2),('\DC3',2),('\DC4',2),('\NAK',2),('\SYN',2),('\ETB',2),('\CAN',2),('\EM',2),('\SUB',2),('\ESC',2),('\FS',2),('\GS',2),('\RS',2),('\US',2),(' ',2),('!',3),('"',4),('#',5),('$',5),('%',8),('&',8),('\'',3),('(',4),(')',4),('*',5),('+',6),(',',3),('-',7),('.',3),('/',3),('0',5),('1',5),('2',5),('3',5),('4',5),('5',5),('6',5),('7',5),('8',5),('9',5),(':',3),(';',3),('<',5),('=',6),('>',5),('?',4),('@',9),('A',8),('B',6),('C',7),('D',7),('E',6),('F',6),('G',7),('H',8),('I',4),('J',4),('K',7),('L',6),('M',10),('N',8),('O',7),('P',6),('Q',7),('R',7),('S',5),('T',6),('U',8),('V',8),('W',10),('X',8),('Y',8),('Z',6),('[',3),('\\',3),(']',3),('^',5),('_',5),('`',3),('a',4),('b',5),('c',4),('d',5),('e',4),('f',4),('g',5),('h',5),('i',3),('j',3),('k',5),('l',4),('m',8),('n',5),('o',5),('p',5),('q',5),('r',4),('s',4),('t',4),('u',5),('v',5),('w',8),('x',6),('y',5),('z',5),('{',4),('|',2),('}',4),('~',7),('\DEL',2),('\128',0),('\129',0),('\130',0),('\131',0),('\132',0),('\133',0),('\134',0),('\135',0),('\136',0),('\137',0),('\138',0),('\139',0),('\140',0),('\141',0),('\142',0),('\143',0),('\144',0),('\145',0),('\146',0),('\147',0),('\148',0),('\149',0),('\150',0),('\151',0),('\152',0),('\153',0),('\154',0),('\155',0),('\156',0),('\157',0),('\158',0),('\159',0),('\160',5),('\161',4),('\162',2),('\163',2),('\164',0),('\165',2),('\166',0),('\167',0),('\168',0),('\169',0),('\170',2),('\171',9),('\172',4),('\173',0),('\174',0),('\175',0),('\176',5),('\177',5),('\178',5),('\179',0),('\180',0),('\181',6),('\182',0),('\183',5),('\184',0),('\185',0),('\186',5),('\187',4),('\188',7),('\189',5),('\190',0),('\191',5),('\192',0),('\193',0),('\194',0),('\195',0),('\196',2),('\197',2),('\198',2),('\199',2),('\200',0),('\201',2),('\202',0),('\203',0),('\204',0),('\205',0),('\206',0),('\207',0),('\208',0),('\209',5),('\210',0),('\211',0),('\212',0),('\213',0),('\214',2),('\215',0),('\216',0),('\217',0),('\218',0),('\219',0),('\220',2),('\221',0),('\222',0),('\223',4),('\224',2),('\225',2),('\226',2),('\227',0),('\228',2),('\229',2),('\230',2),('\231',2),('\232',2),('\233',2),('\234',2),('\235',2),('\236',2),('\237',3),('\238',2),('\239',2),('\240',0),('\241',5),('\242',2),('\243',5),('\244',2),('\245',0),('\246',2),('\247',5),('\248',0),('\249',2),('\250',5),('\251',2),('\252',2),('\253',0),('\254',0),('\255',2)]
+timesRoman24 = makeCharArray [('\NUL',0),('\SOH',6),('\STX',6),('\ETX',6),('\EOT',6),('\ENQ',6),('\ACK',6),('\a',6),('\b',6),('\t',6),('\n',0),('\v',6),('\f',6),('\r',6),('\SO',6),('\SI',6),('\DLE',6),('\DC1',6),('\DC2',6),('\DC3',6),('\DC4',6),('\NAK',6),('\SYN',6),('\ETB',6),('\CAN',6),('\EM',6),('\SUB',6),('\ESC',6),('\FS',6),('\GS',6),('\RS',6),('\US',6),(' ',6),('!',8),('"',10),('#',13),('$',12),('%',19),('&',18),('\'',8),('(',8),(')',8),('*',12),('+',14),(',',7),('-',14),('.',6),('/',7),('0',12),('1',12),('2',12),('3',12),('4',12),('5',12),('6',12),('7',12),('8',12),('9',12),(':',6),(';',7),('<',13),('=',14),('>',13),('?',11),('@',22),('A',17),('B',16),('C',16),('D',17),('E',15),('F',14),('G',18),('H',19),('I',8),('J',11),('K',17),('L',14),('M',22),('N',18),('O',18),('P',15),('Q',18),('R',16),('S',13),('T',16),('U',18),('V',17),('W',23),('X',18),('Y',16),('Z',15),('[',8),('\\',7),(']',8),('^',11),('_',13),('`',7),('a',11),('b',12),('c',11),('d',12),('e',11),('f',7),('g',12),('h',13),('i',6),('j',6),('k',12),('l',6),('m',20),('n',13),('o',12),('p',12),('q',12),('r',8),('s',10),('t',7),('u',13),('v',11),('w',17),('x',13),('y',11),('z',10),('{',10),('|',6),('}',10),('~',13),('\DEL',6),('\128',0),('\129',0),('\130',0),('\131',0),('\132',0),('\133',0),('\134',0),('\135',0),('\136',0),('\137',0),('\138',0),('\139',0),('\140',0),('\141',0),('\142',0),('\143',0),('\144',0),('\145',0),('\146',0),('\147',0),('\148',0),('\149',0),('\150',0),('\151',0),('\152',0),('\153',0),('\154',0),('\155',0),('\156',0),('\157',0),('\158',0),('\159',0),('\160',11),('\161',9),('\162',6),('\163',6),('\164',0),('\165',6),('\166',0),('\167',0),('\168',0),('\169',0),('\170',6),('\171',19),('\172',8),('\173',0),('\174',0),('\175',0),('\176',12),('\177',13),('\178',11),('\179',0),('\180',0),('\181',16),('\182',0),('\183',13),('\184',0),('\185',0),('\186',12),('\187',8),('\188',14),('\189',13),('\190',0),('\191',8),('\192',0),('\193',0),('\194',0),('\195',0),('\196',6),('\197',6),('\198',6),('\199',6),('\200',0),('\201',6),('\202',0),('\203',0),('\204',0),('\205',0),('\206',0),('\207',0),('\208',0),('\209',14),('\210',0),('\211',0),('\212',0),('\213',0),('\214',6),('\215',0),('\216',0),('\217',0),('\218',0),('\219',0),('\220',6),('\221',0),('\222',0),('\223',11),('\224',6),('\225',6),('\226',6),('\227',0),('\228',6),('\229',6),('\230',6),('\231',6),('\232',6),('\233',6),('\234',6),('\235',6),('\236',6),('\237',8),('\238',6),('\239',6),('\240',0),('\241',13),('\242',6),('\243',12),('\244',6),('\245',0),('\246',6),('\247',12),('\248',0),('\249',6),('\250',12),('\251',6),('\252',6),('\253',0),('\254',0),('\255',6)]
+helvetica10  = makeCharArray [('\NUL',0),('\SOH',3),('\STX',3),('\ETX',3),('\EOT',3),('\ENQ',3),('\ACK',3),('\a',3),('\b',3),('\t',3),('\n',0),('\v',3),('\f',3),('\r',3),('\SO',3),('\SI',3),('\DLE',3),('\DC1',3),('\DC2',3),('\DC3',3),('\DC4',3),('\NAK',3),('\SYN',3),('\ETB',3),('\CAN',3),('\EM',3),('\SUB',3),('\ESC',3),('\FS',3),('\GS',3),('\RS',3),('\US',3),(' ',3),('!',3),('"',4),('#',6),('$',6),('%',9),('&',8),('\'',3),('(',4),(')',4),('*',4),('+',6),(',',3),('-',7),('.',3),('/',3),('0',6),('1',6),('2',6),('3',6),('4',6),('5',6),('6',6),('7',6),('8',6),('9',6),(':',3),(';',3),('<',6),('=',5),('>',6),('?',6),('@',11),('A',7),('B',7),('C',8),('D',8),('E',7),('F',6),('G',8),('H',8),('I',3),('J',5),('K',7),('L',6),('M',9),('N',8),('O',8),('P',7),('Q',8),('R',7),('S',7),('T',5),('U',8),('V',7),('W',9),('X',7),('Y',7),('Z',7),('[',3),('\\',3),(']',3),('^',6),('_',6),('`',3),('a',5),('b',6),('c',5),('d',6),('e',5),('f',4),('g',6),('h',6),('i',2),('j',2),('k',5),('l',2),('m',8),('n',6),('o',6),('p',6),('q',6),('r',4),('s',5),('t',4),('u',5),('v',6),('w',8),('x',6),('y',5),('z',5),('{',3),('|',3),('}',3),('~',7),('\DEL',3),('\128',0),('\129',0),('\130',0),('\131',0),('\132',0),('\133',0),('\134',0),('\135',0),('\136',0),('\137',0),('\138',0),('\139',0),('\140',0),('\141',0),('\142',0),('\143',0),('\144',0),('\145',0),('\146',0),('\147',0),('\148',0),('\149',0),('\150',0),('\151',0),('\152',0),('\153',0),('\154',0),('\155',0),('\156',0),('\157',0),('\158',0),('\159',0),('\160',5),('\161',4),('\162',3),('\163',3),('\164',0),('\165',3),('\166',0),('\167',0),('\168',0),('\169',0),('\170',3),('\171',9),('\172',4),('\173',0),('\174',0),('\175',0),('\176',6),('\177',5),('\178',5),('\179',0),('\180',0),('\181',8),('\182',0),('\183',5),('\184',0),('\185',0),('\186',6),('\187',3),('\188',7),('\189',6),('\190',0),('\191',3),('\192',0),('\193',0),('\194',0),('\195',0),('\196',3),('\197',3),('\198',3),('\199',3),('\200',0),('\201',3),('\202',0),('\203',0),('\204',0),('\205',0),('\206',0),('\207',0),('\208',0),('\209',6),('\210',0),('\211',0),('\212',0),('\213',0),('\214',3),('\215',0),('\216',0),('\217',0),('\218',0),('\219',0),('\220',3),('\221',0),('\222',0),('\223',5),('\224',3),('\225',3),('\226',3),('\227',0),('\228',3),('\229',3),('\230',3),('\231',3),('\232',3),('\233',3),('\234',3),('\235',3),('\236',3),('\237',3),('\238',3),('\239',3),('\240',0),('\241',5),('\242',3),('\243',6),('\244',3),('\245',0),('\246',3),('\247',6),('\248',0),('\249',3),('\250',6),('\251',3),('\252',3),('\253',0),('\254',0),('\255',3)]
+helvetica12  = makeCharArray [('\NUL',0),('\SOH',4),('\STX',4),('\ETX',4),('\EOT',4),('\ENQ',4),('\ACK',4),('\a',4),('\b',4),('\t',4),('\n',0),('\v',4),('\f',4),('\r',4),('\SO',4),('\SI',4),('\DLE',4),('\DC1',4),('\DC2',4),('\DC3',4),('\DC4',4),('\NAK',4),('\SYN',4),('\ETB',4),('\CAN',4),('\EM',4),('\SUB',4),('\ESC',4),('\FS',4),('\GS',4),('\RS',4),('\US',4),(' ',4),('!',3),('"',5),('#',7),('$',7),('%',11),('&',9),('\'',3),('(',4),(')',4),('*',5),('+',7),(',',4),('-',8),('.',3),('/',4),('0',7),('1',7),('2',7),('3',7),('4',7),('5',7),('6',7),('7',7),('8',7),('9',7),(':',3),(';',3),('<',7),('=',7),('>',7),('?',7),('@',12),('A',9),('B',8),('C',9),('D',9),('E',8),('F',8),('G',9),('H',9),('I',3),('J',7),('K',8),('L',7),('M',11),('N',9),('O',10),('P',8),('Q',10),('R',8),('S',8),('T',7),('U',8),('V',9),('W',11),('X',9),('Y',9),('Z',9),('[',3),('\\',4),(']',3),('^',6),('_',7),('`',3),('a',7),('b',7),('c',7),('d',7),('e',7),('f',3),('g',7),('h',7),('i',3),('j',3),('k',6),('l',3),('m',9),('n',7),('o',7),('p',7),('q',7),('r',4),('s',6),('t',3),('u',7),('v',7),('w',9),('x',6),('y',7),('z',6),('{',4),('|',3),('}',4),('~',7),('\DEL',4),('\128',0),('\129',0),('\130',0),('\131',0),('\132',0),('\133',0),('\134',0),('\135',0),('\136',0),('\137',0),('\138',0),('\139',0),('\140',0),('\141',0),('\142',0),('\143',0),('\144',0),('\145',0),('\146',0),('\147',0),('\148',0),('\149',0),('\150',0),('\151',0),('\152',0),('\153',0),('\154',0),('\155',0),('\156',0),('\157',0),('\158',0),('\159',0),('\160',7),('\161',5),('\162',4),('\163',4),('\164',0),('\165',4),('\166',0),('\167',0),('\168',0),('\169',0),('\170',3),('\171',11),('\172',5),('\173',0),('\174',0),('\175',0),('\176',7),('\177',7),('\178',7),('\179',0),('\180',0),('\181',11),('\182',0),('\183',7),('\184',0),('\185',0),('\186',6),('\187',4),('\188',8),('\189',7),('\190',0),('\191',3),('\192',0),('\193',0),('\194',0),('\195',0),('\196',4),('\197',4),('\198',4),('\199',4),('\200',0),('\201',4),('\202',0),('\203',0),('\204',0),('\205',0),('\206',0),('\207',0),('\208',0),('\209',7),('\210',0),('\211',0),('\212',0),('\213',0),('\214',4),('\215',0),('\216',0),('\217',0),('\218',0),('\219',0),('\220',4),('\221',0),('\222',0),('\223',7),('\224',4),('\225',4),('\226',4),('\227',0),('\228',4),('\229',4),('\230',4),('\231',4),('\232',4),('\233',4),('\234',4),('\235',4),('\236',4),('\237',3),('\238',4),('\239',4),('\240',0),('\241',7),('\242',4),('\243',7),('\244',4),('\245',0),('\246',4),('\247',7),('\248',0),('\249',4),('\250',7),('\251',4),('\252',4),('\253',0),('\254',0),('\255',4)]
+helvetica18  = makeCharArray [('\NUL',0),('\SOH',5),('\STX',5),('\ETX',5),('\EOT',5),('\ENQ',5),('\ACK',5),('\a',5),('\b',5),('\t',5),('\n',0),('\v',5),('\f',5),('\r',5),('\SO',5),('\SI',5),('\DLE',5),('\DC1',5),('\DC2',5),('\DC3',5),('\DC4',5),('\NAK',5),('\SYN',5),('\ETB',5),('\CAN',5),('\EM',5),('\SUB',5),('\ESC',5),('\FS',5),('\GS',5),('\RS',5),('\US',5),(' ',5),('!',6),('"',5),('#',10),('$',10),('%',16),('&',13),('\'',4),('(',6),(')',6),('*',7),('+',10),(',',5),('-',11),('.',5),('/',5),('0',10),('1',10),('2',10),('3',10),('4',10),('5',10),('6',10),('7',10),('8',10),('9',10),(':',5),(';',5),('<',10),('=',11),('>',10),('?',10),('@',18),('A',12),('B',13),('C',14),('D',13),('E',11),('F',11),('G',14),('H',13),('I',6),('J',10),('K',13),('L',10),('M',16),('N',13),('O',15),('P',12),('Q',15),('R',12),('S',13),('T',12),('U',13),('V',14),('W',18),('X',13),('Y',14),('Z',12),('[',5),('\\',5),(']',5),('^',9),('_',10),('`',4),('a',9),('b',11),('c',10),('d',11),('e',10),('f',6),('g',11),('h',10),('i',4),('j',4),('k',9),('l',4),('m',14),('n',10),('o',11),('p',11),('q',11),('r',6),('s',9),('t',6),('u',10),('v',10),('w',14),('x',10),('y',10),('z',9),('{',6),('|',4),('}',6),('~',10),('\DEL',5),('\128',0),('\129',0),('\130',0),('\131',0),('\132',0),('\133',0),('\134',0),('\135',0),('\136',0),('\137',0),('\138',0),('\139',0),('\140',0),('\141',0),('\142',0),('\143',0),('\144',0),('\145',0),('\146',0),('\147',0),('\148',0),('\149',0),('\150',0),('\151',0),('\152',0),('\153',0),('\154',0),('\155',0),('\156',0),('\157',0),('\158',0),('\159',0),('\160',10),('\161',7),('\162',5),('\163',5),('\164',0),('\165',5),('\166',0),('\167',0),('\168',0),('\169',0),('\170',4),('\171',14),('\172',7),('\173',0),('\174',0),('\175',0),('\176',11),('\177',10),('\178',10),('\179',0),('\180',0),('\181',15),('\182',0),('\183',10),('\184',0),('\185',0),('\186',10),('\187',5),('\188',11),('\189',9),('\190',0),('\191',6),('\192',0),('\193',0),('\194',0),('\195',0),('\196',5),('\197',5),('\198',5),('\199',5),('\200',0),('\201',5),('\202',0),('\203',0),('\204',0),('\205',0),('\206',0),('\207',0),('\208',0),('\209',10),('\210',0),('\211',0),('\212',0),('\213',0),('\214',5),('\215',0),('\216',0),('\217',0),('\218',0),('\219',0),('\220',5),('\221',0),('\222',0),('\223',9),('\224',5),('\225',5),('\226',5),('\227',0),('\228',5),('\229',5),('\230',5),('\231',5),('\232',5),('\233',5),('\234',5),('\235',5),('\236',5),('\237',6),('\238',5),('\239',5),('\240',0),('\241',10),('\242',5),('\243',10),('\244',5),('\245',0),('\246',5),('\247',11),('\248',0),('\249',5),('\250',10),('\251',5),('\252',5),('\253',0),('\254',0),('\255',5)]
+roman        = makeCharArray [('\NUL',0),('\SOH',0),('\STX',0),('\ETX',0),('\EOT',0),('\ENQ',0),('\ACK',0),('\a',0),('\b',0),('\t',0),('\n',0),('\v',0),('\f',0),('\r',0),('\SO',0),('\SI',0),('\DLE',0),('\DC1',0),('\DC2',0),('\DC3',0),('\DC4',0),('\NAK',0),('\SYN',0),('\ETB',0),('\CAN',0),('\EM',0),('\SUB',0),('\ESC',0),('\FS',0),('\GS',0),('\RS',0),('\US',0),(' ',105),('!',27),('"',51),('#',79),('$',76),('%',97),('&',102),('\'',14),('(',47),(')',48),('*',59),('+',97),(',',26),('-',101),('.',26),('/',82),('0',77),('1',67),('2',78),('3',77),('4',80),('5',78),('6',74),('7',77),('8',78),('9',74),(':',26),(';',26),('<',82),('=',97),('>',82),('?',74),('@',74),('A',80),('B',84),('C',84),('D',85),('E',78),('F',79),('G',90),('H',89),('I',21),('J',60),('K',79),('L',71),('M',97),('N',89),('O',89),('P',86),('Q',88),('R',82),('S',81),('T',72),('U',89),('V',82),('W',101),('X',72),('Y',80),('Z',74),('[',46),('\\',78),(']',46),('^',90),('_',104),('`',84),('a',67),('b',70),('c',69),('d',70),('e',69),('f',39),('g',71),('h',71),('i',29),('j',36),('k',63),('l',19),('m',124),('n',71),('o',72),('p',71),('q',71),('r',49),('s',62),('t',39),('u',71),('v',61),('w',80),('x',56),('y',66),('z',62),('{',42),('|',24),('}',41),('~',91),('\DEL',67),('\128',0),('\129',0),('\130',0),('\131',0),('\132',0),('\133',0),('\134',0),('\135',0),('\136',0),('\137',0),('\138',0),('\139',0),('\140',0),('\141',0),('\142',0),('\143',0),('\144',0),('\145',0),('\146',0),('\147',0),('\148',0),('\149',0),('\150',0),('\151',0),('\152',0),('\153',0),('\154',0),('\155',0),('\156',0),('\157',0),('\158',0),('\159',0),('\160',0),('\161',0),('\162',0),('\163',0),('\164',0),('\165',0),('\166',0),('\167',0),('\168',0),('\169',0),('\170',0),('\171',0),('\172',0),('\173',0),('\174',0),('\175',0),('\176',0),('\177',0),('\178',0),('\179',0),('\180',0),('\181',0),('\182',0),('\183',0),('\184',0),('\185',0),('\186',0),('\187',0),('\188',0),('\189',0),('\190',0),('\191',0),('\192',0),('\193',0),('\194',0),('\195',0),('\196',0),('\197',0),('\198',0),('\199',0),('\200',0),('\201',0),('\202',0),('\203',0),('\204',0),('\205',0),('\206',0),('\207',0),('\208',0),('\209',0),('\210',0),('\211',0),('\212',0),('\213',0),('\214',0),('\215',0),('\216',0),('\217',0),('\218',0),('\219',0),('\220',0),('\221',0),('\222',0),('\223',0),('\224',0),('\225',0),('\226',0),('\227',0),('\228',0),('\229',0),('\230',0),('\231',0),('\232',0),('\233',0),('\234',0),('\235',0),('\236',0),('\237',0),('\238',0),('\239',0),('\240',0),('\241',0),('\242',0),('\243',0),('\244',0),('\245',0),('\246',0),('\247',0),('\248',0),('\249',0),('\250',0),('\251',0),('\252',0),('\253',0),('\254',0),('\255',0)]
+monoRoman    = makeCharArray [('\NUL',0),('\SOH',0),('\STX',0),('\ETX',0),('\EOT',0),('\ENQ',0),('\ACK',0),('\a',0),('\b',0),('\t',0),('\n',0),('\v',0),('\f',0),('\r',0),('\SO',0),('\SI',0),('\DLE',0),('\DC1',0),('\DC2',0),('\DC3',0),('\DC4',0),('\NAK',0),('\SYN',0),('\ETB',0),('\CAN',0),('\EM',0),('\SUB',0),('\ESC',0),('\FS',0),('\GS',0),('\RS',0),('\US',0),(' ',105),('!',105),('"',105),('#',105),('$',105),('%',105),('&',105),('\'',105),('(',105),(')',105),('*',105),('+',105),(',',105),('-',105),('.',105),('/',105),('0',105),('1',105),('2',105),('3',105),('4',105),('5',105),('6',105),('7',105),('8',105),('9',105),(':',105),(';',105),('<',105),('=',105),('>',105),('?',105),('@',105),('A',105),('B',105),('C',105),('D',105),('E',105),('F',105),('G',105),('H',105),('I',105),('J',105),('K',105),('L',105),('M',105),('N',105),('O',105),('P',105),('Q',105),('R',105),('S',105),('T',105),('U',105),('V',105),('W',105),('X',105),('Y',105),('Z',105),('[',105),('\\',105),(']',105),('^',105),('_',105),('`',105),('a',105),('b',105),('c',105),('d',105),('e',105),('f',105),('g',105),('h',105),('i',105),('j',105),('k',105),('l',105),('m',105),('n',105),('o',105),('p',105),('q',105),('r',105),('s',105),('t',105),('u',105),('v',105),('w',105),('x',105),('y',105),('z',105),('{',105),('|',105),('}',105),('~',105),('\DEL',105),('\128',0),('\129',0),('\130',0),('\131',0),('\132',0),('\133',0),('\134',0),('\135',0),('\136',0),('\137',0),('\138',0),('\139',0),('\140',0),('\141',0),('\142',0),('\143',0),('\144',0),('\145',0),('\146',0),('\147',0),('\148',0),('\149',0),('\150',0),('\151',0),('\152',0),('\153',0),('\154',0),('\155',0),('\156',0),('\157',0),('\158',0),('\159',0),('\160',0),('\161',0),('\162',0),('\163',0),('\164',0),('\165',0),('\166',0),('\167',0),('\168',0),('\169',0),('\170',0),('\171',0),('\172',0),('\173',0),('\174',0),('\175',0),('\176',0),('\177',0),('\178',0),('\179',0),('\180',0),('\181',0),('\182',0),('\183',0),('\184',0),('\185',0),('\186',0),('\187',0),('\188',0),('\189',0),('\190',0),('\191',0),('\192',0),('\193',0),('\194',0),('\195',0),('\196',0),('\197',0),('\198',0),('\199',0),('\200',0),('\201',0),('\202',0),('\203',0),('\204',0),('\205',0),('\206',0),('\207',0),('\208',0),('\209',0),('\210',0),('\211',0),('\212',0),('\213',0),('\214',0),('\215',0),('\216',0),('\217',0),('\218',0),('\219',0),('\220',0),('\221',0),('\222',0),('\223',0),('\224',0),('\225',0),('\226',0),('\227',0),('\228',0),('\229',0),('\230',0),('\231',0),('\232',0),('\233',0),('\234',0),('\235',0),('\236',0),('\237',0),('\238',0),('\239',0),('\240',0),('\241',0),('\242',0),('\243',0),('\244',0),('\245',0),('\246',0),('\247',0),('\248',0),('\249',0),('\250',0),('\251',0),('\252',0),('\253',0),('\254',0),('\255',0)]
+ FRP/UISF/Graphics/Types.hs view
@@ -0,0 +1,32 @@+-----------------------------------------------------------------------------
+-- |
+-- Module      :  FRP.UISF.Graphics.Types
+-- Copyright   :  (c) Daniel Winograd-Cort 2015
+-- License     :  see the LICENSE file in the distribution
+--
+-- Maintainer  :  dwc@cs.yale.edu
+-- Stability   :  experimental
+
+{-# LANGUAGE BangPatterns, FlexibleInstances, TypeSynonymInstances #-}
+module FRP.UISF.Graphics.Types (
+  -- * Useful Types
+  Point, Angle, Dimension, Rect
+  ) where
+
+
+------------------------------------------------------------
+-- Useful Types
+------------------------------------------------------------
+
+-- | Point describes a point on the GUI.
+type Point = (Int, Int)
+
+-- | A dimension specifies size.
+type Dimension = (Int, Int)
+
+-- | A rectangle has a (bottom left) corner point and a dimension.
+type Rect = (Point, Dimension)
+
+-- | Angles are used when making arcs, circles, etc. or when 
+--  performing rotations.  Angles are measured in Degrees
+type Angle = Double
+ FRP/UISF/Render/GLUT.hs view
@@ -0,0 +1,441 @@+-----------------------------------------------------------------------------
+-- |
+-- Module      :  FRP.UISF.Render.GLUT
+-- Copyright   :  (c) Daniel Winograd-Cort 2015
+-- License     :  see the LICENSE file in the distribution
+--
+-- Maintainer  :  dwc@cs.yale.edu
+-- Stability   :  experimental
+
+module FRP.UISF.Render.GLUT (
+  -- $glut
+  Window,
+  WindowData (..),
+  openWindow,
+  closeWindow,
+  -- * Rendering Graphics in OpenGL
+  renderGraphicInOpenGL,
+  glutKeyToKey
+  ) where
+
+
+import Graphics.UI.GLUT hiding (Key(..), SpecialKey(..), MouseButton(..), vertex, Rect)
+import qualified Graphics.UI.GLUT as GLUT
+import qualified Graphics.Rendering.OpenGL as GL
+import Graphics.Rendering.OpenGL (($=), GLfloat)
+
+import Control.Concurrent
+import Control.Concurrent.MVar
+import Control.Concurrent.STM.TChan
+import Control.Exception (catch,IOException)
+import Control.Monad.STM (atomically)
+import Control.Monad (when)
+import Data.IORef
+import Data.List (unfoldr)
+
+import FRP.UISF.UITypes
+import FRP.UISF.Graphics
+import FRP.UISF.Graphics.Graphic (Graphic(..))
+import FRP.UISF.Graphics.Text (uitextLines)
+
+{- $glut
+This module provides the functions for UISF's direct interface with 
+GLUT and the GUI window itself.  The main function for this is 
+'openWindow', and once a window is open, almost all communication is 
+handled through the returned 'WindowData' object.  The one exception 
+to this is that one can externally close the window, 
+terminating the GUI altogether (although this requires the window 
+object, which is found in the WindowData).
+
+Note that the values in WindowData are all IO actions.  Thus, to get 
+the "current" value of the window's dimensions, one should run the 
+'windowDim' action "now".
+
+Note also that the 'Window' type is being re-exported here as it is 
+used in the 'WindowData' type.
+-}
+
+-------------------
+-- Window Functions
+-------------------
+
+-- | The WindowData object is used for communication between the 
+--  logic (UISF) and the window (GLUT).
+data WindowData = WindowData {
+  setGraphics  :: (Graphic, DirtyBit) -> IO (),
+  -- ^  This action allows a caller to set the current Graphic to display 
+  --    along with a 'DirtyBit' indicating if the Graphic needs to be 
+  --    redrawn.
+  getWindow    :: IO (Maybe Window),
+  -- ^  This action retrieves the active window.  For now, this is used 
+  --    both to check if the GUI is still running (a result of Nothing 
+  --    indicates that it is not) and to externally close the window.  
+  --    Note that if GLUT closes the window (e.g. the user clicks the 
+  --    close button), this reference will be updated to Nothing to 
+  --    prevent double closure.
+  getWindowDim :: IO Dimension,
+  -- ^  This action retrieves the window's current dimensions.  There 
+  --    is no way to set this value outside of the initial dimension 
+  --    provided by openWindow (perhaps a future feature).
+  getNextEvent :: IO UIEvent,
+  -- ^  This action retrieves the next keyboard/mouse event to be 
+  --    processed.  In the case that there is no new event, NoUIEvent 
+  --    is provided.
+  peekNextEvent :: IO UIEvent,
+  -- ^  This action peeks at the next keyboard/mouse event to be 
+  --    processed.  In the case that there is no new event, NoUIEvent 
+  --    is provided.  This was added for a potential performance boost.
+  getElapsedGUITime :: IO Double
+  -- ^  This action retrieves the number of real time seconds that have 
+  --    elapsed since the GUI began.
+}
+
+-- | This function creates the GUI window.  It takes as arguments 
+--  a default background color, a title for the window, and the initial 
+--  dimensions for the window; it produces a WindowData object to use 
+--  as communication to the window.
+--
+--  Note that the main GLUT loop is run in a separate OS thread produced 
+--  by forkOS.
+openWindow :: RGB -> String -> Dimension -> IO WindowData
+openWindow rgb title (x,y) = do
+  gRef  <- newIORef (nullGraphic, False)
+  wRef  <- newIORef Nothing
+  wdRef <- newIORef (x,y)
+  eChan <- atomically newTChan
+  continue <- newEmptyMVar
+  let w = WindowData (writeIORef gRef) (readIORef wRef)
+                     (readIORef wdRef) (nextEvent tryReadTChan eChan) 
+                     (nextEvent tryPeekTChan eChan) guiTime
+  -- REMARK: forkIO seems to work fine, but if GLUT starts misbehaving, 
+  --    this may need to change to forkOS.
+  forkIO (f gRef wRef wdRef eChan continue)
+  takeMVar continue
+  return w
+ where 
+  nextEvent r c = do
+    me <- atomically $ r c
+    case me of
+      Nothing -> return NoUIEvent
+      Just e  -> return e
+  f gRef wRef wdRef eChan continue = do
+    -- Initialize and create the window.
+    (_progName, otherArgs) <- getArgsAndInitialize
+    initialDisplayMode $= [DoubleBuffered]
+    w <- createWindow title
+    windowSize $= Size (fromIntegral x) (fromIntegral y)
+    -- Update the WindowData Window reference to point to this new window.
+    writeIORef wRef (Just w)
+    
+    -- We want the program to be able to continue when the window closes.
+    catch (actionOnWindowClose $= ContinueExecution)
+          (const (return ())::IOException->IO())
+    -- Set the default background color.
+    setBackgroundColor rgb
+    
+    -- Set up the various call back functions.
+    displayCallback $= displayCB gRef
+    idleCallback $= Just (idleCB gRef)
+    reshapeCallback $= Just (reshapeCB wdRef)
+    keyboardMouseCallback $= Just (keyboardMouseCB eChan)
+    motionCallback $= Just (motionCB eChan)
+    passiveMotionCallback $= Just (motionCB eChan)
+    catch (closeCallback $= Just (closeCB wRef))
+          (const (return ())::IOException->IO())
+    
+    -- These 4 settings are pulled from elsewhere.
+    -- They're probably good?
+    lineSmooth $= Enabled
+    blend $= Enabled
+    blendFunc $= (SrcAlpha, OneMinusSrcAlpha)
+    lineWidth $= 1.5
+    
+    -- Indicate to the main thread that the window is good to go.
+    putMVar continue ()
+    
+    -- Begin the main loop.
+    mainLoop
+
+-- | When provided with an active window, this function will close 
+--  the window.
+closeWindow :: Window -> IO ()
+closeWindow = destroyWindow
+
+-- | Set the default background color for the GUI window.
+setBackgroundColor :: RGB -> IO ()
+setBackgroundColor rgb = clearColor $= Color4 r g b 0
+  where (r',g',b') = extractRGB rgb
+        r = fromIntegral r' / 255
+        g = fromIntegral g' / 255
+        b = fromIntegral b' / 255
+
+-- | The callback to update the display.
+displayCB :: IORef (Graphic, DirtyBit) -> DisplayCallback
+displayCB ref = do 
+  (g, _) <- readIORef ref
+  loadIdentity
+  clear [ColorBuffer, StencilBuffer]
+  (Size x y) <- get windowSize
+  renderGraphicInOpenGL (fromIntegral x, fromIntegral y) g
+  swapBuffers
+  postRedisplay Nothing
+
+-- | When the GUI is idle, we should check if the dirty bit is set.  
+--  If so, we signal a redraw of the display.
+idleCB :: IORef (Graphic, DirtyBit) -> IdleCallback
+idleCB ref = do
+  db <- atomicModifyIORef ref (\(g,db) -> ((g,False),db))
+  when db $ postRedisplay Nothing
+
+-- | When the window is resized, we perform this mess to make sure 
+--  everything is drawn properly.  This model assumes no stretching 
+--  and instead forces the user to deal with exact pixel sizes.
+reshapeCB :: IORef Dimension -> ReshapeCallback
+reshapeCB wdref size@(Size w h) = do
+  writeIORef wdref (fromIntegral w, fromIntegral h)
+  viewport $= (Position 0 0, size)
+  matrixMode $= Projection
+  loadIdentity
+  ortho2D 0 (realToFrac w) (realToFrac h) 0
+  matrixMode $= Modelview 0
+  loadIdentity
+  postRedisplay Nothing
+
+-- | When a keyboard or mouse event comes in, send it to the 'WindowData' 
+--  object for external processing.  Also, update the global keyState so 
+--  that 'isKeyPressed' and the has***Modifier functions work as expected.
+keyboardMouseCB :: TChan UIEvent -> KeyboardMouseCallback
+keyboardMouseCB chan key d modifiers (Position x y) = do
+  let k = glutKeyToKey key
+      down = (d == Down)
+      p = (fromIntegral x, fromIntegral y)
+  mods <- updateKeyState k down
+  case k of
+    (Char c) -> 
+      atomically $ writeTChan chan Key{ char = c, modifiers = mods, isDown = down}
+    (SpecialKey sk) -> 
+      atomically $ writeTChan chan SKey{ skey = sk, modifiers = mods, isDown = down}
+    (MouseButton mb) -> 
+      atomically $ writeTChan chan Button{ pt = p, mbutton = mb, isDown = down}
+
+-- | When the mouse moves at all, add an event to the 'WindowData' for 
+--  external processing.
+motionCB :: TChan UIEvent -> MotionCallback
+motionCB chan (Position x y) = 
+  atomically $ writeTChan chan MouseMove{ pt = (fromIntegral x, fromIntegral y)}
+
+-- | When the window closes, update the window stored in the 'WindowData'.
+closeCB :: IORef (Maybe Window) -> CloseCallback
+closeCB ref = writeIORef ref Nothing
+
+-- | Converts the GUI's elapsed time from GLUT's integral millisecond 
+--  standard into floating point seconds.
+guiTime :: IO Double
+guiTime = do
+  mills <- get elapsedTime
+  return $ fromIntegral mills / 1000
+
+
+------------------------------------------------------------
+-- Rendering Graphics in OpenGL
+------------------------------------------------------------
+
+-- | This function takes the current dimensions of the window 
+--  (necessary for the bounding operation 'boundGraphic') and a Graphic 
+--  and produces the OpenGL IO action that actually performs the 
+--  rendering.  Two notes about it:
+--
+--  - Currently, it is using 'Graphics.UI.GLUT.Fixed8By13' for 
+--    rendering text.
+--
+--  - I have had some trouble with nesting uses of PreservingMatrix 
+--    and scissoring, so bounded graphics (and perhaps other graphic 
+--    transformations in general) may be a little buggy.
+renderGraphicInOpenGL :: Dimension -> Graphic -> IO ()
+
+renderGraphicInOpenGL _ NoGraphic = return ()
+
+renderGraphicInOpenGL s (GColor rgb graphic) = (GL.color color >> renderGraphicInOpenGL s graphic) where
+  (r,g,b) = extractRGB rgb
+  color = GL.Color3 (c2f r) (c2f g) (c2f b) :: GL.Color3 GLfloat
+  c2f i = fromIntegral i / 255
+
+renderGraphicInOpenGL _ (GText (x,y) uistr) = 
+  let tlines = zip (uitextLines uistr) [0..]
+      drawLine (s,i) = do 
+        -- We need to zipWith like this to get the String x-offsets.
+        let ss = unfoldr buildList (0,unwrapUIT s)
+            buildList (_,[]) = Nothing
+            buildList (x,(c,f,str):rest) = Just ((x,c,f,str), (x+textWidth' f str, rest))
+            th = textHeight s
+            yoff = (i * th) + (th `div` 2) + 3
+        mapM_ (drawStr yoff) ss
+      drawStr yoff (xoff, c, f, str) = GL.preservingMatrix $ do
+        case c of
+          Nothing -> return ()
+          Just rgb -> GL.color color where
+            (r,g,b) = extractRGB rgb
+            color = GL.Color3 (c2f r) (c2f g) (c2f b) :: GL.Color3 GLfloat
+            c2f i = fromIntegral i / 255
+--      This code is used for Bitmap fonts (raster offset values may need to be adjusted)
+        GL.currentRasterPosition $= GLUT.Vertex4 
+            (fromIntegral $ x + xoff) 
+            (fromIntegral $ y + yoff) 0 1
+        GLUT.renderString f str
+--      This code is used for Stroke fonts (scale and translate values may need to be adjusted)
+--        GL.translate (vector (x, y+16*(i+1)))
+--        GL.scale 0.12 (-0.12) (1::GLfloat)
+--        GLUT.renderString GLUT.MonoRoman s
+  in mapM_ drawLine tlines
+
+renderGraphicInOpenGL _ (GPolyLine ps) = 
+  GL.renderPrimitive GL.LineStrip (mapM_ vertex ps)
+
+renderGraphicInOpenGL _ (GPolygon ps) = 
+  GL.renderPrimitive GL.Polygon (mapM_ vertex ps)
+
+renderGraphicInOpenGL _ (GEllipse rect) = GL.preservingMatrix $ do
+  let ((x, y), (width, height)) = normaliseRect rect
+      r@(r1,r2) = (width / 2, height / 2)
+  GL.translate $ vectorR (x + r1, y + r2) --r
+  GL.renderPrimitive GL.Polygon $ mapM_ vertexR 
+    [ (r1 * cos i, r2 * sin i) | i <- segment 0 (2 * pi) (6 / (r1 + r2)) ]
+
+renderGraphicInOpenGL _ (GArc rect start extent) = GL.preservingMatrix $ do
+  let ((x, y), (width, height)) = normaliseRect rect
+      r@(r1, r2) = (width / 2, height / 2)
+  GL.translate $ vectorR (x + r1, y + r2)
+  GL.renderPrimitive GL.LineStrip $ mapM_ vertexR 
+    [ (r1 * cos i, r2 * sin i) | i <- segment (-(start + extent) * pi / 180) 
+        (-start * pi / 180) (6 / (r1 + r2)) ]
+
+renderGraphicInOpenGL _ (GBezier []) = return ()
+renderGraphicInOpenGL s (GBezier ps) = renderGraphicInOpenGL s (GPolyLine ps') where
+  ps' = map (bezier ps) (segment 0 1 dt)
+  dt = 1 / (lineLength ps / 8)
+  lineLength :: [Point] -> Double
+  lineLength ((x1,y1):(x2,y2):ps') = 
+    let dx = fromIntegral $ x2 - x1
+        dy = fromIntegral $ y2 - y1
+    in sqrt (dx * dx + dy * dy) + lineLength ((x2,y2):ps')
+  lineLength _ = 0
+  bezier :: [Point] -> Double -> Point
+  bezier [(x1,y1)] _t = (x1, y1)
+  bezier [(x1,y1),(x2,y2)] t = (x1 + round (fromIntegral (x2 - x1) * t), 
+                                y1 + round (fromIntegral (y2 - y1) * t))
+  bezier ps t = bezier (map (\ (p, q) -> bezier [p,q] t) (zip ps (tail ps))) t
+
+renderGraphicInOpenGL s (GTranslate (x,y) g) = 
+  GL.translate (vector (x,y)) >> renderGraphicInOpenGL s g >> GL.translate (vector (0-x,0-y))
+--renderGraphicInOpenGL (GTranslate p g) = 
+--  GL.preservingMatrix $ GL.translate (vector p) >> renderGraphicInOpenGL g
+
+renderGraphicInOpenGL s@(_,windowY) (GBounded ((x,y), (w,h)) g) = do
+    let [x', y', w', h'] = map fromIntegral [x, windowY-y-h, w, h]
+    oldScissor <- GL.get GL.scissor
+    let ((x'',y''),(w'',h'')) = maybe ((x',y'),(w',h'))
+            (\(GL.Position a b, GL.Size c d) -> intersect ((x',y'),(w',h')) ((a,b),(c,d))) oldScissor
+    -- FIXME: This intersection of scissors may not be right, but I'm not sure what's better
+    GL.scissor $= Just (GL.Position x'' y'', GL.Size w'' h'')
+    renderGraphicInOpenGL s g
+    GL.scissor $= oldScissor
+  where
+    intersect ((x,y),(w,h)) ((x',y'),(w',h')) = ((x'',y''),(w'',h'')) where
+      x'' = min x x'
+      y'' = min y y'
+      w'' = max 0 $ (min (x+w) (x'+w')) - x''
+      h'' = max 0 $ (min (y+h) (y'+h')) - y''
+
+
+renderGraphicInOpenGL s (GRotate p a' g) = 
+  GL.preservingMatrix $ GL.rotate a (vector p) >> renderGraphicInOpenGL s g
+--  GL.rotate a (vector p) >> renderGraphicInOpenGL g >> GL.rotate (0-a) (vector p)
+  where a = realToFrac a'
+
+renderGraphicInOpenGL s (GScale x' y' g) = 
+  GL.preservingMatrix $ GL.scale x y (1::GLfloat) >> renderGraphicInOpenGL s g
+--  GL.scale x y (1::GLfloat) >> renderGraphicInOpenGL g >> GL.scale (1/x) (1/y) (1::GLfloat)
+  where x = realToFrac x'
+        y = realToFrac y'
+
+renderGraphicInOpenGL s (OverGraphic over base) = 
+  renderGraphicInOpenGL s base >> renderGraphicInOpenGL s over
+
+
+
+------------------------------------------------------------
+-- Helper functions
+------------------------------------------------------------
+normaliseRect :: Rect -> ((Double, Double),(Double, Double))
+normaliseRect ((x, y), (w, h)) = ((fromIntegral x', fromIntegral y'), (fromIntegral w', fromIntegral h'))
+  where (x',w') = if w < 0 then (x+w, 0-w) else (x, w)
+        (y',h') = if h < 0 then (y+h, 0-h) else (y, h)
+
+segment :: (Num t, Ord t) => t -> t -> t -> [t]
+segment start stop step = ts start
+  where ts i = if i >= stop then [stop] else i : ts (i + step)
+
+vertex :: Point -> IO ()
+vertex (x,y) = GL.vertex $ GL.Vertex3 (fromIntegral x) (fromIntegral y) (0::GLfloat)
+
+vertexR :: (Double,Double) -> IO ()
+vertexR (x,y) = GL.vertex $ GL.Vertex3 (realToFrac x) (realToFrac y) (0::GLfloat)
+
+vector :: (Int, Int) -> GL.Vector3 GLfloat
+vector (x,y) = GL.Vector3 (fromIntegral x) (fromIntegral y) 0
+
+vectorR :: (Double,Double) -> GL.Vector3 GLfloat
+vectorR (x,y) = GL.Vector3 (realToFrac x) (realToFrac y) 0
+
+
+------------------------------------------------------------
+-- Key support
+------------------------------------------------------------
+
+-- | Convert GLUT's key codes to UISF's internal ones.
+glutKeyToKey :: GLUT.Key -> Key
+glutKeyToKey key 
+ = case key of
+        GLUT.Char '\13'                            -> SpecialKey KeyEnter
+        GLUT.Char '\9'                             -> SpecialKey KeyTab
+        GLUT.Char '\ESC'                           -> SpecialKey KeyEsc
+        GLUT.Char '\DEL'                           -> SpecialKey KeyDelete
+        GLUT.Char '\BS'                            -> SpecialKey KeyBackspace
+        GLUT.Char c                                -> Char c
+        GLUT.SpecialKey GLUT.KeyF1                 -> SpecialKey KeyF1
+        GLUT.SpecialKey GLUT.KeyF2                 -> SpecialKey KeyF2
+        GLUT.SpecialKey GLUT.KeyF3                 -> SpecialKey KeyF3
+        GLUT.SpecialKey GLUT.KeyF4                 -> SpecialKey KeyF4
+        GLUT.SpecialKey GLUT.KeyF5                 -> SpecialKey KeyF5
+        GLUT.SpecialKey GLUT.KeyF6                 -> SpecialKey KeyF6
+        GLUT.SpecialKey GLUT.KeyF7                 -> SpecialKey KeyF7
+        GLUT.SpecialKey GLUT.KeyF8                 -> SpecialKey KeyF8
+        GLUT.SpecialKey GLUT.KeyF9                 -> SpecialKey KeyF9
+        GLUT.SpecialKey GLUT.KeyF10                -> SpecialKey KeyF10
+        GLUT.SpecialKey GLUT.KeyF11                -> SpecialKey KeyF11
+        GLUT.SpecialKey GLUT.KeyF12                -> SpecialKey KeyF12
+        GLUT.SpecialKey GLUT.KeyLeft               -> SpecialKey KeyLeft
+        GLUT.SpecialKey GLUT.KeyUp                 -> SpecialKey KeyUp
+        GLUT.SpecialKey GLUT.KeyRight              -> SpecialKey KeyRight
+        GLUT.SpecialKey GLUT.KeyDown               -> SpecialKey KeyDown
+        GLUT.SpecialKey GLUT.KeyPageUp             -> SpecialKey KeyPageUp
+        GLUT.SpecialKey GLUT.KeyPageDown           -> SpecialKey KeyPageDown
+        GLUT.SpecialKey GLUT.KeyHome               -> SpecialKey KeyHome
+        GLUT.SpecialKey GLUT.KeyEnd                -> SpecialKey KeyEnd
+        GLUT.SpecialKey GLUT.KeyInsert             -> SpecialKey KeyInsert
+        GLUT.SpecialKey GLUT.KeyNumLock            -> SpecialKey KeyNumLock
+        GLUT.SpecialKey GLUT.KeyBegin              -> SpecialKey KeyBegin
+        GLUT.SpecialKey GLUT.KeyDelete             -> SpecialKey KeyDelete
+        GLUT.SpecialKey (GLUT.KeyUnknown i)        -> SpecialKey (KeyUnknown i)
+        GLUT.SpecialKey GLUT.KeyShiftL             -> SpecialKey KeyShiftL
+        GLUT.SpecialKey GLUT.KeyShiftR             -> SpecialKey KeyShiftR
+        GLUT.SpecialKey GLUT.KeyCtrlL              -> SpecialKey KeyCtrlL
+        GLUT.SpecialKey GLUT.KeyCtrlR              -> SpecialKey KeyCtrlR
+        GLUT.SpecialKey GLUT.KeyAltL               -> SpecialKey KeyAltL
+        GLUT.SpecialKey GLUT.KeyAltR               -> SpecialKey KeyAltR
+        GLUT.MouseButton GLUT.LeftButton           -> MouseButton LeftButton
+        GLUT.MouseButton GLUT.MiddleButton         -> MouseButton MiddleButton
+        GLUT.MouseButton GLUT.RightButton          -> MouseButton RightButton
+        GLUT.MouseButton GLUT.WheelUp              -> MouseButton WheelUp
+        GLUT.MouseButton GLUT.WheelDown            -> MouseButton WheelDown
+        GLUT.MouseButton (GLUT.AdditionalButton i) -> MouseButton (AdditionalButton i)
− FRP/UISF/SOE.hs
@@ -1,714 +0,0 @@-module FRP.UISF.SOE (
-  -- * Window Functions
-  runGraphics,
-  Title,
-  Size,
-  Window,
-  openWindow,
-  getMainWindowSize,
-  clearWindow,
-  drawInWindow,
-  drawInWindowNow,
-  setGraphic,
-  setGraphic',
-  setDirty,
-  closeWindow,
-  openWindowEx,
-  -- * Drawing Functions
-  RedrawMode,
-  drawGraphic,
-  drawBufferedGraphic,
-  Graphic,
-  nullGraphic,
-  emptyGraphic,
-  overGraphic ,
-  overGraphics,
-  translateGraphic,
-  Color (..),
-  RGB,
-  RGBA,
-  rgb,
-  rgba,
-  withColor,
-  withColor',
-  text,
-  Point,
-  ellipse,
-  shearEllipse,
-  line,
-  polygon,
-  polyline,
-  polyBezier,
-  Angle,
-  arc,
-  scissorGraphic,
---  Region,     --Regions are an unused feature
---  createRectangle,
---  createEllipse,
---  createPolygon,
---  andRegion,
---  orRegion,
---  xorRegion,
---  diffRegion,
---  drawRegion,
---  getKey,     -- See note at definition for why these are left out
---  getLBP,
---  getRBP,
-  -- * Event Handling Functions
-  Key(..),
-  SpecialKey (..),
-  UIEvent (..),
-  hasShiftModifier, hasCtrlModifier, hasAltModifier, 
-  maybeGetWindowEvent,
-  getWindowEvent,
-  Word32,
-  timeGetTime,
-  word32ToInt,
-  isKeyPressed
-  ) where
-
-import Data.Ix (Ix)
-import Data.Word (Word32)
-import Graphics.UI.GLFW (Key(..), SpecialKey(..), KeyButtonState(..))
-import qualified Graphics.UI.GLFW as GLFW
-import qualified Graphics.Rendering.OpenGL as GL
-import Graphics.Rendering.OpenGL (($=), GLfloat)
-import System.IO.Unsafe (unsafePerformIO)
-import Control.Monad (when, unless)
-import Control.Concurrent.STM.TChan
-import Control.Monad.STM (atomically)
-import Control.Concurrent.MVar
-import Data.IORef
-import Data.List (delete)
-
-
--------------------
--- Key state
--------------------
-
-keyState :: IORef ([Char],[SpecialKey])
-keyState = unsafePerformIO $ newIORef ([],[])
-
-addCharToKeyState :: Char -> IO ()
-addCharToKeyState c = atomicModifyIORef keyState $ \(cs,ss) -> ((c:cs,ss),())
-
-addSKeyToKeyState :: SpecialKey -> IO ()
-addSKeyToKeyState s = atomicModifyIORef keyState $ \(cs,ss) -> ((cs,s:ss),())
-
-removeCharFromKeyState :: Char -> IO ()
-removeCharFromKeyState c = atomicModifyIORef keyState $ \(cs,ss) -> ((delete c cs,ss),())
-
-removeSKeyFromKeyState :: SpecialKey -> IO ()
-removeSKeyFromKeyState s = atomicModifyIORef keyState $ \(cs,ss) -> ((cs,delete s ss),())
-
--------------------
--- Window Functions
--------------------
-
-runGraphics :: IO () -> IO ()
-runGraphics main = main
-
-type Title = String
-type Size = (Int, Int)
-
-data Window = Window {
-  graphicVar :: MVar (Graphic, Bool), -- ^ boolean to remember if it's dirty
-  eventsChan :: TChan UIEvent
-}
-
--- | Graphic is just a wrapper for OpenGL IO
-newtype Graphic = Graphic (IO ())
-
-initialized, opened :: MVar Bool
-initialized = unsafePerformIO (newMVar False)
-opened = unsafePerformIO (newMVar False)
-
-initialize :: IO ()
-initialize = do
-  i <- readMVar initialized
-  unless i $ do
-      _ <- GLFW.initialize
-      modifyMVar_ initialized (const $ return True)
-      return ()
-
-openWindow :: Title -> Size -> IO Window
-openWindow title size =
-  openWindowEx title Nothing (Just size) drawBufferedGraphic
-
--- pos is always ignored due to GLFW
-openWindowEx :: Title -> Maybe Point -> Maybe Size -> RedrawMode -> IO Window
-openWindowEx title _position size (RedrawMode _useDoubleBuffer) = do
-  let siz = maybe (GL.Size 400 300) fromSize size
-  initialize
-  gVar <- newMVar (emptyGraphic, False)
-  eChan <- atomically newTChan
-  _ <- GLFW.openWindow siz [GLFW.DisplayStencilBits 8, GLFW.DisplayAlphaBits 8] GLFW.Window
-  GLFW.windowTitle $= title
-  modifyMVar_ opened (\_ -> return True)
-  GL.shadeModel $= GL.Smooth
-  -- enable antialiasing
-  GL.lineSmooth $= GL.Enabled
-  GL.blend $= GL.Enabled
-  GL.blendFunc $= (GL.SrcAlpha, GL.OneMinusSrcAlpha)
-  GL.lineWidth $= 1.5
-
-  -- this will hang on Windows
-  -- let updateWindow = readMVar gVar >>= (\(Graphic g) -> g >> GLFW.swapBuffers)
-  -- GLFW.windowRefreshCallback $= updateWindow
-
-  let motionCallback (GL.Position x y) = atomically $ 
-        writeTChan eChan MouseMove { pt = (fromIntegral x, fromIntegral y) }
-  GLFW.mousePosCallback $= motionCallback
-     
-  let charCallback c Press = do 
-        ks <- readIORef keyState
-        atomically $ writeTChan eChan Key{ char = c, modifiers = ks, isDown = True}
-      charCallback c Release = return () -- This never happens
-  let keyCallBack (CharKey c) Press = do
---        ks <- readIORef keyState
---        atomically $ writeTChan eChan Key{ char = c, modifiers = ks, isDown = True}
-        addCharToKeyState c
-      keyCallBack (CharKey c) Release = do
-        removeCharFromKeyState c
-        ks <- readIORef keyState
-        atomically $ writeTChan eChan Key{ char = c, modifiers = ks, isDown = False}
-      keyCallBack (SpecialKey sk) Press = do
-        ks <- readIORef keyState
-        atomically $ writeTChan eChan SKey{ skey = sk, modifiers = ks, isDown = True}
-        addSKeyToKeyState sk
-      keyCallBack (SpecialKey sk) Release = do
-        removeSKeyFromKeyState sk
-        ks <- readIORef keyState
-        atomically $ writeTChan eChan SKey{ skey = sk, modifiers = ks, isDown = False}
-  GLFW.charCallback $= charCallback 
-  GLFW.keyCallback  $= keyCallBack
-  GLFW.enableSpecial GLFW.KeyRepeat
-
-  GLFW.mouseButtonCallback $= (\but state -> do
-    GL.Position x y <- GL.get GLFW.mousePos
-    atomically $ writeTChan eChan Button{
-        pt = (fromIntegral x, fromIntegral y),
-        isLeft = (but == GLFW.ButtonLeft),
-        isDown = (state == Press)})
-
-  GLFW.windowSizeCallback $= atomically . writeTChan eChan . Resize
-  GLFW.windowRefreshCallback $= atomically (writeTChan eChan Refresh)
-  GLFW.windowCloseCallback $= (closeWindow_ eChan >> return True)
-
-  return Window {
-    graphicVar = gVar,
-    eventsChan = eChan
-  }
-
-getMainWindowSize :: IO Size
-getMainWindowSize = do
-  (GL.Size x y) <- GL.get GLFW.windowSize
-  return (fromIntegral x, fromIntegral y)
-
-clearWindow :: Window -> IO ()
-clearWindow win = setGraphic win (Graphic (return ()))
-
-drawInWindow :: Window -> Graphic -> IO ()
-drawInWindow win graphic = 
-  modifyMVar_ (graphicVar win) (\ (g, _) -> 
-    return (overGraphic graphic g, True)) 
-
--- | if window is marked as dirty, mark it clean, draw and swap buffer;
--- otherwise do nothing.
-updateWindowIfDirty :: Window -> IO ()
-updateWindowIfDirty win = do
-   io <- modifyMVar (graphicVar win) (\ (g@(Graphic io), dirty) -> 
-            return ((g, False), when dirty (io >> GLFW.swapBuffers)))
-   io
-
-drawInWindowNow :: Window -> Graphic -> IO ()
-drawInWindowNow win graphic = do
-  drawInWindow win graphic
-  updateWindowIfDirty win
-
--- | setGraphic set the given Graphic over empty (black) background for
--- display in current Window.
-setGraphic :: Window -> Graphic -> IO ()
-setGraphic win graphic = 
-    modifyMVar_ (graphicVar win) (\_ -> 
-        return (overGraphic graphic emptyGraphic, True))
-
-setGraphic' :: Window -> Graphic -> IO ()
-setGraphic' win graphic = 
-    modifyMVar_ (graphicVar win) (\(_, dirty) -> 
-        return (overGraphic graphic emptyGraphic, dirty))
-
-setDirty :: Window -> IO ()
-setDirty win = 
-    modifyMVar_ (graphicVar win) (\(g, _) -> return (g, True))
-
-closeWindow :: Window -> IO ()
-closeWindow win = closeWindow_ (eventsChan win)
-
-closeWindow_ :: TChan UIEvent -> IO ()
-closeWindow_ chan = do
-  atomically $ writeTChan chan Closed
-  modifyMVar_ opened (\_ -> return False)
-  GLFW.closeWindow
-  GLFW.pollEvents
-
---------------------
--- Drawing Functions
---------------------
-
-newtype RedrawMode = RedrawMode Bool
-
-drawGraphic :: RedrawMode
-drawGraphic = RedrawMode False
-
-drawBufferedGraphic :: RedrawMode
-drawBufferedGraphic = RedrawMode True
-
-data Color = Black
-           | Blue
-           | Green
-           | Cyan
-           | Red
-           | Magenta
-           | Yellow
-           | White
-  deriving (Eq, Ord, Bounded, Enum, Ix, Show, Read)
-
-type Angle = GLfloat
-
-nullGraphic :: Graphic
-nullGraphic = Graphic $ return ()
-
-emptyGraphic :: Graphic
-emptyGraphic = Graphic $ do 
-  GL.clearColor $= GL.Color4 (0xec/0xff) (0xe9/0xff) (0xd8/0xff) (0x00) -- GL.Color4 0 0 0 0
-  GL.clear [GL.ColorBuffer, GL.StencilBuffer]
-
-translateGraphic :: (Int, Int) -> Graphic -> Graphic
-translateGraphic (x, y) (Graphic g) = Graphic $ do
-  GL.translate (GL.Vector3 (fromIntegral x) (fromIntegral y) (0::GLfloat))
-  g
-  GL.translate (GL.Vector3 (fromIntegral (0-x)) (fromIntegral (0-y)) (0::GLfloat))
---translateGraphic (x, y) (Graphic g) = Graphic $ GL.preservingMatrix $ do
---  GL.translate (GL.Vector3 (fromIntegral x) (fromIntegral y) (0::GLfloat))
---  g
-
-overGraphic :: Graphic -> Graphic -> Graphic
-overGraphic (Graphic over) (Graphic base) = Graphic (base >> over)
-
-overGraphics :: [Graphic] -> Graphic
-overGraphics = foldl1 overGraphic
-
-colorToRGB :: Color -> GL.Color3 GLfloat
-colorToRGB Black   = GL.Color3 0 0 0
-colorToRGB Blue    = GL.Color3 0 0 1
-colorToRGB Green   = GL.Color3 0 1 0
-colorToRGB Cyan    = GL.Color3 0 1 1
-colorToRGB Red     = GL.Color3 1 0 0
-colorToRGB Magenta = GL.Color3 1 0 1
-colorToRGB Yellow  = GL.Color3 1 1 0
-colorToRGB White   = GL.Color3 1 1 1
-
-withColor :: Color -> Graphic -> Graphic
-withColor color = withColor' (colorToRGB color)
-
-withColor' :: GL.Color a => a -> Graphic -> Graphic
-withColor' color (Graphic g) = Graphic (GL.color color >> g)
-
-type RGB = GL.Color3 GL.GLfloat
-type RGBA = GL.Color4 GL.GLfloat
-
-rgb :: (Integral r, Integral g, Integral b) => r -> g -> b -> RGB
-rgb r g b = GL.Color3 (c2f r) (c2f g) (c2f b) :: RGB
-rgba :: (Integral r, Integral g, Integral b, Integral a) => r -> g -> b -> a -> RGBA
-rgba r g b a = GL.Color4 (c2f r) (c2f g) (c2f b) (c2f a) :: RGBA
-c2f :: (Integral c, Fractional f) => c -> f
-c2f i = fromIntegral i / 255
-
-text :: Point -> String -> Graphic
-text (x,y) str = Graphic $ GL.preservingMatrix $ do
-  GL.translate (GL.Vector3 (fromIntegral x) (fromIntegral y + 16) (0::GLfloat))
-  GL.scale 1 (-1) (1::GLfloat)
-  GLFW.renderString GLFW.Fixed8x16 str
-
-type Point = (Int, Int)
-
-ellipse :: Point -> Point -> Graphic
-ellipse pt1 pt2 = Graphic $ GL.preservingMatrix $ do
-  let (x, y, width, height) = normaliseBounds pt1 pt2
-      (r1, r2) = (width / 2, height / 2)
-  GL.translate (GL.Vector3 (x + r1) (y + r2) 0)
-  GL.renderPrimitive GL.Polygon (circle r1 r2 0 (2 * pi) (6 / (r1 + r2)))
-      
-shearEllipse :: Point -> Point -> Point -> Graphic
-shearEllipse p0 p1 p2 = Graphic $ 
-  let (x0,y0) = fromPoint p0
-      (x1,y1, w, h) = normaliseBounds p1 p2
-      (x2,y2) = (x1 + w, y1 + h)
-      x =  (x1 + x2) / 2  -- centre of parallelogram
-      y =  (y1 + y2) / 2
-      dx1 = (x1 - x0) / 2 -- distance to corners from centre
-      dy1 = (y1 - y0) / 2
-      dx2 = (x2 - x0) / 2
-      dy2 = (y2 - y0) / 2
-      pts = [ (x + c*dx1 + s*dx2, y + c*dy1 + s*dy2)
-            | (c,s) <- cos'n'sins ]
-      cos'n'sins = [ (cos a, sin a) | a <- segment 0 (2 * pi) (40 / (w + h))]
-  in GL.renderPrimitive GL.Polygon $ 
-        mapM_ (\ (x, y) -> GL.vertex (vertex3 x y 0)) pts
-
-line :: Point -> Point -> Graphic
-line (x1, y1) (x2, y2) = Graphic $ 
-  GL.renderPrimitive GL.LineStrip (do
-    GL.vertex (vertex3 (fromIntegral x1) (fromIntegral y1) 0)
-    GL.vertex (vertex3 (fromIntegral x2) (fromIntegral y2) 0))
-
-polygon :: [Point] -> Graphic
-polygon ps = Graphic $
-  GL.renderPrimitive GL.Polygon (foldr1 (>>) (map 
-    (\ (x, y) -> GL.vertex (vertex3 (fromIntegral x) (fromIntegral y) 0))
-    ps))
-
-polyline :: [Point] -> Graphic
-polyline ps = Graphic $ 
-  GL.renderPrimitive GL.LineStrip (foldr1 (>>) (map 
-    (\ (x, y) -> GL.vertex (vertex3 (fromIntegral x) (fromIntegral y) 0))
-    ps))
-
-polyBezier :: [Point] -> Graphic
-polyBezier [] = Graphic $ return ()
-polyBezier ps = polyline (map (bezier ps) (segment 0 1 dt))
-  where
-    dt = 1 / (lineLength ps / 8)
-    lineLength :: [Point] -> GLfloat
-    lineLength ((x1,y1):(x2,y2):ps') = 
-      let dx = x2 - x1
-          dy = y2 - y1
-      in sqrt (fromIntegral (dx * dx + dy * dy)) + lineLength ((x2,y2):ps')
-    lineLength _ = 0
-
-bezier :: [Point] -> GLfloat -> Point
-bezier [(x1,y1)] _t = (x1, y1)
-bezier [(x1,y1),(x2,y2)] t = (x1 + truncate (fromIntegral (x2 - x1) * t), 
-                              y1 + truncate (fromIntegral (y2 - y1) * t))
-bezier ps t = bezier (map (\ (p, q) -> bezier [p,q] t) (zip ps (tail ps))) t
-
-arc :: Point -> Point -> Angle -> Angle -> Graphic
-arc pt1 pt2 start extent = Graphic $ GL.preservingMatrix $ do
-  let (x, y, width, height) = normaliseBounds pt1 pt2
-      (r1, r2) = (width / 2, height / 2)
-  GL.translate (GL.Vector3 (x + r1) (y + r2) 0)
-  GL.renderPrimitive GL.LineStrip (circle r1 r2 
-    (-(start + extent) * pi / 180) (-start * pi / 180) (6 / (r1 + r2)))
-
-
-scissorGraphic :: (Point, Size) -> Graphic -> Graphic
-scissorGraphic ((x,y), (w,h)) (Graphic g) = Graphic $ do
-    (_,windowY) <- getMainWindowSize
-    let [x', y', w', h'] = map fromIntegral [x, windowY-y-h, w, h]
-    oldScissor <- GL.get GL.scissor
-    GL.scissor $= Just (GL.Position x' y', GL.Size w' h')
-    g
-    GL.scissor $= oldScissor
-
-
--------------------
--- Region Functions
--------------------
-
-{- Unused
-
-createRectangle :: Point -> Point -> Region
-createRectangle pt1 pt2 =
-  let (x,y,width,height) = normaliseBounds' pt1 pt2 
-      [x0, y0, x1, y1] = map fromIntegral [x, y, x + width, y + height]
-      drawing = 
-        GL.renderPrimitive GL.Quads (do
-        GL.vertex (vertex3 x0 y0 0)
-        GL.vertex (vertex3 x1 y0 0)
-        GL.vertex (vertex3 x1 y1 0)
-        GL.vertex (vertex3 x0 y1 0))
-   in [[Pos ("R" ++ show (x0,y0,x1,y1), drawing)]]
-
-createEllipse :: Point -> Point -> Region
-createEllipse pt1 pt2 =
-  let (x,y,width,height) = normaliseBounds' pt1 pt2
-      drawing = 
-        GL.preservingMatrix $ do
-          let (x, y, width, height) = normaliseBounds pt1 pt2
-              (r1, r2) = (width / 2, height / 2)
-          GL.translate (GL.Vector3 (x + r1) (y + r2) 0)
-          GL.renderPrimitive GL.Polygon (circle r1 r2 0 (2 * pi) (6 / (r1 + r2)))
-  in [[Pos ("E" ++ show (x, y, width, height), drawing)]]
-
-createPolygon :: [Point] -> Region
-createPolygon [] = [[]]
-createPolygon ps =
-  let (minx, maxx, miny, maxy) = (minimum (map fst ps), maximum (map fst ps),
-                                  minimum (map snd ps), maximum (map snd ps))
-      drawing = do
-        GL.renderPrimitive GL.Polygon (foldr1 (>>) (map 
-          (\ (x, y) -> GL.vertex (vertex3 (fromIntegral x) (fromIntegral y) 0))
-          ps))
-   in [[Pos ("P"++show ps, drawing)]]
-
-andRegion, orRegion, xorRegion, diffRegion :: Region -> Region -> Region
-
--- We'll convert region expression into disjuction canonical form
--- so as to make rendering easier using Stencil buffer.
-
-type Region = [Conjuction]
-type Conjuction = [Atom]
-data Atom = Pos Atom' | Neg Atom'
-type Atom' = (String, IO ())
-instance Show Atom where
-  show (Pos (s, _)) = "+" ++ s
-  show (Neg (s, _)) = "-" ++ s
-
-conjuction :: Region -> Region -> Region
-conjuction xs ys = [ x ++ y | x <- xs, y <- ys ]
-disjuction xs ys = xs ++ ys
-negTerm [] = []
-negTerm xs = foldl1 conjuction (map negA xs)
-  where
-    negA :: Conjuction -> Region 
-    negA ys = map negS ys
-    negS :: Atom -> Conjuction
-    negS (Pos i) = [Neg i]
-    negS (Neg i) = [Pos i]
-
-data RegionOp = AND | OR | XOR | DIFF
-
-andRegion  = combineRegion AND
-orRegion   = combineRegion OR
-xorRegion  = combineRegion XOR
-diffRegion = combineRegion DIFF
-
-drawRegion :: Region -> Graphic
-drawRegion term = Graphic drawAux
-  where
-    drawAux = do
-      GL.stencilMask $= 1
-      GL.stencilTest $= GL.Enabled
-      sequence_ [drawConjuction (posT t) (negT t) | t <- term]
-      GL.stencilTest $= GL.Disabled
-
-    posT [] = []
-    posT (Pos x:xs) = x : posT xs
-    posT (_:xs) = posT xs
-
-    negT [] = []
-    negT (Neg x:xs) = x : negT xs
-    negT (_:xs) = negT xs
-
-    drawConjuction ps ns = do
-      -- render all positive atoms only to stencil buffer
-      GL.depthFunc $= Just GL.Never
-      GL.stencilMask $= 0xff
-      GL.stencilFunc $= (GL.Greater, 0, 0xff)
-      -- every pixel rendered increases the value in the stencil buffer by 1
-      GL.stencilOp $= (GL.OpIncr, GL.OpIncr, GL.OpZero)
-      mapM_ drawIt ps
-      -- render all negative atoms to clear the stencil pixel to 0
-      GL.stencilOp $= (GL.OpZero, GL.OpZero, GL.OpZero)
-      mapM_ drawIt ns
-      -- finally render all positive atoms to screen where the stencil pixel
-      -- equals (length ps)
-      GL.depthFunc $= Just GL.Always
-      GL.stencilFunc $= (GL.Equal, fromIntegral $ length ps, 0xff)
-      GL.stencilOp $= (GL.OpZero, GL.OpZero, GL.OpZero)
-      mapM_ drawIt ps
-
-    drawIt (_, io) = io
-
---combineRegion :: Cairo.Operator -> Region -> Region -> Region
-combineRegion operator a b =
-  case operator of
-    AND -> conjuction a b
-    OR -> disjuction a b
-    XOR -> disjuction (conjuction (negTerm a) b) (conjuction a (negTerm b))
-    DIFF -> conjuction a (negTerm b)
-
--}
----------------------------
--- Event Handling Functions
----------------------------
-
-data UIEvent = 
-    Key {
-      char :: Char,
-      modifiers :: ([Char],[SpecialKey]),
-      isDown :: Bool
-    }
-  | SKey {
-      skey :: SpecialKey,
-      modifiers :: ([Char],[SpecialKey]),
-      isDown :: Bool
-    }
-  | Button {
-     pt :: Point,
-     isLeft :: Bool,
-     isDown :: Bool
-    }
-  | MouseMove {
-      pt :: Point
-    }
-  | Resize GL.Size
-  | Refresh
-  | Closed
-  | NoUIEvent
- deriving Show
-
-hasShiftModifier :: ([Char],[SpecialKey]) -> Bool
-hasShiftModifier (_, sks) = elem LSHIFT sks || elem RSHIFT sks
-
-hasCtrlModifier :: ([Char],[SpecialKey]) -> Bool
-hasCtrlModifier (_, sks) = elem LCTRL sks || elem RCTRL sks
-
-hasAltModifier :: ([Char],[SpecialKey]) -> Bool
-hasAltModifier (_, sks) = elem LALT sks || elem RALT sks
-
--- | getWindowEvent and maybeGetWindowEvent both take an additional argument 
---  sleepTime that tells how long to sleep in the case where there are no
---  window events to return.  This is used to allow the cpu to take other 
---  tasks at these times rather than needlessly spinning.  The sleepTime 
---  parameter used to be fixed at 0.01.
-
-getWindowEvent :: Double -> Window -> IO UIEvent
-getWindowEvent sleepTime win = do
-  event <- maybeGetWindowEvent sleepTime win
-  maybe (getWindowEvent sleepTime win) return event
-
-maybeGetWindowEvent :: Double -> Window -> IO (Maybe UIEvent)
-maybeGetWindowEvent sleepTime win = let winChan = eventsChan win in do
-  updateWindowIfDirty win
-  mevent <- atomically $ tryReadTChan winChan
-  case mevent of
-    Nothing -> GLFW.sleep sleepTime >> GLFW.pollEvents >> return Nothing
-    Just Refresh -> do
-      (Graphic io, _) <- readMVar (graphicVar win)
-      io
-      GLFW.swapBuffers
-      maybeGetWindowEvent sleepTime win
-    Just (e@(Resize _)) -> do
-      (Resize size@(GL.Size w h)) <- getLastResizeEvent winChan e
-      GL.viewport $= (GL.Position 0 0, size)
-      GL.matrixMode $= GL.Projection
-      GL.loadIdentity
-      GL.ortho2D 0 (realToFrac w) (realToFrac h) 0
-      -- force a refresh, needed for OS X
-      atomically $ writeTChan winChan Refresh
-      maybeGetWindowEvent sleepTime win
-    Just e -> return (Just e)
-
--- | When a window is resized, all of the resize events queue up until the 
---   mouse button is released.  This causes some delay as each individual 
---   resize event is handled and then the window is redrawn.  This function 
---   clears all resize and refresh events until the last resize one.
---   Note that because this function is used, a Refresh event should follow 
---   the resizing.
-getLastResizeEvent :: TChan UIEvent -> UIEvent -> IO UIEvent
-getLastResizeEvent ch prev = do
-    mevent <- atomically $ tryReadTChan ch
-    case mevent of
-      Nothing -> return prev
-      Just (e@(Resize _)) -> getLastResizeEvent ch e
-      Just Refresh        -> getLastResizeEvent ch prev
-      Just e -> atomically (unGetTChan ch e) >> return prev
-
-
---  getKeyEx, getKey, getButton, getLBP, and getRBP are defined here but 
---  never used in Euterpea.  Furthermore, due to the change in getWindowEvent 
---  so that it now requires a sleepTime argument (previously fixed at 0.01), 
---  they either need to be parameterized over sleepTime or set.  I'm not 
---  sure which is the better solution, so I will leave them commented out 
---  until they're needed.
-
-{-
-getKeyEx :: Window -> Bool -> IO Char
-getKeyEx win down = loop
-  where loop = do e <- getWindowEvent win
-                  case e of
-                    (Key { char = ch, isDown = d })
-                      | d == down -> return ch
-                    Closed -> return '\x0'
-                    _ -> loop
-
-getKey :: Window -> IO Char
-getKey win = do
-  ch <- getKeyEx win True
-  if ch == '\x0' then return ch
-    else getKeyEx win False
-
-getButton :: Window -> Int -> Bool -> IO Point
-getButton win but down = loop
-  where loop = do e <- getWindowEvent win
-                  case e of
-                    (Button { pt = pt, isDown = id })
-                      | id == down -> return pt
-                    _ -> loop
-
-getLBP :: Window -> IO Point
-getLBP w = getButton w 1 True
-
-getRBP :: Window -> IO Point
-getRBP w = getButton w 2 True
--}
-
--- | use GLFW's high resolution timer
-timeGetTime :: IO Double
-timeGetTime = GL.get GLFW.time
-
-word32ToInt :: Word32 -> Int
-word32ToInt = fromIntegral
-
--- | Designed to be used with Key, CharKey, or SpecialKey
-isKeyPressed :: Enum a => a -> IO Bool
-isKeyPressed k = do
-    kbs <- GLFW.getKey k
-    return $ case kbs of
-        Press   -> True
-        Release -> False
-
-----------------------
--- Auxiliary Functions
-----------------------
-
---vertex4 :: GLfloat -> GLfloat -> GLfloat -> GLfloat -> GL.Vertex4 GLfloat
---vertex4 = GL.Vertex4
-
-vertex3 :: GLfloat -> GLfloat -> GLfloat -> GL.Vertex3 GLfloat
-vertex3 = GL.Vertex3
-
-normaliseBounds :: Point -> Point -> (GLfloat,GLfloat,GLfloat,GLfloat)
-normaliseBounds (x1,y1) (x2,y2) = (x, y, width, height)
-  where x = fromIntegral $ min x1 x2
-        y = fromIntegral $ min y1 y2
-        width  = fromIntegral $ abs $ x1 - x2
-        height = fromIntegral $ abs $ y1 - y2
-
---normaliseBounds' :: Point -> Point -> (Int,Int,Int,Int)
---normaliseBounds' (x1,y1) (x2,y2) = (x, y, width, height)
---  where x = min x1 x2
---        y = min y1 y2
---        width  = abs $ x1 - x2
---        height = abs $ y1 - y2
-
-fromPoint :: Point -> (GLfloat, GLfloat)
-fromPoint (x1, x2) = (fromIntegral x1, fromIntegral x2)
-
-fromSize :: Size -> GL.Size
-fromSize (x, y) = GL.Size (fromIntegral x) (fromIntegral y)
-
--- we add 20 pixels to the y position to leave space for window title bar
---fromPosition (x, y) = GL.Position (fromIntegral x) (20 + fromIntegral y)
-
-circle :: GLfloat -> GLfloat -> GLfloat -> GLfloat -> GLfloat -> IO ()
-circle r1 r2 start stop step =
-  let vs = [ (r1 * cos i, r2 * sin i) | i <- segment start stop step ]
-  in mapM_ (\(x, y) -> GL.vertex (vertex3 x y 0)) vs
-
-segment :: (Num t, Ord t) => t -> t -> t -> [t]
-segment start stop step = ts start
-  where ts i = if i >= stop then [stop] else i : ts (i + step)
-
FRP/UISF/UISF.hs view
@@ -10,46 +10,48 @@ -- A simple Graphical User Interface with concepts borrowed from Phooey
 -- by Conal Elliot.
 
-{-# LANGUAGE ScopedTypeVariables, Arrows, RecursiveDo, CPP, OverlappingInstances, FlexibleInstances, TypeSynonymInstances #-}
+{-# LANGUAGE Arrows, RecursiveDo, CPP, OverlappingInstances, FlexibleInstances, TypeSynonymInstances, MultiParamTypeClasses #-}
 
 module FRP.UISF.UISF (
     UISF(..),
     uisfSource, uisfSink, uisfPipe,
     uisfSourceE, uisfSinkE, uisfPipeE,
     -- * UISF Getters
-    getTime, getCTX, getEvents, getFocusData, addTerminationProc, getMousePosition, 
+    getDeltaTime, getCTX, withCTX, getEvents, getFocusData, addTerminationProc, getMousePosition, 
     -- * UISF constructors, transformers, and converters
     mkUISF, 
-    -- * UISF Lifting
-    -- $lifting
-    asyncUISFE, asyncUISFV, --asyncUISFC, 
     -- * Layout Transformers
     -- $lt
     leftRight, rightLeft, topDown, bottomUp, 
     conjoin, unconjoin, 
     setLayout, setSize, pad, 
     -- * Execute UI Program
-    UIParams (..), defaultUIParams,
+    UIParams, defaultUIParams,
+    uiInitialize, uiClose, uiTitle, uiSize, uiInitFlow, uiTickDelay, uiCloseOnEsc, uiBackground,
     runUI, runUI'
 
 ) where
 
 #if __GLASGOW_HASKELL__ >= 610
 import Control.Category
-import Prelude hiding ((.), id)
+import Prelude hiding ((.), id, mapM_)
+#else
+import Prelude hiding (mapM_)
 #endif
 import Control.Arrow
 import Control.Arrow.Operations
 
-import FRP.UISF.SOE
+import FRP.UISF.Graphics
+import FRP.UISF.Render.GLUT
 import FRP.UISF.UITypes
 
-import FRP.UISF.AuxFunctions (Automaton, Time, evMap, 
-                              SEvent, ArrowTime (..), ArrowIO (..),
-                              asyncE, asyncV)
+import FRP.UISF.AuxFunctions
+    (SEvent, Time, DeltaT, getDeltaT, accumTime, evMap)
+import FRP.UISF.Asynchrony
+    (ArrowIO (..), Automaton, asyncE, asyncEOn, asyncV)
 
-import Control.Monad (when)
-import qualified Graphics.UI.GLFW as GLFW (sleep)
+import Control.Monad (when, unless)
+import Data.Foldable (mapM_)
 import Control.Concurrent
 import Control.DeepSeq
 import Data.IORef
@@ -62,7 +64,7 @@ 
 data UISF b c = UISF 
   { uisfLayout :: Flow -> Layout,
-    uisfFun    :: (CTX, Focus, Time, UIEvent, b) -> 
+    uisfFun    :: (CTX, Focus, DeltaT, UIEvent, b) -> 
                   IO (DirtyBit, Focus, Graphic, TerminationProc, c, UISF b c) }
 
 instance Category UISF where
@@ -121,10 +123,20 @@     fun inps = do
       d <- iod
       (db, foc', g, tp, c, uisff') <- uisfFun (f d) inps
-      return (db, foc', g, tp, c, uisff')
+      return (db, foc', g, tp, c, setDirty uisff')
+    setDirty (UISF l f) = UISF l h where
+      h inp = do
+        (_, foc', g, tp, c, uisf) <- f inp
+        return (True, foc', g, tp, c, uisf)
+  terminalAIO = addTerminationProc
 
-instance ArrowTime UISF where
-  time = getTime
+instance ArrowReader DeltaT UISF where
+  readState = getDeltaTime
+  newReader (UISF l f) = UISF l h where
+    h (ctx, foc, dt, e, (b, dt')) = do
+      (db, foc', g, tp, c, uisf) <- f (ctx, foc, dt', e, b)
+      return (db, foc', g, tp, c, newReader uisf)
+    
 
 
 ------------------------------------------------------------
@@ -161,13 +173,23 @@ ------------------------------------------------------------
 
 -- | Get the time signal from a UISF.
-getTime      :: UISF () Time
-getTime      = mkUISF nullLayout (\(_,f,t,_,_) -> (False, f, nullGraphic, nullTP, t))
+getDeltaTime :: UISF b DeltaT
+getDeltaTime = mkUISF nullLayout (\(_,f,dt,_,_) -> (False, f, nullGraphic, nullTP, dt))
 
+{-# DEPRECATED getCTX "As of UISF-0.4.0.0, use withCTX instead" #-}
 -- | Get the context signal from a UISF.
+--   This has been deprecated in favor of withCTX as it can provide 
+--   misleading information.
 getCTX       :: UISF () CTX
 getCTX       = mkUISF nullLayout (\(c,f,_,_,_) -> (False, f, nullGraphic, nullTP, c))
 
+-- | Provide the context signal to the UISF.
+withCTX :: UISF (CTX,a) b -> UISF a b
+withCTX (UISF l f) = UISF l h where
+  h (ctx, foc, t, e, b) = do
+    (db, foc', g, tp, c, uisf) <- f (ctx, foc, t, e, (ctx,b))
+    return (db, foc', g, tp, c, withCTX uisf)
+
 -- | Get the UIEvent signal from a UISF.
 getEvents    :: UISF () UIEvent
 getEvents    = mkUISF nullLayout (\(_,f,_,e,_) -> (False, f, nullGraphic, nullTP, e))
@@ -193,43 +215,12 @@   returnA -< p
 
 -- | This function creates a UISF with the given parameters.
-mkUISF :: Layout -> ((CTX, Focus, Time, UIEvent, a) -> (DirtyBit, Focus, Graphic, TerminationProc, b)) -> UISF a b
+mkUISF :: Layout -> ((CTX, Focus, DeltaT, UIEvent, a) -> (DirtyBit, Focus, Graphic, TerminationProc, b)) -> UISF a b
 mkUISF l f = UISF (const l) fun where
   fun inps = let (db, foc, g, tp, b) = f inps in return (db, foc, g, tp, b, mkUISF l f)
 
 
 ------------------------------------------------------------
--- * UISF Lifting
-------------------------------------------------------------
--- $lifting The following two functions are for lifting Automatons to UISFs.  
-
--- | This is the standard one that appropriately keeps track of 
---   simulated time vs real time.  
---
--- The clockrate is the simulated rate of the input signal function.
--- The buffer is the number of time steps the given signal function is allowed 
--- to get ahead of real time.  The real amount of time that it can get ahead is
--- the buffer divided by the clockrate seconds.
--- The output signal function takes and returns values in real time.  The return 
--- values are the list of bs generated in the given time step, each time stamped.
--- 
--- Note that the returned list may be long if the clockrate is much 
--- faster than real time and potentially empty if it's slower.
--- Note also that the caller can check the time stamp on the element 
--- at the end of the list to see if the inner, "simulated" signal 
--- function is performing as fast as it should.
-asyncUISFV :: NFData b => Double -> Double -> Automaton (->) a b -> UISF a [(b, Time)]
-asyncUISFV clockrate buffer sf = proc a -> do
-  t <- time -< ()
-  asyncV clockrate buffer (addTerminationProc . killThread) sf -< (a, t)
-
-
--- | We can also lift a signal function to a UISF asynchronously.
-asyncUISFE :: NFData b => Automaton (->) a b -> UISF (SEvent a) (SEvent b)
-asyncUISFE = asyncE (addTerminationProc . killThread)
-
-
-------------------------------------------------------------
 -- * Layout Transformers
 ------------------------------------------------------------
 
@@ -273,9 +264,9 @@ -- | Add space padding around a widget.
 pad  :: (Int, Int, Int, Int) -> UISF a b -> UISF a b
 pad args@(w,n,e,s) (UISF fl f) = UISF layout h where
-  layout ctx = let l = fl ctx in l { hFixed = hFixed l + w + e, vFixed = vFixed l + n + s }
-  h (ctx, foc, t, e, b) = let ((x,y),(bw,bh)) = bounds ctx in do
-    (db, foc', g, tp, c, uisf) <- f (ctx {bounds = ((x + w, y + n),(bw,bh))}, foc, t, e, b)
+  layout ctx = let l = fl ctx in l { wFixed = wFixed l + w + e, hFixed = hFixed l + n + s }
+  h (ctx, foc, t, evt, b) = let ((x,y),(bw,bh)) = bounds ctx in do
+    (db, foc', g, tp, c, uisf) <- f (ctx {bounds = ((x + w, y + n),(bw-w-e,bh-n-s))}, foc, t, evt, b)
     return (db, foc', g, tp, c, pad args uisf)
 
 
@@ -293,13 +284,24 @@   , uiTitle :: String       -- ^ The UI window's title.
   , uiSize :: Dimension     -- ^ The size of the UI window.
   , uiInitFlow :: Flow      -- ^ The initial Flow setting.
-  , uiTickDelay :: Double   -- ^ How long the UI will sleep between clock 
+  , uiTickDelay :: DeltaT   -- ^ How long the UI will sleep between clock 
                             --   ticks if no events are detected.  This 
                             --   should be probably be set to O(milliseconds), 
                             --   but it can be set to 0 for better performance 
                             --   (but also higher CPU usage)
+  , uiCloseOnEsc :: Bool    -- ^ Should the UI window close when the user 
+                            --   presses the escape key?
+  , uiBackground :: RGB     -- ^ The default color of the UI window background.
 }
 
+instance Show UIParams where
+  show p = "{UIParams containing: title="++show (uiTitle p)++
+           ", size="++show (uiSize p)++
+           ", initial flow="++show (uiInitFlow p)++
+           ", tick delay="++show (uiTickDelay p)++
+           ", background color="++show (uiBackground p)++
+           (if uiCloseOnEsc p then ", and closes on ESC}" else ", and does not close on ESC}")
+
 -- | This is the default UIParams value and what is used in runUI'.
 defaultUIParams :: UIParams
 defaultUIParams = UIParams {
@@ -308,7 +310,9 @@     uiTitle = "User Interface",
     uiSize = (300, 300),
     uiInitFlow = TopDown,
-    uiTickDelay = 0.001
+    uiTickDelay = 0.001,
+    uiCloseOnEsc = False,
+    uiBackground = colorToRGB LightBeige
 }
 
 defaultCTX :: Flow -> Dimension -> CTX
@@ -328,70 +332,59 @@ runUI p sf = do
     tref <- newIORef Nothing
     uiInitialize p
-    w <- openWindowEx (uiTitle p) (Just (0,0)) (Just $ uiSize p) drawBufferedGraphic
-    finally (go tref w) (terminate tref w)
+    w <- openWindow (uiBackground p) (uiTitle p) (uiSize p)
+    finally (go tref w defaultFocus 0 sf) (terminate tref w)
   where
     terminate tref w = do
-      closeWindow w
+      setGraphics w (nullGraphic, False)
+      mwindow <- getWindow w
+      mapM_ closeWindow mwindow
       tproc <- readIORef tref
+      --sequence_ tproc
       case tproc of
         Nothing -> return ()
         Just t -> t
-      --mapM_ killThread tids
       uiClose p
-    go tref w = runGraphics $ do
-      (events, addEv) <- makeStream
-      let pollEvents = windowUser (uiTickDelay p) w addEv
-      -- poll events before we start to make sure event queue isn't empty
-      t0 <- timeGetTime
-      pollEvents
-      let render :: Bool -> [UIEvent] -> Focus -> UISF () () -> IO ()
-          render drawit' (inp:inps) lastFocus uisf = do
-            wSize <- getMainWindowSize
-            t <- timeGetTime
-            let rt = t - t0
+    go tref w lastFocus tprev uisf = do
+      mwindow <- getWindow w
+      -- If the window is not there, GL has closed it.  Time to stop.
+      case mwindow of
+        Nothing -> return ()
+        Just _ -> do
+          ev <- getNextEvent' w
+          -- If the event is the Escape key, that may be a signal to stop.
+          let die = case ev of
+                (SKey KeyEsc _ True) -> True
+                _ -> False
+          unless (uiCloseOnEsc p && die) $ do
+            -- If there's no event (NoUIEvent), then sleep for tickdelay before processing.
+            when (ev == NoUIEvent) (threadDelay $ truncate $ uiTickDelay p * 1000000)
+            -- For any other event, immediately process it.
+            wSize <- getWindowDim w
+            t <- getElapsedGUITime w
             let ctx = defaultCTX (uiInitFlow p) wSize
-            (dirty, foc, graphic, tproc', _, uisf') <- uisfFun uisf (ctx, lastFocus, rt, inp, ())
-            -- delay graphical output when event queue is not empty
-            setGraphic' w graphic
-            let drawit = dirty || drawit'
-                foc' = resetFocus foc
+            (dirty, foc, graphic, tproc', _, uisf') <- uisfFun uisf (ctx, lastFocus, t-tprev, ev, ())
+            let foc' = resetFocus foc
+                -- When we're in the middle of setting focus, don't set 
+                -- the graphic yet.  Wait until it's done, and then set it.
+                dirty' = case (snd lastFocus, snd foc') of
+                    (_, SetFocusTo _) -> False
+                    (SetFocusTo _, NoFocus) -> True
+                    _ -> dirty
+            case dirty' of 
+              -- Is this deepseq even helping?
+              True -> deepseq graphic $ setGraphics w (graphic, True)
+              False -> setGraphics w (graphic, False)
             atomicModifyIORef' tref (\tproc -> (mergeTP tproc' tproc, ()))
-            foc' `seq` case inp of
-              -- Timer only comes in when we are done processing user events
-              NoUIEvent -> do 
-                -- output graphics 
-                when drawit $ setDirty w
-                quit <- pollEvents
-                if quit then return ()
-                        else render False inps foc' uisf'
-              _ -> render drawit inps foc' uisf'
-          render _ [] _ _ = return ()
-      render True events defaultFocus sf
-      -- wait a little while before all Midi messages are flushed
-      GLFW.sleep 0.5
-
-windowUser :: Double -> Window -> (UIEvent -> IO ()) -> IO Bool
-windowUser tickDelay w addEv = do 
-  quit <- getEvents
-  addEv NoUIEvent
-  return quit
- where 
-  getEvents :: IO Bool
-  getEvents = do
-    mev <- maybeGetWindowEvent tickDelay w
-    case mev of
-      Nothing -> return False
-      Just e  -> case e of
--- There's a bug somewhere with GLFW that makes pressing ESC freeze up 
--- GHCi (specifically when calling GLFW.closeWindow), so I've removed this.
---        SKey GLFW.ESC True -> closeWindow w >> return True
-        Closed          -> return True
-        _               -> addEv e >> getEvents
-
-makeStream :: IO ([a], a -> IO ())
-makeStream = do
-  ch <- newChan
-  contents <- getChanContents ch
-  return (contents, writeChan ch)
-
+            go tref w foc' t uisf'
+    -- this getNextEvent' function is implementing a possible performance boost.
+    -- TODO: Does this actually help at all?
+    getNextEvent' w = do
+      e <- getNextEvent w
+      case e of
+        MouseMove _ -> do
+          e' <- peekNextEvent w
+          case e' of
+            MouseMove _ -> getNextEvent' w
+            _ -> return e
+        _ -> return e
FRP/UISF/UITypes.hs view
@@ -9,10 +9,37 @@ 
 {-# LANGUAGE RecursiveDo #-}
 
-module FRP.UISF.UITypes where
+module FRP.UISF.UITypes (
+  -- * UI Types
+  -- $uitypes
+  TerminationProc(..), nullTP, mergeTP,
+  -- * Rendering Context
+  CTX(..), Flow(..),
+  -- * UI Layout
+  makeLayout, LayoutType(..), nullLayout, Layout(..),
+  -- * Context and Layout Functions
+  divideCTX, mergeLayout, 
+  -- * Graphics
+  mergeGraphics,
+  -- * System State
+  DirtyBit, Focus, WidgetID, FocusInfo(..),
+  -- * UIEvent
+  UIEvent(..),
+  Key(..),
+  SpecialKey(..),
+  MouseButton(..),
+  -- * Key State Checks
+  hasShiftModifier, hasCtrlModifier, hasAltModifier,
+  isKeyPressed,
+  -- * Framework Connections
+  -- $frameworkconnections
+  updateKeyState
+  ) where
 
-import FRP.UISF.SOE
-import FRP.UISF.AuxFunctions (mergeE)
+import FRP.UISF.Graphics
+import Data.IORef
+import Data.List (delete)
+import System.IO.Unsafe (unsafePerformIO)
 
 ------------------------------------------------------------
 -- * UI Types
@@ -70,7 +97,10 @@ 
 -- | A method for merging two termination procedures.
 mergeTP :: TerminationProc -> TerminationProc -> TerminationProc
-mergeTP = mergeE (>>)
+mergeTP Nothing     Nothing     = Nothing
+mergeTP le@(Just _) Nothing     = le
+mergeTP Nothing     re@(Just _) = re
+mergeTP (Just l)    (Just r)    = Just (l >> r)
 
 
 ------------------------------------------------------------
@@ -99,10 +129,6 @@ 
 -- | Flow determines widget ordering.
 data Flow = TopDown | BottomUp | LeftRight | RightLeft deriving (Eq, Show)
--- | A dimension specifies size.
-type Dimension = (Int, Int)
--- | A rectangle has a corner point and a dimension.
-type Rect = (Point, Dimension)
 
 
 ------------------------------------------------------------
@@ -123,10 +149,10 @@ makeLayout :: LayoutType  -- ^ Horizontal Layout information
            -> LayoutType  -- ^ Vertical Layout information
            -> Layout
-makeLayout (Fixed h) (Fixed v) = Layout 0 0 h v 0 0
-makeLayout (Stretchy minW) (Fixed v) = Layout 1 0 0 v minW 0
-makeLayout (Fixed h) (Stretchy minH) = Layout 0 1 h 0 0 minH
-makeLayout (Stretchy minW) (Stretchy minH) = Layout 1 1 0 0 minW minH
+makeLayout (Fixed w) (Fixed h) = Layout 0 0 w h 0 0 0
+makeLayout (Stretchy wMin) (Fixed h) = Layout 1 0 0 h wMin 0 0
+makeLayout (Fixed w) (Stretchy hMin) = Layout 0 1 w 0 0 hMin 0
+makeLayout (Stretchy wMin) (Stretchy hMin) = Layout 1 1 0 0 wMin hMin 0
 
 -- | A dimension can either be:
 data LayoutType = 
@@ -137,29 +163,32 @@ 
 -- | The null layout is useful for \"widgets\" that do not appear or 
 --   take up space on the screen.
-nullLayout = Layout 0 0 0 0 0 0
+nullLayout = NullLayout --Layout 0 0 0 0 0 0 0
 
 
 -- | More complicated layouts can be manually constructed with direct 
 -- access to the Layout data type.
 --
--- 1. hFill and vFill specify how much stretching space (in comparative 
---    units) in the horizontal and vertical directions should be 
---    allocated for this widget.
+-- 1. wStretch and hStretch specify how much stretching space (in comparative 
+--    units) in the width and height should be allocated for this widget.
 -- 
--- 2. hFixed and vFixed specify how much non-stretching space (in pixels) 
+-- 2. wFixed and hFixed specify how much non-stretching space (in pixels) 
 --    of width and height should be allocated for this widget.
 -- 
--- 3. minW and minH specify minimum values (in pixels) of width and height 
+-- 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.
 
-data Layout = Layout
-  { hFill  :: Int
-  , vFill  :: Int
-  , hFixed :: Int
-  , vFixed :: Int
-  , minW   :: Int
-  , minH   :: Int
+data Layout = NullLayout | Layout
+  { wStretch :: Int
+  , hStretch :: Int
+  , wFixed   :: Int
+  , hFixed   :: Int
+  , wMin     :: Int
+  , hMin     :: Int
+  , lFill    :: Int
   } deriving (Eq, Show)
 
 
@@ -174,41 +203,33 @@ -- | Divides the CTX among the two given layouts.
 
 divideCTX :: CTX -> Layout -> Layout -> (CTX, CTX)
-divideCTX ctx@(CTX a ((x, y), (w, h)) c) 
-          ~(Layout wFill  hFill  wFixed  hFixed  wMin  hMin) 
-          ~(Layout wFill' hFill' wFixed' hFixed' wMin' hMin') =
-  if c then (ctx, ctx) else
-  case a of
-    TopDown   -> (CTX a ((x, y),                 (w1T, h1T)) c, 
-                  CTX a ((x, y + h1T),           (w2T, h2T)) c)
-    BottomUp  -> (CTX a ((x, y + h - h1T),       (w1T, h1T)) c, 
-                  CTX a ((x, y + h - h1T - h2T), (w2T, h2T)) c)
-    LeftRight -> (CTX a ((x, y),                 (w1L, h1L)) c, 
-                  CTX a ((x + w1L, y),           (w2L, h2L)) c)
-    RightLeft -> (CTX a ((x + w - w1L, y),       (w1L, h1L)) c, 
-                  CTX a ((x + w - w1L - w2L, y), (w2L, h2L)) c)
-  where
-    -- The commented out code here forces the contexts to match exactly 
-    -- what the layout requests.  The code in place matches to the first 
-    -- layout and then gives the rest of the context to the second.
-    -- A more robust design may require a special "filler" layout that 
-    -- is not stretchy but will accept any leftover pixels.  We could 
-    -- then have a filler widget that is essentially (arr id) with this 
-    -- special layout.
-    wportion fill = div' (fill * (w - wFixed - wFixed')) (wFill + wFill')
-    (w1L,w2L) = let w1 = wFixed  + max wMin  (wportion wFill)
-                    w2 = wFixed' + max wMin' (wportion wFill')
-                in (w1, w-w1) --if w1+w2 > w then (w1, w-w1) else (w1, w2)
-    h1L = h --max hMin  (if hFill  == 0 then hFixed  else h)
-    h2L = h --max hMin' (if hFill' == 0 then hFixed' else h)
-    hportion fill = div' (fill * (h - hFixed - hFixed')) (hFill + hFill')
-    (h1T,h2T) = let h1 = hFixed  + max hMin  (hportion hFill)
-                    h2 = hFixed' + max hMin' (hportion hFill')
-                in (h1, h-h1) --if h1+h2 > h then (h1, h-h1) else (h1, h2)
-    w1T = w --max wMin  (if wFill  == 0 then wFixed  else w)
-    w2T = w --max wMin' (if wFill' == 0 then wFixed' else w)
-    div' b 0 = 0
-    div' b d = div b d
+divideCTX ctx@(CTX a ((x, y), (w, h)) c) l1 l2 = if c then (ctx,ctx) else case (l1,l2) of
+  (NullLayout, _) -> (CTX a ((0,0),(0,0)) c, ctx)
+  (_, NullLayout) -> (ctx, CTX a ((0,0),(0,0)) c)
+  ((Layout wStretch  hStretch  wFixed  hFixed  wMin  hMin  lFill), 
+   (Layout wStretch' hStretch' wFixed' hFixed' wMin' hMin' lFill')) ->
+      case a of
+        TopDown   -> (CTX a ((x, y),                 (w1T, h1T)) c, 
+                      CTX a ((x, y + h1T),           (w2T, h2T)) c)
+        BottomUp  -> (CTX a ((x, y + h - h1T),       (w1T, h1T)) c, 
+                      CTX a ((x, y + h - h1T - h2T), (w2T, h2T)) c)
+        LeftRight -> (CTX a ((x, y),                 (w1L, h1L)) c, 
+                      CTX a ((x + w1L, y),           (w2L, h2L)) c)
+        RightLeft -> (CTX a ((x + w - w1L, y),       (w1L, h1L)) c, 
+                      CTX a ((x + w - w1L - w2L, y), (w2L, h2L)) c)
+      where
+        (w1L,w2L,w1T,w2T) = calc w wStretch wStretch' wFixed wFixed' wMin wMin' lFill lFill'
+        (h1T,h2T,h1L,h2L) = calc h hStretch hStretch' hFixed hFixed' hMin hMin' lFill lFill'
+        calc len stretch stretch' fixed fixed' lmin lmin' fill fill' = (st1, st2, fi1, fi2) where
+          portion s = div' (s * (len - fixed - fixed')) (stretch + stretch')
+          (st1,st2) = let u = min len $ fixed  + max lmin  (portion stretch)
+                          v =           fixed' + max lmin' (portion stretch')
+                          por f = div' (f * (len - u - v)) (fill + fill')
+                      in if u+v > len then (u, len-u) else (u + por fill, v + por fill')
+          fi1 = if fill  > 0 then len else max lmin  (if stretch  == 0 then fixed  else len)
+          fi2 = if fill' > 0 then len else max lmin' (if stretch' == 0 then fixed' else len)
+        div' b 0 = 0
+        div' b d = div b d
 
 
 -----------------
@@ -217,15 +238,18 @@ -- | Merge two layouts into one.
 
 mergeLayout :: Flow -> Layout -> Layout -> Layout
-mergeLayout a (Layout n m u v minw minh) (Layout n' m' u' v' minw' minh') = 
+mergeLayout a NullLayout l = l
+mergeLayout a l NullLayout = l
+mergeLayout a (Layout n m u v minw minh lFill) (Layout n' m' u' v' minw' minh' lFill') = 
   case a of
-    TopDown   -> Layout (max' n n') (m + m') (max u u') (v + v') (max minw minw') (minh + minh')
-    BottomUp  -> Layout (max' n n') (m + m') (max u u') (v + v') (max minw minw') (minh + minh')
-    LeftRight -> Layout (n + n') (max' m m') (u + u') (max v v') (minw + minw') (max minh minh')
-    RightLeft -> Layout (n + n') (max' m m') (u + u') (max v v') (minw + minw') (max minh minh')
+    TopDown   -> Layout (max' n n') (m + m') (max u u') (v + v') (max minw minw') (minh + minh') lFill''
+    BottomUp  -> Layout (max' n n') (m + m') (max u u') (v + v') (max minw minw') (minh + minh') lFill''
+    LeftRight -> Layout (n + n') (max' m m') (u + u') (max v v') (minw + minw') (max minh minh') lFill''
+    RightLeft -> Layout (n + n') (max' m m') (u + u') (max v v') (minw + minw') (max minh minh') lFill''
   where
     max' 0 0 = 0
     max' _ _ = 1
+    lFill'' = lFill + lFill'
 
 
 ------------------------------------------------------------
@@ -237,11 +261,11 @@ -- based on their layouts and the context.
 -- TODO: Make sure this works as well as it should
 mergeGraphics :: CTX -> (Graphic, Layout) -> (Graphic, Layout) -> Graphic
-mergeGraphics ctx (g1, l1) (g2, l2) = case (l1 == nullLayout, l2 == nullLayout) of
-  (True,  True)  -> nullGraphic
-  (True,  False) -> g2
-  (False, True)  -> g1
-  (False, False) -> overGraphic g2 g1
+mergeGraphics ctx (g1, l1) (g2, l2) = case (l1, l2) of
+  (NullLayout,  NullLayout)  -> nullGraphic
+  (NullLayout,  _) -> g2
+  (_, NullLayout)  -> g1
+  (_, _) -> overGraphic g2 g1
 
 
 ------------------------------------------------------------
@@ -281,5 +305,164 @@   deriving (Show, Eq)
 
 
+------------------------------------------------------------
+-- * UIEvent
+------------------------------------------------------------
+
+
+-- | 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 'Key's that were down when the given 
+--  event took place.
+data UIEvent = 
+  -- | A Key UIEvent indicates that the user has typed a regular key 
+  --  on his/her keyboard.  These will either be upper or lowercase 
+  --  characters.
+    Key {
+      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 {
+      skey :: SpecialKey,
+      modifiers :: [Key],
+      isDown :: Bool
+    }
+  -- | A Button UIEvent indicates that the user has pressed a mouse 
+  --  button.
+  | Button {
+     pt :: Point,
+     mbutton :: MouseButton,
+     isDown :: Bool
+    }
+  -- | Every time the mouse moves, a MouseMove UIEvent will fire.
+  | MouseMove {
+      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.).
+  | NoUIEvent
+ deriving (Eq,Show)
+
+
+-------------------
+-- Key state
+-------------------
+{- $frameworkconnections
+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.
+-}
+
+-- | The global IORef storing the state of all current key presses.
+keyState :: IORef [Key]
+keyState = unsafePerformIO $ newIORef []
+
+-- | 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.
+updateKeyState :: Key   -- ^ The Key pressed/released.
+               -> Bool  -- ^ True if pressed, False if released.
+               -> IO [Key]  -- ^ The updated key state.
+updateKeyState k s = case s of
+    True  -> atomicModifyIORef keyState (dup . add)
+    False -> atomicModifyIORef keyState (dup . remove)
+  where
+    add ks = if k `elem` ks then ks else k:ks
+    remove ks = delete k ks
+    dup x = (x,x)
+
+-- | This is a convenience function that tests whether either of the 
+--  right or left shift keys is in the given list.
+hasShiftModifier :: [Key] -> Bool
+hasShiftModifier ks = elem (SpecialKey KeyShiftL) ks || elem (SpecialKey KeyShiftR) ks
+
+-- | This is a convenience function that tests whether either of the 
+--  right or left control keys is in the given list.
+hasCtrlModifier :: [Key] -> Bool
+hasCtrlModifier ks = elem (SpecialKey KeyCtrlL) ks || elem (SpecialKey KeyCtrlR) ks
+
+-- | This is a convenience function that tests whether either of the 
+--  right or left alt keys is in the given list.
+hasAltModifier :: [Key] -> Bool
+hasAltModifier ks = elem (SpecialKey KeyAltL) ks || elem (SpecialKey KeyAltR) ks
+
+-- | Checks the global key state to determine whether the given key is 
+--  currently pressed down.
+isKeyPressed :: Key -> IO Bool
+isKeyPressed k = do
+    ks <- readIORef keyState
+    return $ elem k ks
+
+
+
+
+
+-- | A Key can either be a character, a special key, or a mouse button.
+data Key
+   = Char Char
+   | SpecialKey SpecialKey
+   | MouseButton MouseButton
+   deriving ( Eq, Ord, Show )
+
+-- | 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 SpecialKey
+   = KeyF1
+   | KeyF2
+   | KeyF3
+   | KeyF4
+   | KeyF5
+   | KeyF6
+   | KeyF7
+   | KeyF8
+   | KeyF9
+   | KeyF10
+   | KeyF11
+   | KeyF12
+   | KeyLeft
+   | KeyUp
+   | KeyRight
+   | KeyDown
+   | KeyPageUp
+   | KeyPageDown
+   | KeyHome
+   | KeyEnd
+   | KeyInsert
+   | KeyNumLock
+   | KeyBegin
+   | KeyDelete
+   | KeyShiftL
+   | KeyShiftR
+   | KeyCtrlL
+   | KeyCtrlR
+   | KeyAltL
+   | KeyAltR
+   | KeyEnter
+   | KeyTab
+   | KeyEsc
+   | KeyBackspace
+   | KeyUnknown Int
+   deriving ( Eq, Ord, Show )
+
+-- | The standard mouse buttons are represented, but for specialty mice, 
+--  one can also use the 'AdditionalButton' value.
+data MouseButton
+   = LeftButton
+   | MiddleButton
+   | RightButton
+   | WheelUp
+   | WheelDown
+   | AdditionalButton Int
+   deriving ( Eq, Ord, Show )
 
 
FRP/UISF/Widget.hs view
@@ -7,43 +7,23 @@ -- Maintainer  :  dwc@cs.yale.edu
 -- Stability   :  experimental
 --
--- A simple Graphical User Interface based on FRP. It uses the SOE
--- graphics library, and draws custom widgets on the screen.
--- 
--- SOE graphics uses OpenGL as the primitive drawing routine, and
--- GLFW library to provide window and input support.
--- 
--- The monadic UI concept is borrowed from Phooey by Conal Elliott.
+-- These are the default, built-in widgets for UISF.
 
 {-# LANGUAGE RecursiveDo, Arrows, TupleSections #-}
 {-# OPTIONS_HADDOCK prune #-}
 
 module FRP.UISF.Widget where
 
-import FRP.UISF.SOE
+import FRP.UISF.Widget.Construction
+import FRP.UISF.Graphics
 import FRP.UISF.UITypes
 import FRP.UISF.UISF
-import FRP.UISF.AuxFunctions (SEvent, Time, timer, edge, delay, constA, concatA)
+import FRP.UISF.AuxFunctions
 
 import Control.Arrow
-import Data.Maybe (fromMaybe)
+import Data.Char (isSpace)
 
-------------------------------------------------------------
--- Shorthand and Helper Functions
-------------------------------------------------------------
 
--- Default padding between border and content
-padding :: Int
-padding = 3 
-
--- Introduce a shorthand for overGraphic
-(//) :: Graphic -> Graphic -> Graphic
-(//) = overGraphic
-
--- And a nice way to make a graphic under only certain conditions
-whenG :: Bool -> Graphic -> Graphic
-whenG b g = if b then g else nullGraphic
-
 ------------------------------------------------------------
 -- * Widgets
 ------------------------------------------------------------
@@ -52,29 +32,46 @@ -- Text Label --
 ----------------
 -- | Labels are always left aligned and vertically centered.
-label :: String -> UISF a a
+label :: UITexty s => s -> UISF a a
 label s = mkBasicWidget layout draw
   where
-    (minw, minh) = (length s * 8 + padding * 2, 16 + padding * 2)
+    (minw, minh) = (textWidth s + padding * 2, textHeight s + padding * 2)
     layout = makeLayout (Fixed minw) (Fixed minh)
     draw ((x, y), (w, h)) = withColor Black $ text (x + padding, y + (h `div` 2) - 8) s
 
 -----------------
 -- Display Box --
 -----------------
+-- | DisplayField is an output widget showing the instantaneous value of
+--  a signal of Strings.  It will show the String over how ever much 
+--  space it has available to it.  The static argument will decide what 
+--  to cut off in the case where it does not have space to show the 
+--  entire String: if given True, it will prefer the older characters 
+--  (cutting off later text), and if given False, it will prefer the 
+--  newer characters (cutting off older ones.
+displayField :: UITexty s => WrapSetting -> UISF s ()
+displayField wrap = arr toUIText >>> mkWidget (toUIText "") layout (\v v' _ _ -> ((), v, v /= v')) draw 
+  where
+    minh = textHeight "" + padding * 2
+    layout = makeLayout (Stretchy $ padding * 2) (Stretchy minh)
+    draw b@((x,y), (w, h)) _ s = 
+      let th = textHeight s
+          w' = w - padding * 2
+          (pts', texts) = prepText wrap 1 ((x+padding,y+padding), (w-padding*2, h-padding*2)) s
+          pts = map (\(x,y) -> (x+padding,y+padding)) pts'
+      in withColor Black (textLines $ zip pts $ map (fst . textWithinPixels w') texts)
+         // shadowBox pushed b 
+         // withColor White (rectangleFilled b)
+
+
 -- | DisplayStr is an output widget showing the instantaneous value of
 --   a signal of strings.
 displayStr :: UISF String ()
-displayStr = mkWidget "" d (\v v' _ _ -> ((), v, v /= v')) draw 
-  where
-    minh = 16 + padding * 2
-    d = makeLayout (Stretchy 8) (Fixed minh)
-    draw b@((x,y), (w, _h)) _ s = 
-      let n = (w - padding * 2) `div` 8
-      in withColor Black (text (x + padding, y + padding) (take n s)) 
-         // box pushed b 
-         // withColor White (block b)
+displayStr = setLayout layout $ displayField NoWrap
+  where layout = makeLayout (Stretchy $ padding * 2) (Fixed $ textHeight "" + padding * 2)
 
+
+
 -- | display is a widget that takes any show-able value and displays it.
 display :: Show a => UISF a ()
 display = arr show >>> displayStr
@@ -91,149 +88,221 @@ --------------
 -- Text Box --
 --------------
--- | Textbox is a widget showing the instantaneous value of a signal of 
--- strings.
--- 
--- The textbox widget will often be used with ArrowLoop (the rec keyword).  
--- However, it uses 'delay' internally, so there should be no fear of a blackhole.
--- 
--- The textbox widget supports mouse clicks and typing as well as the 
--- left, right, end, home, delete, and backspace special keys.
-textbox :: UISF String String
-textbox = focusable $ 
-  conjoin $ proc s -> do
-    inFocus <- isInFocus -< ()
-    k <- getEvents -< ()
-    ctx <- getCTX -< ()
-    rec let (s', i) = if inFocus then update s iPrev ctx k else (s, iPrev)
-        iPrev <- delay 0 -< i
-    displayStr -< seq i s'
-    inf <- delay False -< inFocus
-    b <- if inf then timer -< 0.5 else returnA -< Nothing
-    b' <- edge -< not inFocus --For use in drawing the cursor
-    rec willDraw <- delay True -< willDraw'
-        let willDraw' = maybe willDraw (const $ not willDraw) b --if isJust b then not willDraw else willDraw
-    canvas' displayLayout drawCursor -< case (inFocus, b, b', i == iPrev) of
-              (True,  Just _, _, _) -> Just (willDraw, i)
-              (True,  _, _, False)  -> Just (willDraw, i)
-              (False, _, Just _, _) -> Just (False, i)
-              _ -> Nothing
-    returnA -< s'
-  where
-    minh = 16 + padding * 2
-    displayLayout = makeLayout (Stretchy 8) (Fixed minh)
-    update s  i _ (Key c _ True)          = (take i s ++ [c] ++ drop i s, i+1)
-    update s  i _ (SKey BACKSPACE _ True) = (take (i-1) s ++ drop i s, max (i-1) 0)
-    update s  i _ (SKey DEL       _ True) = (take i s ++ drop (i+1) s, i)
-    update s  i _ (SKey LEFT      _ True) = (s, max (i-1) 0)
-    update s  i _ (SKey RIGHT     _ True) = (s, min (i+1) (length s))
-    update s _i _ (SKey END       _ True) = (s, length s)
-    update s _i _ (SKey HOME      _ True) = (s, 0)
-    update s _i c (Button (x,_) True True) = (s, min (length s) $ (x - xoffset c) `div` 8)
-    update s  i _ _                        = (s, max 0 $ min i $ length s)
-    drawCursor (False, _) _ = nullGraphic
-    drawCursor (True, i) (w,_h) = 
-        let linew = padding + i*8
-        in if linew > w then nullGraphic else withColor Black $
-            line (linew, padding) (linew, 16+padding)
-    xoffset = fst . fst . bounds
+-- | The textbox widget creates a one line field where users can 
+--  enter text.  It supports mouse clicks and typing as well as the 
+--  left, right, end, home, delete, and backspace special keys.
+--
+--  The value displayed can be generated by mouse and keyboard events, 
+--  but it can also be set programmatically by providing the widget's 
+--  input stream with an event containing the value to display.  By 
+--  using rec and delay, one can update the contents based on e.g. 
+--  other widgets.
+--
+--  The static argument provides the textbox with initial text.
+textbox :: UITexty s => s -> UISF (SEvent s) String
+textbox s = (setLayout layout . textField CharWrap) s >>> arr uitextToString
+  where layout = makeLayout (Stretchy $ padding * 2) (Fixed $ textHeight "" + padding * 2)
 
--- | This variant of the textbox takes a static argument that is 
---   the initial value in the textbox.  Then, it takes a stream of 
---   'SEvent String' and only externally updates the contents of the 
---   textbox when an event occurs.
-textboxE :: String -> UISF (SEvent String) String
-textboxE startingVal = proc ms -> do
-  rec s  <- delay startingVal -< ts
-      ts <- textbox -< maybe s id ms
+{-# DEPRECATED textboxE "As of UISF-0.4.0.0, use textbox instead" #-}
+textboxE :: UITexty s => s -> UISF (SEvent s) String
+textboxE = textbox
+
+-- | The textbox' variant of textbox contains no internal state about 
+--  the text it displays.  Thus, it must be paired with rec and delay 
+--  and used bidirectionally to be effective.
+textbox' :: UITexty s => UISF s UIText
+textbox' = setLayout layout $ textField' CharWrap
+  where layout = makeLayout (Stretchy $ padding * 2) (Fixed $ textHeight "" + padding * 2)
+
+-- | TextFields are like textboxes but can support multiple lines.  By 
+--  default, they are stretchy in the vertical dimension.
+textField :: UITexty s => WrapSetting -> s -> UISF (SEvent s) UIText
+textField wrap startingVal = proc ms -> do
+  rec s  <- delay $ toUIText startingVal -< ts
+      ts <- textField' wrap -< maybe s toUIText ms
   returnA -< ts
 
+-- | A variant of textField that contains no internal state about the 
+--  text it displays.
+textField' :: UITexty s => WrapSetting -> UISF s UIText
+textField' wrap = focusable $ mkWidget (toUIText "",0) layout process draw 
+  where
+    paddedRect :: Rect -> Rect
+    paddedRect ((x,y), (w, h)) = ((x+padding,y+padding), (w-padding*2, h-padding*2))
+    layout = makeLayout (Stretchy $ padding * 2) (Stretchy $ textHeight "" + padding * 2)
+    draw b@((x,y), (w, h)) inFocus (s,i) = 
+      let texth = textHeight s
+          w' = w - padding * 2
+          b' = paddedRect b
+          (pts, texts) = prepText wrap 1 b' s
+          (i',j) = calcLine (i,0) (map uitextToString texts)
+          texts' = drop (j + 1 - length pts) texts
+          j' = min j (length pts - 1)
+          cursory = y + padding + j'*texth
+          cursorx = x + 1+padding + textWidth (takeUIText i' $ texts !! j)
+          cpt1 = (cursorx, cursory)
+          cpt2 = (cursorx, cursory+texth)
+      in withColor Black (textLines $ zip pts $ map (fst . textWithinPixels w') texts')
+         // whenG (inFocus && inside cpt1 b' && inside cpt2 b') 
+                  (withColor Gray $ line cpt1 cpt2)
+         // shadowBox pushed b 
+         // withColor White (rectangleFilled b)
+    calcLine ij [] = ij
+    calcLine (i,j) [s] = if i < length s then (i,j) else case reverse s of
+      '\n':_ -> (0,j+1)
+      _ -> (i,j)
+    calcLine (i,j) (s:ss) = let i' = i - length s in if i' >= 0 then calcLine (i',j+1) ss else (i,j)
+    trimTailWS :: UIText -> UIText
+    trimTailWS (UIText uit) = case reverse uit of
+      [] -> UIText uit
+      ((c,f,str):uit') -> case reverse str of
+        [] -> trimTailWS $ UIText $ reverse uit'
+        (ch:s') -> if isSpace ch then UIText $ reverse ((c,f,reverse s'):uit')
+                   else UIText uit
+    process str state@(_,i) b@((x,y),(w,_)) evt = (snew, (snew,inew), state /= (snew,inew))
+      where 
+        s = toUIText str
+        texth = textHeight s
+        (pts, texts) = prepText wrap 1 (paddedRect b) s
+        strings = map uitextToString texts
+        (i',j) = calcLine (i,0) strings
+        (snew,inew) = case evt of
+          (Key c _ True)             -> let (t,d) = splitUIText i s 
+                                        in (t `appendUIText` toUIText [c] `appendUIText` d, i+1)
+          (SKey KeyEnter     _ True) -> let (t,d) = splitUIText i s 
+                                        in (t `appendUIText` toUIText "\n" `appendUIText` d, i+1) 
+          (SKey KeyBackspace _ True) -> let (t,d) = splitUIText (i-1) s 
+                                        in (t `appendUIText` dropUIText 1 d, max (i-1) 0)
+          (SKey KeyDelete    _ True) -> let (t,d) = splitUIText i s 
+                                        in (t `appendUIText` dropUIText 1 d, i)
+          (SKey KeyLeft      _ True) -> (s, max (i-1) 0)
+          (SKey KeyRight     _ True) -> (s, min (i+1) (uitextLen s))
+          -- For KeyUp, we are on the jth line moving to the (j-1)th line.
+          -- We add up the first (j-2) lines and then add the number of characters 
+          -- in line (j-1) that take up the same pixel width as the number at i' in 
+          -- the jth line.
+          -- Note that because j is 0-indexed, we add 1 whenever we do a take.
+          (SKey KeyUp        _ True) -> (s, if j <= 0 then 0 else
+            sum (map length $ take (j-1) strings) +
+            (uitextLen $ fst $ textWithinPixels (textWidth $ takeUIText i' (texts!!j)) 
+                            $ trimTailWS (texts!!(j-1))))
+          -- KeyDown is the same as KeyUp but in the other direction.
+          (SKey KeyDown      _ True) -> (s, if j >= length texts - 1 then uitextLen s else
+            sum (map length $ take (j+1) strings) + 
+            (uitextLen $ fst $ textWithinPixels (textWidth $ takeUIText i' (texts!!j)) 
+                            $ trimTailWS (texts!!(j+1))))
+          (SKey KeyEnd       _ True) -> (s, uitextLen s)
+          (SKey KeyHome      _ True) -> (s, 0)
+          (Button (bx,by) LeftButton True) -> (s,
+            let j' = ((by - y) `div` texth) + max 0 (j - length pts)
+            in if j' >= length texts then uitextLen s 
+               else sum (map length $ take j' strings) + 
+                    (uitextLen $ fst $ textWithinPixels (bx - x) $ trimTailWS (texts!!j')))
+          _  -> (s, max 0 $ min i $ uitextLen s)
+
+
+
+
 -----------
 -- Title --
 -----------
 -- | Title frames a UI by borders, and displays a static title text.
-title :: String -> UISF a b -> UISF a b
+title :: UITexty s => s -> UISF a b -> UISF a b
 title str (UISF fl f) = UISF layout h where
-  (tw, th) = (length str * 8, 16)
+  (tw, th) = (textWidth str, textHeight str)
   drawit ((x, y), (w, h)) = 
     withColor Black (text (x + 10, y) str) 
-    // withColor' bg (block ((x + 8, y), (tw + 4, th))) 
-    // box marked ((x, y + 8), (w, h - 16))
-  layout ctx = let l = fl ctx in l { hFixed = hFixed l + tw, vFixed = vFixed l + 36 }
-                                    -- ,minW = max (tw + 20) (minW l), minH = max 36 (minH l) }
+    // withColor bg (rectangleFilled ((x + 8, y), (tw + 4, th))) 
+    // shadowBox marked ((x, y + 8), (w, h - 8))
+  layout ctx = let l = fl ctx in l { wMin = max (wMin l) tw, hFixed = hFixed l + th + 10 }
   h (CTX flow bbx@((x,y), (w,h)) cj,foc,t,inp, a) = 
-    let ctx' = CTX flow ((x + 4, y + 20), (w - 8, h - 32)) cj
+    let ctx' = CTX flow ((x + 6, y + th+2), (w - 12, h - th - 10)) cj
     in do (db, foc', g, cd, b, uisf) <- f (ctx', foc, t, inp, a)
           return (db, foc', drawit bbx // g, cd, b, title str uisf)
 
 
+-- | spacer is an empty widget that will take up empty space without 
+--  requesting any space.  This can be useful for lining up other 
+--  widgets, for instance if one wants the borders from titles to align 
+--  even when the titles are bordering widgets of slightly different 
+--  sizes.
+spacer :: UISF a a
+spacer = mkBasicWidget (Layout 0 0 0 0 0 0 1) (const nullGraphic)
+
+
 ------------
 -- Button --
 ------------
 -- | A button is a focusable input widget with a state of being on or off.  
--- It can be activated with either a button press or the enter key.
--- (Currently, there is no support for the space key due to non-special 
---  keys not having Release events.)
--- Buttons also show a static label.
+--  It can be activated with either a button press, the enter key, or the 
+--  space key.  Buttons also show a static label.
 -- 
--- The regular button is down as long as the mouse button or key press is 
--- down and then returns to up.
-button :: String -> UISF () Bool
-button = genButton False
-
--- | The sticky button, on the other hand, once 
--- pressed, remains depressed until is is clicked again to be released.
--- Thus, it looks like a button, but it behaves more like a checkbox.
-stickyButton :: String -> UISF () Bool
-stickyButton = genButton True
-
--- This is used to create the buttons.
-genButton :: Bool -> String -> UISF () Bool
-genButton sticky l = focusable $ 
-  mkWidget False d (if sticky then processSticky else processRegular) draw
+--  The regular button is down as long as the mouse button or key press is 
+--  down and then returns to up.
+button :: UITexty s => s -> UISF () Bool
+button l = focusable $ 
+  mkWidget False d process draw
   where
-    (tw, th) = (8 * length l, 16) 
+    (tw, th) = (textWidth l, textHeight l)
     (minw, minh) = (tw + padding * 2, th + padding * 2)
     d = makeLayout (Stretchy minw) (Fixed minh)
     draw b@((x,y), (w,h)) inFocus down = 
       let x' = x + (w - tw) `div` 2 + if down then 0 else -1
           y' = y + (h - th) `div` 2 + if down then 0 else -1
       in withColor Black (text (x', y') l) 
-         // whenG inFocus (box marked b)
-         // box (if down then pushed else popped) b
-    processRegular _ s b evt = (s', s', s /= s')
+         // whenG inFocus (shadowBox marked b)
+         // shadowBox (if down then pushed else popped) b
+    process _ s b evt = (s', s', s /= s')
       where 
         s' = case evt of
-          Button pt True down | pt `inside` b -> case (s, down) of
+          Button pt LeftButton down | pt `inside` b -> case (s, down) of
             (False, True) -> True
             (True, False) -> False
             _ -> s
           MouseMove pt       -> (pt `inside` b) && s
-          SKey ENTER _ down -> down
+          SKey KeyEnter _ down -> down
           Key ' ' _ down -> down
           _ -> s
-    processSticky _ s b evt = (s', s', s /= s')
-      where 
-        s' = case evt of
-          Button pt True True | pt `inside` b -> not s
-          SKey ENTER _ True -> not s
-          Key ' ' _ True -> not s
-          _ -> s
 
 
+-- | The sticky button, on the other hand, once 
+-- pressed, remains depressed until is is clicked again to be released.
+-- Thus, it looks like a button, but it behaves more like a checkbox.
+stickyButton :: UITexty s => s -> UISF () Bool
+stickyButton l = constA Nothing >>> stickyButtonS l
+
+-- | This variant of stickyButton is settable by its input stream.
+stickyButtonS :: UITexty s => s -> UISF (SEvent Bool) Bool
+stickyButtonS l = arr (fmap $ \b -> if b then 1 else 0) >>> cycleboxS d lst 0 where
+  (tw, th) = (textWidth l, textHeight l)
+  (minw, minh) = (tw + padding * 2, th + padding * 2)
+  d = makeLayout (Stretchy minw) (Fixed minh)
+  draw down b@((x,y), (w,h)) inFocus = 
+    let x' = x + (w - tw) `div` 2 + if down then 0 else -1
+        y' = y + (h - th) `div` 2 + if down then 0 else -1
+    in withColor Black (text (x', y') l) 
+       // whenG inFocus (shadowBox marked b)
+       // shadowBox (if down then pushed else popped) b
+  lst = [(draw False, False),(draw True, True)]
+
+
 ---------------
 -- Check Box --
 ---------------
 -- | Checkbox allows selection or deselection of an item.
 --   It has a static label as well as an initial state.
-checkbox :: String -> Bool -> UISF () Bool
-checkbox l state = proc _ -> do
+checkbox :: UITexty s => s -> Bool -> UISF () Bool
+checkbox l state = constA Nothing >>> checkboxS l state
+
+-- | This variant of checkbox is settable by its input stream.
+checkboxS :: UITexty s => s -> Bool -> UISF (SEvent Bool) Bool
+checkboxS l state = proc eb -> do
   rec s  <- delay state -< s'
       e  <- edge <<< toggle state d draw -< s
-      let s' = maybe s (const $ not s) e
+      let s' = maybe (maybe s (const $ not s) e) id eb
   returnA -< s'
   where
-    (tw, th) = (8 * length l, 16) 
+    (tw, th) = (textWidth l, textHeight l)
     (minw, minh) = (tw + padding * 2, th + padding * 2)
     d = makeLayout (Stretchy minw) (Fixed minh)
     draw ((x,y), (_w,h)) inFocus down = 
@@ -241,14 +310,14 @@           y' = y + (h - th) `div` 2
           b = ((x + padding + 2, y + h `div` 2 - 6), (12, 12))
       in  withColor Black (text (x', y') l) 
-          // whenG inFocus (box marked b)
+          // whenG inFocus (shadowBox marked b)
           // whenG down 
-             (withColor' gray3 $ polyline 
+             (withColor DarkBeige $ polyline 
                [(x + padding + 5, y + h `div` 2),
                 (x + padding + 7, y + h `div` 2 + 3),
                 (x + padding + 11, y + h `div` 2 - 2)])
-          // box pushed b 
-          // withColor White (block b)
+          // shadowBox pushed b 
+          // withColor White (rectangleFilled b)
 
 
 ---------------------
@@ -261,14 +330,12 @@ --   The output stream is a list of each a value that was paired with a 
 --   String value for which the check box is checked.
 checkGroup :: [(String, a)] -> UISF () [a]
-checkGroup sas = let (s, a) = unzip sas in
-  constA (repeat ()) >>> 
-  concatA (zipWith checkbox s (repeat False)) >>>
-  arr (map fst . filter snd . zip a)
+checkGroup sas = constA (repeat Nothing) >>> checkGroupS sas
 
---checkGroup :: [String] -> UISF () [Bool]
---checkGroup ss = constA (repeat ()) >>> 
---                concatA (zipWith checkbox ss (repeat False))
+checkGroupS :: [(String, a)] -> UISF [SEvent Bool] [a]
+checkGroupS sas = let (s, a) = unzip sas in
+  concatA (zipWith checkboxS s (repeat False)) >>>
+  arr (map fst . filter snd . zip a)
 
 
 -------------------
@@ -279,68 +346,93 @@ -- and the index of the initially selected one, and the widget itself 
 -- returns the continuous stream representing the index of the selected 
 -- choice.
-radio :: [String] -> Int -> UISF () Int
-radio labels i = proc _ -> do
+radio :: UITexty s => [s] -> Int -> UISF () Int
+radio labels i = constA Nothing >>> radioS labels i
+
+-- | This variant of radio is settable by its input stream.
+radioS :: UITexty s => [s] -> Int -> UISF (SEvent Int) Int
+radioS labels i = proc ei -> do
   rec s   <- delay i -< s''
       s'  <- aux 0 labels -< s
-      let s'' = maybe s id s'
+      let s'' = maybe (maybe s id s') id ei
   returnA -< s''
   where
-    aux :: Int -> [String] -> UISF Int (SEvent Int)
+    --aux :: Int -> [String] -> UISF Int (SEvent Int)
     aux _ [] = arr (const Nothing)
     aux j (l:ls) = proc n -> do
       u <- edge <<< toggle (i == j) d draw -< n == j
       v <- aux (j + 1) ls -< n
       returnA -< maybe v (const $ Just j) u
       where
-        (tw, th) = (8 * length l, 16) 
+        (tw, th) = (textWidth l, textHeight l)
         (minw, minh) = (tw + padding * 2, th + padding * 2)
         d = makeLayout (Stretchy minw) (Fixed minh)
         draw ((x,y), (_w,h)) inFocus down = 
-          let x' = x + padding + 16 
-              y' = y + (h - th) `div` 2
-          in  withColor Black (text (x', y') l) 
-              // whenG down (circle gray3 (x,y) (5,6) (9,10))
-              // circle gray3 (x,y) (2,3) (12,13) 
-              // circle gray0 (x,y) (2,3) (13,14) 
-              // whenG inFocus (circle gray2 (x,y) (0,0) (14,15))
+          let xT = x + padding + 16 
+              yT = y + (h - th) `div` 2
+              xC = x + padding + 2
+              yC = y + (th `div` 2)
+          in  withColor Black (text (xT, yT) l) 
+              // withColor DarkBeige (circleOutline (xC, yC) 5)
+              // withColor White (arc ((xC-5, yC-5), (11, 11)) 0 360)
+              // whenG down    (withColor DarkBeige (circleFilled (xC, yC) 3))
+              // whenG inFocus (withColor MediumBeige (circleOutline (xC, yC) 7))
 
 --------------
 -- List Box --
 --------------
+
+
 -- | The listbox widget creates a box with selectable entries.
--- The input stream is the list of entries as well as which entry is 
--- currently selected, and the output stream is the index of the newly 
--- selected entry.  Note that the index can be greater than the length 
--- of the list (simply indicating no choice selected).
-listbox :: (Eq a, Show a) => UISF ([a], Int) Int
-listbox = focusable $ mkWidget ([], -1) layout process draw >>> delay (-1)
+--  It takes two static values indicating the initial list 
+--  of data to display and the initial index selected (use -1 for no 
+--  selection).  It takes two event streams that can be used to 
+--  independently set the list and index.  The output stream is the 
+--  currently selected index.
+--
+--  Note that the index can be greater than the length 
+--  of the list (simply indicating no choice selected).
+listbox :: (Eq a, Show a) => [a] -> Int -> UISF (SEvent [a], SEvent Int) Int
+listbox sDB sI = proc (eDB, eI) -> do
+  rec let db' = maybe db id eDB
+      db <- delay sDB -< db'
+      i' <- delay sI  -< i
+      i  <- listbox'  -< (db', maybe i' id eI)
+  returnA -< i
+
+-- | This variant of listbox does not keep its list or index stored 
+--  internally and thus accepts a stream of those values.  As such, 
+--  it requires no static initializing parameters.  This can be useful 
+--  when the list or index are being updated frequently.
+listbox' :: (Eq a, Show a) => UISF ([a], Int) Int
+listbox' = focusable $ mkWidget ([], -1) layout process draw
   where
-    layout = makeLayout (Stretchy 80) (Stretchy 16)
+    layout = makeLayout (Stretchy 80) (Stretchy lineheight)
     -- takes the rectangle to draw in and a tuple of the list of choices and the index selected
-    lineheight = 16
+    lineheight = textHeight ""
     --draw :: Show a => Rect -> ([a], Int) -> Graphic
-    draw rect@(_,(w,_h)) _ (lst, i) = 
-        genTextGraphic rect i lst  
-        // box pushed rect 
-        // withColor White (block rect)
+    draw rect@((x,y'),(w,_h)) _ (lst, i) = 
+        genTextGraphic (y'+2) i lst --shadowbox is 2 pixels wide, so we add 2 to y
+        // shadowBox pushed rect 
+        // withColor White (rectangleFilled rect)
         where
-          n = (w - padding * 2) `div` 8
+          trimText v = fst $ textWithinPixels (w - padding * 2) (show v)
           genTextGraphic _ _ [] = nullGraphic
-          genTextGraphic ((x,y),(w,h)) i (v:vs) = (if i == 0
-                then withColor White (text (x + padding, y + padding) (take n (show v)))
-                     // withColor Blue (block ((x,y),(w,lineheight)))
-                else withColor Black (text (x + padding, y + padding) (take n (show v)))) 
-                     // genTextGraphic ((x,y+lineheight),(w,h-lineheight)) (i - 1) vs
+          genTextGraphic y i (v:vs) = (if i == 0
+                then withColor White (text (x + padding, y + padding) (trimText v))
+                     // withColor Blue (rectangleFilled ((x+2,y),(w-4,lineheight)))
+                      --shadowbox is 2 pixels wide, so we add 2 to x and subtract 4 from w
+                else withColor Black (text (x + padding, y + padding) (trimText v))) 
+                // genTextGraphic (y+lineheight) (i - 1) vs
     process :: Eq a => ([a], Int) -> ([a], Int) -> Rect -> UIEvent -> (Int, ([a], Int), Bool)
     process (lst,i) olds bbx e = (i', (lst, i'), olds /= (lst, i'))
         where
         i' = case e of
-          Button pt True True -> boundCheck $ pt2index pt
-          SKey DOWN _ True   -> min (i+1) (length lst - 1)
-          SKey UP   _ True   -> max (i-1) 0
-          SKey HOME _ True   -> 0
-          SKey END  _ True   -> length lst - 1
+          Button pt LeftButton True -> boundCheck $ pt2index pt
+          SKey KeyDown _ True   -> min (i+1) (length lst - 1)
+          SKey KeyUp   _ True   -> max (i-1) 0
+          SKey KeyHome _ True   -> 0
+          SKey KeyEnd  _ True   -> length lst - 1
           _ -> boundCheck i
         ((_,y),_) = bbx
         pt2index (_px,py) = (py-y) `div` lineheight
@@ -352,7 +444,7 @@ ----------------
 
 -- $ Sliders are input widgets that allow the user to choose a value within 
--- a given range.  They come in both continous and discrete flavors as well 
+-- a given range.  They come in both continuous and discrete flavors as well 
 -- as in both vertical and horizontal layouts.
 -- 
 -- Sliders take a boundary argument giving the minimum and maximum possible 
@@ -361,16 +453,32 @@ 
 hSlider, vSlider :: RealFrac a => (a, a) -> a -> UISF () a
 -- | Horizontal Continuous Slider
-hSlider = slider True
+hSlider a b = constA Nothing >>> hSliderS a b
 -- | Vertical Continuous Slider
-vSlider = slider False
+vSlider a b = constA Nothing >>> vSliderS a b
+
 hiSlider, viSlider :: Integral a => a -> (a, a) -> a -> UISF () a
 -- | Horizontal Discrete Slider
-hiSlider = iSlider True
+hiSlider a b c = constA Nothing >>> hiSliderS a b c
 -- | Vertical Discrete Slider
-viSlider = iSlider False
+viSlider a b c = constA Nothing >>> viSliderS a b c
 
-slider :: RealFrac a => Bool -> (a, a) -> a -> UISF () a
+
+-- $ Sliders also come in a programmatically updatable variety.
+
+hSliderS, vSliderS :: RealFrac a => (a, a) -> a -> UISF (SEvent a) a
+-- | Settable Horizontal Continuous Slider
+hSliderS = slider True
+-- | Settable Vertical Continuous Slider
+vSliderS = slider False
+
+hiSliderS, viSliderS :: Integral a => a -> (a, a) -> a -> UISF (SEvent a) a
+-- | Settable Horizontal Discrete Slider
+hiSliderS = iSlider True
+-- | Settable Vertical Discrete Slider
+viSliderS = iSlider False
+
+slider :: RealFrac a => Bool -> (a, a) -> a -> UISF (SEvent a) a
 slider hori (min, max) = mkSlider hori v2p p2v jump
   where
     v2p v w = truncate ((v - min) / (max - min) * fromIntegral w)
@@ -381,7 +489,7 @@       let v' = v + fromIntegral d * (max - min) * 16 / fromIntegral w
       in if v' < min then min else if v' > max then max else v'
 
-iSlider :: Integral a => Bool -> a -> (a, a) -> a -> UISF () a
+iSlider :: Integral a => Bool -> a -> (a, a) -> a -> UISF (SEvent a) a
 iSlider hori step (min, max) = mkSlider hori v2p p2v jump
   where
     v2p v w = w * fromIntegral (v - min) `div` fromIntegral (max - min)
@@ -408,8 +516,8 @@ -- (value,time) event pairs, but since there can be zero or more points 
 -- at once, we use [] rather than 'SEvent' for the type.
 -- The values in the (value,time) event pairs should be between -1 and 1.
-realtimeGraph :: RealFrac a => Layout -> Time -> Color -> UISF [(a,Time)] ()
-realtimeGraph layout hist color = arr ((),) >>> first getTime >>>
+realtimeGraph :: RealFrac a => Layout -> DeltaT -> Color -> UISF [(a,Time)] ()
+realtimeGraph layout hist color = arr ((),) >>> first accumTime >>>
   mkWidget ([(0,0)],0) layout process draw
   where draw _              _ ([],        _) = nullGraphic
         draw ((x,y), (w,h)) _ (lst@(_:_), t) = translateGraphic (x,y) $ 
@@ -427,22 +535,21 @@ -- Histogram --
 ---------------
 -- | The histogram widget creates a histogram of the input map.  It assumes 
--- that the elements are to be displayed linearly and evenly spaced.
+-- that the elements are to be displayed linearly and evenly spaced.  Also, 
+-- the values to be plotted must be between 0 and 1 (inclusive).
 histogram :: RealFrac a => Layout -> UISF (SEvent [a]) ()
 histogram layout = 
   mkWidget Nothing layout process draw
   where process Nothing Nothing  _ _ = ((), Nothing, False)
         process Nothing (Just a) _ _ = ((), Just a, False) --TODO check if this should be True
         process (Just a) _       _ _ = ((), Just a, True)
-        draw (xy, (w, h)) _ = translateGraphic xy . mymap (polyline . mkPts)
+        draw (xy, (w, h)) _ (Just lst@(_:_)) = translateGraphic xy $ polyline $ mkPts lst
           where mkPts l  = zip (reverse $ xs $ length l) (map adjust . normalize . reverse $ l)
                 xs n     = let k = n-1 in 0 : map (\x -> truncate $ fromIntegral (w*x) / fromIntegral k) [1..k]
                 adjust i = buffer + truncate (fromIntegral (h - 2*buffer) * (1 - i))
-                normalize lst = map (/m) lst where m = maximum lst
-                buffer = truncate $ fromIntegral h / 10
-                mymap :: ([a] -> Graphic) -> SEvent [a] -> Graphic
-                mymap f (Just lst@(_:_)) = f lst
-                mymap _ _ = nullGraphic
+                normalize lst = map (max 0 . min 1) lst
+                buffer = min 12 $ truncate $ fromIntegral h / 10
+        draw _ _ _ = nullGraphic
 
 -- | The histogramWithScale widget creates a histogram and an x coordinate scale.
 histogramWithScale :: RealFrac a => Layout -> UISF (SEvent [(a,String)]) ()
@@ -451,164 +558,72 @@   where process Nothing Nothing  _ _ = ((), Nothing, False)
         process Nothing (Just a) _ _ = ((), Just a, False) --TODO check if this should be True
         process (Just a) _       _ _ = ((), Just a, True)
-        draw (xy, (w, h)) _ = mymap (polyline . mkPts) mkScale
-          where mkPts l  = zip (reverse $ xs $ length l) (map adjust . normalize . reverse $ l)
+        draw (xy, (w, h)) _ (Just lst@(_:_)) = translateGraphic xy $ mkScale strLst // (polyline $ mkPts aLst)
+          where (aLst, strLst) = unzip lst
+                mkPts l  = zip (reverse $ xs $ length l) (map adjust . normalize . reverse $ l)
                 xs n     = let k  = n-1
-                               w' = w - sidebuffer * 2
-                           in sidebuffer : map (\x -> sidebuffer + (truncate $ fromIntegral (w'*x) / fromIntegral k)) [1..k]
-                adjust i = bottombuffer + truncate (fromIntegral (h - topbuffer - bottombuffer) * (1 - i))
-                normalize lst = map (/m) lst where m = maximum lst
-                topbuffer = truncate $ fromIntegral h / 10
-                bottombuffer = max 20 topbuffer
-                sidebuffer = 20
-                mkScale l = foldl (\pg (x,s) -> translateGraphic xy (withColor Black (text (x-((8*length s) `div` 2), h-12) s)) // pg) 
+                               w' = w - leftbuffer - rightbuffer
+                           in leftbuffer : map (\x -> leftbuffer + (truncate $ fromIntegral (w'*x) / fromIntegral k)) [1..k]
+                adjust i = topbuffer + truncate (fromIntegral (h - topbuffer - bottombuffer) * (1 - i))
+                normalize lst = map (max 0 . min 1) lst
+                topbuffer = min 12 $ truncate $ fromIntegral h / 10
+                bottombuffer = 20
+                leftbuffer = 4 + (8 * length (head strLst)) `div` 2
+                rightbuffer = 4 + (8 * length (last strLst)) `div` 2
+                mkScale l = foldl (\pg (x,s) -> withColor Black (text (x-((8*length s) `div` 2), h-16) s) // pg) 
                                   nullGraphic $ zip (xs $ length l) l
-                mymap :: ([a] -> Graphic) -> ([String] -> Graphic) -> SEvent [(a,String)] -> Graphic
-                mymap f g (Just lst@(_:_)) = let (fl,gl) = unzip lst in g gl // translateGraphic xy (f fl)
-                mymap _ _ _ = nullGraphic
+        draw _ _ _ = nullGraphic
 
 
 ------------------------------------------------------------
--- * Widget Builders
+-- *** Virtual Real Estate
 ------------------------------------------------------------
 
--- | mkWidget is a helper function to make stateful widgets easier to write.  
--- In essence, it breaks down the idea of a widget into 4 constituent 
--- components: state, layout, computation, and drawing.
--- 
--- As 'mkWidget' allows for making stateful widgets, the first parameter is 
--- simply the initial state.
--- 
--- The layout is the static layout that this widget will use.  It 
--- cannot be dependent on any streaming arguments, but a layout can have 
--- \"stretchy\" sides so that it can expand/shrink to fit an area.  Learn 
--- more about making layouts in 'UIType's UI Layout section -- specifically, 
--- check out the 'makeLayout' function and the 'LayoutType' data type.
--- 
--- The computation is where the logic of the widget is held.  This 
--- function takes as input the streaming argument a, the widget's state, 
--- a Rect of coordinates indicating the area that has been allotted for 
--- this widget, and the 'UIEvent' that is triggering this widget's activation 
--- (see the definition of 'UIEvent' in SOE).  The output consists of the 
--- streaming output, the new state, and the dirty bit, which represents 
--- whether the widget needs to be redrawn.
--- 
--- Lastly, the drawing routine takes the same Rect as the computation, a 
--- Bool that is true when this widget is in focus and false otherwise, 
--- and the current state of the widget (technically, this state is the 
--- one freshly returned from the computation).  Its output is the Graphic 
--- that this widget should display.
-
-mkWidget :: s                                 -- ^ initial state
-         -> Layout                            -- ^ layout
-         -> (a -> s -> Rect -> UIEvent ->
-             (b, s, DirtyBit))                -- ^ computation
-         -> (Rect -> Bool -> s -> Graphic)    -- ^ drawing routine
-         -> UISF a b
-mkWidget i layout comp draw = proc a -> do
-  rec s  <- delay i -< s'
-      (b, s') <- mkUISF layout aux -< (a, s)
-  returnA -< b
-    where
-      aux (ctx,f,t,e,(a,s)) = (db, f, g, nullTP, (b, s'))
-        where
-          rect = bounds ctx
-          (b, s', db) = comp a s rect e
-          g = scissorGraphic rect $ draw rect (snd f == HasFocus) s'
-
--- | Occasionally, one may want to display a non-interactive graphic in 
--- the UI.  'mkBasicWidget' facilitates this.  It takes a layout and a 
--- simple drawing routine and produces a non-interacting widget.
-mkBasicWidget :: Layout               -- ^ layout
-              -> (Rect -> Graphic)    -- ^ drawing routine
-              -> UISF a a
-mkBasicWidget layout draw = mkUISF layout $ \(ctx, f, _, _, a) ->
-  (False, f, draw $ bounds ctx, nullTP, a)
-
-
--- | The toggle is useful in the creation of both 'checkbox'es and 'radio' 
--- buttons.  It displays on/off according to its input, and when the mouse 
--- is clicked on it, it will output True (otherwise it outputs False).
--- 
--- The UISF returned from a call to toggle accepts the state stream and 
--- returns whether the toggle is being clicked.
-
-toggle :: (Eq s) => s                     -- ^ Initial state value
-       -> Layout                          -- ^ The layout for the toggle
-       -> (Rect -> Bool -> s -> Graphic)  -- ^ The drawing routine
-       -> UISF s Bool
-toggle iState layout draw = focusable $ 
-  mkWidget iState layout process draw
+-- | The scrollable function puts sub-widgets into a virtual canvas that 
+--  can be scrolled using sliders that appear when necessary.  The first 
+--  argument is the actual layout of the scrollable area, and the second 
+--  argument is the size of the virtual canvas.
+scrollable :: Layout -> Dimension -> UISF a b -> UISF a b
+scrollable layout (w,h) sf = withCTX $ proc ((CTX flow (asdf, (w',h')) _),a) -> do
+  (| bottomUp (do
+    ws <- if w > w' then hSlider (0,1) 0 -< ()
+                    else returnA -< 0
+    (| rightLeft (do
+      hs <- if h > h' then vSlider (0,1) 0 -< ()
+                      else returnA -< 0
+      transform sf -< (flow, ws, hs, a) ) |) ) |)
   where
-    process s s' _ e = (on, s, s /= s')
-      where 
-        on = case e of
-          Button _ True   True -> True
-          SKey ENTER _ True -> True
-          Key  ' '      _ True -> True
-          _ -> False 
+    transform (UISF fl f) = UISF (const layout) fun where
+      fun (CTX flow' bbx'@((x',y'), (w',h')) cj',foc,t,inp, (flow, ws, hs, a)) = do
+        (db, foc', g, cd, b, uisf) <- f (ctx', foc, t, update inp, a)
+        return (db, foc', restrict g, cd, b, transform uisf)
+          where
+            xoff = max 0 $ round $ (fromIntegral (w-w')) * ws
+            yoff = max 0 $ round $ (fromIntegral (h-h')) * hs
+            ctx' = CTX flow ((x',y'), (w,h)) cj'
+            update (MouseMove p) = MouseMove $ adjustPoint p bbx' (w,h) (xoff,yoff)
+            update (Button p@(x,y) mb isDown) = Button (adjustPoint p bbx' (w,h) (xoff,yoff)) mb isDown
+            update e = e
+            restrict g = boundGraphic bbx' $ translateGraphic (0-xoff,0-yoff) g
+            compareRange :: Ord a => a -> (a,a) -> Ordering
+            compareRange x (l,u) = case (x < l, x > u) of
+              (True, _) -> LT
+              (False, True) -> GT
+              _ -> EQ
+            adjustPoint (x,y) ((x',y'), (w',h')) (w,h) (xoff,yoff) = (xu,yu) where
+              xu = case compareRange x (x', x'+w') of
+                      LT -> x - xoff
+                      EQ -> x + xoff
+                      GT -> x + w - w'
+              yu = case compareRange y (y', y'+h') of
+                      LT -> y - yoff
+                      EQ -> y + yoff
+                      GT -> y + h - h'
 
--- | The mkSlider widget builder is useful in the creation of all sliders.
 
-mkSlider :: Eq a => Bool              -- ^ True for horizontal, False for vertical
-         -> (a -> Int -> Int)         -- ^ A function for converting a value to a position
-         -> (Int -> Int -> a)         -- ^ A function for converting a position to a value
-         -> (Int -> Int -> a -> a)    -- ^ A function for determining how much to jump when 
-                                      -- a click is on the slider but not the target
-         -> a                         -- ^ The initial value for the slider
-         -> UISF () a
-mkSlider hori val2pos pos2val jump val0 = focusable $ 
-  mkWidget (val0, Nothing) d process draw 
-  where
-    rotP p@(x,y) ((bx,by),_) = if hori then p else (bx + y - by, by + x - bx)
-    rotR r@(p,(w,h)) bbx = if hori then r else (rotP p bbx, (h,w))
-    (minw, minh) = (16 + padding * 2, 16 + padding * 2)
-    (tw, th) = (16, 8)
-    d = if hori then makeLayout (Stretchy minw) (Fixed minh)
-                else makeLayout (Fixed minh) (Stretchy minw)
-    val2pt val ((bx,by), (bw,_bh)) = 
-      let p = val2pos val (bw - padding * 2 - tw)
-      in (bx + p + padding, by + 8 - th `div` 2 + padding) 
-    bar ((x,y),(w,_h)) = ((x + padding + tw `div` 2, y + 6 + padding), 
-                         (w - tw - padding * 2, 4))
-    draw b inFocus (val, _) = 
-      let p@(mx,my) = val2pt val (rotR b b)
-      in  box popped (rotR (p, (tw, th)) b) 
-          // whenG inFocus (box marked $ rotR (p, (tw-2, th-2)) b) 
-          // withColor' bg (block $ rotR ((mx + 2, my + 2), (tw - 4, th - 4)) b) 
-          // box pushed (rotR (bar (rotR b b)) b)
-    process _ (val, s) b evt = (val', (val', s'), val /= val') 
-      where
-        (val', s') = case evt of
-          Button pt' True down -> let pt = rotP pt' bbx in
-            case (pt `inside` target, down) of
-              (True, True) -> (val, Just (ptDiff pt val))
-              (_, False)   -> (val, Nothing)
-              (False, True) | pt `inside` bar' -> clickonbar pt
-              _ -> (val, s)
-          MouseMove pt' -> let pt = rotP pt' bbx in
-            case s of
-              Just pd -> (pt2val pd pt, Just pd)
-              Nothing -> (val, s)
-          SKey LEFT  _ True -> if hori then (jump (-1) bw val, s) else (val, s)
-          SKey RIGHT _ True -> if hori then (jump 1    bw val, s) else (val, s)
-          SKey UP    _ True -> if hori then (val, s) else (jump (-1) bw val, s)
-          SKey DOWN  _ True -> if hori then (val, s) else (jump 1    bw val, s)
-          SKey HOME  _ True -> (pos2val 0  (bw - 2 * padding - tw), s)
-          SKey END   _ True -> (pos2val bw (bw - 2 * padding - tw), s)
-          _ -> (val, s)
-        bbx@((bx,_by),(bw,_bh)) = rotR b b
-        bar' = let ((x,y),(w,h)) = bar bbx in ((x,y-4),(w,h+8))
-        target = (val2pt val bbx, (tw, th)) 
-        ptDiff (x,_) val = 
-          let (x', y') = val2pt val bbx
-          in (x' + tw `div` 2 - x, y' + th `div` 2 - x)
-        pt2val (dx, _dy) (x,_y) = pos2val (x + dx - bx - tw `div` 2) (bw - 2 * padding - tw)
-        clickonbar (x',_y') = 
-          let (x,_y) = val2pt val bbx
-              val' = jump (if x' < x then -1 else 1) bw val
-          in (val', s)
-
+------------------------------------------------------------
+-- *** Custom Graphics
+------------------------------------------------------------
 
 -- | Canvas displays any graphics. The input is a signal of graphics
 -- events because we only want to redraw the screen when the input
@@ -630,129 +645,4 @@     process (Just a) _ _ _ = ((), Just a, True)
     process Nothing  a _ _ = ((), a, False)
 
-
----------------
--- Cycle Box --
----------------
--- | cyclebox is a clickable widget that cycles through a predefined set 
---   set of appearances/output values.
-cyclebox :: Layout -> [(Rect -> Bool -> Graphic, b)] -> Int -> UISF () b
-cyclebox d lst start = focusable $ 
-  mkWidget start d process draw
-  where
-    len = length lst
-    incr i = (i+1) `mod` len
-    draw b inFocus i = (fst (lst!!i)) b inFocus
-    process _ i b evt = (snd (lst!!i'), i', i /= i')
-      where 
-        i' = case evt of
-          Button _ True True -> incr i
-          SKey ENTER _ True -> incr i
-          Key ' ' _ True -> incr i
-          _ -> i
-
--- | cyclebox' is a cyclebox that additionally accepts input events that 
---   can set it to a particular appearance/output.
-cyclebox' :: Layout -> [(Rect -> Bool -> Graphic, b)] -> Int -> UISF (SEvent Int) b
-cyclebox' d lst start = focusable $ 
-  mkWidget start d process draw
-  where
-    len = length lst
-    incr i = (i+1) `mod` len
-    draw b inFocus i = (fst (lst!!i)) b inFocus
-    process ei i b evt = (snd (lst!!i'), i', i /= i')
-      where 
-        j = fromMaybe i ei
-        i' = case evt of
-          Button _ True True -> incr j
-          SKey ENTER _ True -> incr j
-          Key ' ' _ True -> incr j
-          _ -> j
-
-
-------------------------------------------------------------
--- * Focus
-------------------------------------------------------------
-
--- $ Any widget that wants to accept mouse button clicks or keystrokes 
--- must be focusable.  The focusable function below achieves this.
-
--- | Making a widget focusable makes it accessible to tabbing and allows 
--- it to see any mouse button clicks and keystrokes when it is actually 
--- in focus.
-focusable :: UISF a b -> UISF a b
-focusable (UISF layout f) = proc x -> do
-  rec hasFocus <- delay False -< hasFocus'
-      (y, hasFocus') <- UISF layout (h f) -< (x, hasFocus)
-  returnA -< y
- where
-  h fun (ctx, (myid,focus),t, inp, (a, hasFocus)) = do
-    lshift <- isKeyPressed LSHIFT
-    rshift <- isKeyPressed RSHIFT
-    let isShift = lshift || rshift
-        (f, hasFocus') = case (focus, hasFocus, inp) of
-          (HasFocus, _, _) -> (HasFocus, True)
-          (SetFocusTo n, _, _) | n == myid -> (NoFocus, True)
-          (DenyFocus, _, _) -> (DenyFocus, False)
-          (_, _,    Button pt _ True) -> (NoFocus, pt `inside` bounds ctx)
-          (_, True, SKey TAB _ True) -> if isShift then (SetFocusTo (myid-1), False) 
-                                                    else (SetFocusTo (myid+1), False)
-          (_, _, _) -> (focus, hasFocus)
-        focus' = if hasFocus' then HasFocus else DenyFocus
-        inp' = if hasFocus' then (case inp of 
-              SKey TAB _ _ -> NoUIEvent
-              _ -> inp)
-               else (case inp of 
-              Button _ _ True -> NoUIEvent
-              Key  _ _ _      -> NoUIEvent
-              SKey _ _ _      -> NoUIEvent
-              _ -> inp)
-        redraw = hasFocus /= hasFocus'
-    (db, _, g, cd, b, UISF newLayout fun') <- fun (ctx, (myid,focus'), t, inp', a)
-    return (db || redraw, (myid+1,f), g, cd, (b, hasFocus'), UISF newLayout (h fun'))
-
--- | Although mouse button clicks and keystrokes will be available once a 
--- widget marks itself as focusable, the widget may also simply want to 
--- know whether it is currently in focus to change its appearance.  This 
--- can be achieved with the following signal function.
-isInFocus :: UISF () Bool
-isInFocus = getFocusData >>> arr ((== HasFocus) . snd)
-
-
-------------------------------------------------------------
--- UI colors and drawing routine
-------------------------------------------------------------
-
-bg, gray0, gray1, gray2, gray3, blue3 :: RGB
-bg = rgb 0xec 0xe9 0xd8
-gray0 = rgb 0xff 0xff 0xff
-gray1 = rgb 0xf1 0xef 0xe2
-gray2 = rgb 0xac 0xa8 0x99
-gray3 = rgb 0x71 0x6f 0x64
-blue3 = rgb 0x31 0x3c 0x79
-
-box :: [(RGB,RGB)] -> Rect -> Graphic
-box [] _ = nullGraphic 
-box ((t, b):cs) ((x, y), (w, h)) = 
-  box cs ((x + 1, y + 1), (w - 2, h - 2)) 
-  // withColor' t (line (x, y) (x, y + h - 1) 
-                   // line (x, y) (x + w - 2, y)) 
-  // withColor' b (line (x + 1, y + h - 1) (x + w - 1, y + h - 1) 
-                   // line (x + w - 1, y) (x + w - 1, y + h - 1))
-
-circle :: RGB -> Point -> Dimension -> Dimension -> Graphic
-circle c (x, y) (w1, h1) (w2, h2) = 
-  withColor' c $ arc (x + padding + w1, y + padding + h1) 
-                     (x + padding + w2, y + padding + h2) 0 360
-
-block :: Rect -> Graphic
-block ((x,y), (w, h)) = polygon [(x, y), (x + w, y), (x + w, y + h), (x, y + h)]
-
-pushed, popped, marked :: [(RGB,RGB)]
-pushed = [(gray2, gray0),(gray3, gray1)]
-popped = [(gray1, gray3),(gray0, gray2)]
-marked = [(gray2, gray0),(gray0, gray2)]
-
-inside :: Point -> Rect -> Bool
-inside (u, v) ((x, y), (w, h)) = u >= x && v >= y && u < x + w && v < y + h
 
+ FRP/UISF/Widget/Construction.hs view
@@ -0,0 +1,302 @@+-----------------------------------------------------------------------------
+-- |
+-- Module      :  FRP.UISF.Widget.Construction
+-- Copyright   :  (c) Daniel Winograd-Cort 2015
+-- License     :  see the LICENSE file in the distribution
+--
+-- Maintainer  :  dwc@cs.yale.edu
+-- Stability   :  experimental
+--
+-- This module provides functions and utilities that help in the 
+-- construction of new widgets.  They are used by FRP.UISF.Widget, 
+-- and can be used for any custom widgets as well.
+
+{-# LANGUAGE RecursiveDo, Arrows #-}
+
+module FRP.UISF.Widget.Construction where
+
+import FRP.UISF.Graphics
+import FRP.UISF.UITypes
+import FRP.UISF.UISF
+import FRP.UISF.AuxFunctions (SEvent, delay, constA)
+
+import Control.Arrow
+import Data.Maybe (fromMaybe)
+
+
+------------------------------------------------------------
+-- Shorthand and Helper Functions
+------------------------------------------------------------
+
+-- | Default padding between border and content.
+padding :: Int
+padding = 3 
+
+-- | The default assumed background color of the GUI window.
+bg :: Color
+bg = LightBeige
+
+-- | An infix shorthand for overGraphic.
+(//) :: Graphic -> Graphic -> Graphic
+(//) = overGraphic
+
+-- | A nice way to make a graphic under only certain conditions.
+whenG :: Bool -> Graphic -> Graphic
+whenG True  g = g
+whenG False _ = nullGraphic
+
+-- | Tests whether a Point is within the bounds of a rectangle.
+inside :: Point -> Rect -> Bool
+inside (u, v) ((x, y), (w, h)) = u >= x && v >= y && u < x + w && v < y + h
+
+
+
+------------------------------------------------------------
+-- * Widget Builders
+------------------------------------------------------------
+
+-- | mkWidget is a helper function to make stateful widgets easier to write.  
+-- In essence, it breaks down the idea of a widget into 4 constituent 
+-- components: state, layout, computation, and drawing.
+-- 
+-- As 'mkWidget' allows for making stateful widgets, the first parameter is 
+-- simply the initial state.
+-- 
+-- The layout is the static layout that this widget will use.  It 
+-- cannot be dependent on any streaming arguments, but a layout can have 
+-- \"stretchy\" sides so that it can expand/shrink to fit an area.  Learn 
+-- more about making layouts in 'UIType's UI Layout section -- specifically, 
+-- check out the 'makeLayout' function and the 'LayoutType' data type.
+-- 
+-- The computation is where the logic of the widget is held.  This 
+-- function takes as input the streaming argument a, the widget's state, 
+-- a Rect of coordinates indicating the area that has been allotted for 
+-- this widget, and the 'UIEvent' that is triggering this widget's activation 
+-- (see the definition of 'UIEvent' in SOE).  The output consists of the 
+-- streaming output, the new state, and the dirty bit, which represents 
+-- whether the widget needs to be redrawn.
+-- 
+-- Lastly, the drawing routine takes the same Rect as the computation, a 
+-- Bool that is true when this widget is in focus and false otherwise, 
+-- and the current state of the widget (technically, this state is the 
+-- one freshly returned from the computation).  Its output is the Graphic 
+-- that this widget should display.
+
+mkWidget :: s                                 -- ^ initial state
+         -> Layout                            -- ^ layout
+         -> (a -> s -> Rect -> UIEvent ->
+             (b, s, DirtyBit))                -- ^ computation
+         -> (Rect -> Bool -> s -> Graphic)    -- ^ drawing routine
+         -> UISF a b
+mkWidget i layout comp draw = proc a -> do
+  rec s  <- delay i -< s'
+      (b, s') <- mkUISF layout aux -< (a, s)
+  returnA -< b
+    where
+      aux (ctx,f,t,e,(a,s)) = (db, f, g, nullTP, (b, s'))
+        where
+          rect = bounds ctx
+          (b, s', db) = comp a s rect e
+          g = {-scissorGraphic rect $ -} draw rect (snd f == HasFocus) s'
+
+-- | Occasionally, one may want to display a non-interactive graphic in 
+-- the UI.  'mkBasicWidget' facilitates this.  It takes a layout and a 
+-- simple drawing routine and produces a non-interacting widget.
+mkBasicWidget :: Layout               -- ^ layout
+              -> (Rect -> Graphic)    -- ^ drawing routine
+              -> UISF a a
+mkBasicWidget layout draw = mkUISF layout $ \(ctx, f, _, _, a) ->
+  (False, f, draw $ bounds ctx, nullTP, a)
+
+
+-- | The toggle is useful in the creation of both 'checkbox'es and 'radio' 
+-- buttons.  It displays on/off according to its input, and when the mouse 
+-- is clicked on it, it will output True (otherwise it outputs False).
+-- 
+-- The UISF returned from a call to toggle accepts the state stream and 
+-- returns whether the toggle is being clicked.
+
+toggle :: (Eq s) => s                     -- ^ Initial state value
+       -> Layout                          -- ^ The layout for the toggle
+       -> (Rect -> Bool -> s -> Graphic)  -- ^ The drawing routine
+       -> UISF s Bool
+toggle iState layout draw = focusable $ 
+  mkWidget iState layout process draw
+  where
+    process s s' _ e = (on, s, s /= s')
+      where 
+        on = case e of
+          Button _ LeftButton True -> True
+          SKey KeyEnter _     True -> True
+          Key  ' '      _     True -> True
+          _ -> False 
+
+-- | The mkSlider widget builder is useful in the creation of all sliders.
+
+mkSlider :: Eq a => Bool              -- ^ True for horizontal, False for vertical
+         -> (a -> Int -> Int)         -- ^ A function for converting a value to a position
+         -> (Int -> Int -> a)         -- ^ A function for converting a position to a value
+         -> (Int -> Int -> a -> a)    -- ^ A function for determining how much to jump when 
+                                      -- a click is on the slider but not the target
+         -> a                         -- ^ The initial value for the slider
+         -> UISF (SEvent a) a
+mkSlider hori val2pos pos2val jump val0 = focusable $ 
+  mkWidget (val0, Nothing) d process draw 
+  where
+    rotP p@(x,y) ((bx,by),_) = if hori then p else (bx + y - by, by + x - bx)
+    rotR r@(p,(w,h)) bbx = if hori then r else (rotP p bbx, (h,w))
+    (minw, minh) = (16 + padding * 2, 16 + padding * 2)
+    (tw, th) = (16, 8)
+    d = if hori then makeLayout (Stretchy minw) (Fixed minh)
+                else makeLayout (Fixed minh) (Stretchy minw)
+    val2pt val ((bx,by), (bw,_bh)) = 
+      let p = val2pos val (bw - padding * 2 - tw)
+      in (bx + p + padding, by + 8 - th `div` 2 + padding) 
+    bar ((x,y),(w,_h)) = ((x + padding + tw `div` 2, y + 6 + padding), 
+                         (w - tw - padding * 2, 4))
+    draw b inFocus (val, _) = 
+      let p@(mx,my) = val2pt val (rotR b b)
+      in  shadowBox popped (rotR (p, (tw, th)) b) 
+          // whenG inFocus (shadowBox marked $ rotR (p, (tw-2, th-2)) b) 
+          // withColor bg (rectangleFilled $ rotR ((mx + 2, my + 2), (tw - 4, th - 4)) b) 
+          // shadowBox pushed (rotR (bar (rotR b b)) b)
+    process ea (val, s) b evt = (val', (val', s'), val /= val') 
+      where
+        (val', s') = case ea of
+          Just a -> (a, s)
+          Nothing -> case evt of
+            Button pt' LeftButton down -> let pt = rotP pt' bbx in
+              case (pt `inside` target, down) of
+                (True, True) -> (val, Just (ptDiff pt val))
+                (_, False)   -> (val, Nothing)
+                (False, True) | pt `inside` bar' -> (clickonbar pt, s)
+                _ -> (val, s)
+            MouseMove pt' -> let pt = rotP pt' bbx in
+              case s of
+                Just pd -> (pt2val pd pt, Just pd)
+                Nothing -> (val, s)
+            SKey KeyLeft  _ True -> if hori then (jump (-1) bw val, s) else (val, s)
+            SKey KeyRight _ True -> if hori then (jump 1    bw val, s) else (val, s)
+            SKey KeyUp    _ True -> if hori then (val, s) else (jump (-1) bw val, s)
+            SKey KeyDown  _ True -> if hori then (val, s) else (jump 1    bw val, s)
+            SKey KeyHome  _ True -> (pos2val 0  (bw - 2 * padding - tw), s)
+            SKey KeyEnd   _ True -> (pos2val bw (bw - 2 * padding - tw), s)
+            _ -> (val, s)
+        bbx@((bx,_by),(bw,_bh)) = rotR b b
+        bar' = let ((x,y),(w,h)) = bar bbx in ((x,y-4),(w,h+8))
+        target = (val2pt val bbx, (tw, th)) 
+        ptDiff (x,_) val = 
+          let (x', y') = val2pt val bbx
+          in (x' + tw `div` 2 - x, y' + th `div` 2 - x)
+        pt2val (dx, _dy) (x,_y) = pos2val (x + dx - bx - tw `div` 2) (bw - 2 * padding - tw)
+        clickonbar (x',_y') = 
+          let (x,_y) = val2pt val bbx
+          in jump (if x' < x then -1 else 1) bw val
+
+
+---------------
+-- Cycle Box --
+---------------
+-- | cyclebox is a clickable widget that cycles through a predefined set 
+--   set of appearances/output values.
+cyclebox :: Layout -> [(Rect -> Bool -> Graphic, b)] -> Int -> UISF () b
+cyclebox d lst start = constA Nothing >>> cycleboxS d lst start
+
+-- | cycleboxS is a cyclebox that additionally accepts input events that 
+--   can set it to a particular appearance/output.
+cycleboxS :: Layout -> [(Rect -> Bool -> Graphic, b)] -> Int -> UISF (SEvent Int) b
+cycleboxS d lst start = focusable $ 
+  mkWidget start d process draw
+  where
+    len = length lst
+    incr i = (i+1) `mod` len
+    draw b inFocus i = (fst (lst!!i)) b inFocus
+    process ei i b evt = (snd (lst!!i'), i', i /= i')
+      where 
+        j = fromMaybe i ei
+        i' = case evt of
+          Button _ LeftButton True -> incr j
+          SKey KeyEnter _     True -> incr j
+          Key ' ' _           True -> incr j
+          _ -> j
+
+
+------------------------------------------------------------
+-- * Focus
+------------------------------------------------------------
+
+-- $ Any widget that wants to accept mouse button clicks or keystrokes 
+-- must be focusable.  The focusable function below achieves this.
+
+-- | Making a widget focusable makes it accessible to tabbing and allows 
+-- it to see any mouse button clicks and keystrokes when it is actually 
+-- in focus.
+focusable :: UISF a b -> UISF a b
+focusable (UISF layout f) = proc x -> do
+  rec hasFocus <- delay False -< hasFocus'
+      (y, hasFocus') <- UISF layout (h f) -< (x, hasFocus)
+  returnA -< y
+ where
+  h fun (ctx, (myid,focus),t, inp, (a, hasFocus)) = do
+    let (f, hasFocus') = case (focus, hasFocus, inp) of
+          (HasFocus, _, _) -> (HasFocus, True)
+          (SetFocusTo n, _, _) | n == myid -> (NoFocus, True)
+          (DenyFocus, _, _) -> (DenyFocus, False)
+          (_, _,    Button pt _ True) -> (NoFocus, pt `inside` bounds ctx)
+          (_, True, SKey KeyTab ms True) -> if hasShiftModifier ms 
+                                            then (SetFocusTo (myid-1), False) 
+                                            else (SetFocusTo (myid+1), False)
+          (_, _, _) -> (focus, hasFocus)
+        focus' = if hasFocus' then HasFocus else DenyFocus
+        inp' = if hasFocus' then (case inp of 
+              SKey KeyTab _ _ -> NoUIEvent
+              _ -> inp)
+               else (case inp of 
+              Button _ _ True -> NoUIEvent -- TODO: why "True" and not "_"?
+              Key  _ _ _      -> NoUIEvent
+              SKey _ _ _      -> NoUIEvent
+              _ -> inp)
+        redraw = hasFocus /= hasFocus'
+    (db, _, g, cd, b, UISF newLayout fun') <- fun (ctx, (myid,focus'), t, inp', a)
+    return (db || redraw, (myid+1,f), g, cd, (b, hasFocus'), UISF newLayout (h fun'))
+
+-- | Although mouse button clicks and keystrokes will be available once a 
+-- widget marks itself as focusable, the widget may also simply want to 
+-- know whether it is currently in focus to change its appearance.  This 
+-- can be achieved with the following signal function.
+isInFocus :: UISF () Bool
+isInFocus = getFocusData >>> arr ((== HasFocus) . snd)
+
+
+------------------------------------------------------------
+-- * Supplemental Drawing Function
+------------------------------------------------------------
+
+-- | A convenience function for making a box that appears to have a 
+--  shadow.  This is accomplished by using four colors representing:
+--
+--  (Top outside, Top inside, Bottom inside, Bottom Outside).
+--
+--  This is designed to be used with the below values 'pushed', 
+--  'popped', and 'marked'.
+shadowBox :: (Color,Color,Color,Color) -> Rect -> Graphic
+shadowBox (to,ti,bi,bo) ((x, y), (w, h)) = 
+     withColor to (line (x, y) (x, y + h - 1) 
+                // line (x, y) (x + w - 2, y)) 
+  // withColor bo (line (x + 1, y + h - 1) (x + w - 1, y + h - 1) 
+                // line (x + w - 1, y) (x + w - 1, y + h - 1))
+  // withColor ti (line (x + 1, y + 1) (x + 1, y + h - 2) 
+                // line (x + 1, y + 1) (x + w - 3, y + 1)) 
+  // withColor bi (line (x + 2, y + h - 2) (x + w - 2, y + h - 2) 
+                // line (x + w - 2, y + 1) (x + w - 2, y + h - 2))
+
+pushed, popped, marked :: (Color,Color,Color,Color)
+-- | A 'pushed' 'shadowBox' appears as if it is pushed inward.
+pushed = (MediumBeige, DarkBeige, VLightBeige, White)
+-- | A 'popped' 'shadowBox' appears as if it pops outward.
+popped = (VLightBeige, White, MediumBeige, DarkBeige)
+-- | A 'marked' 'shadowBox' appears somewhat between popped and pushed 
+--  and is designed to indicate that the box is at the ready.
+marked = (MediumBeige, White, MediumBeige, White)
+
+
ReadMe.txt view
@@ -7,17 +7,16 @@ -----------------------------
 
 The UISF package provides an arrowized FRP library for graphical user 
-interfaces.  UISF stems from work done on Euterpea 
-(http://haskell.cs.yale.edu/).
+interfaces.  UISF stems from work done on Euterpea (http://haskell.cs.yale.edu/).
 
-See Liense for licensing information.
+See License for licensing information.
 
 
 ============================
 ==== Getting the Source ====
 ============================
 
-Currently (1/14/2015), the most up-to-date version of UISF is 
+Currently (7/20/2015), the most up-to-date version of UISF is 
 available through GitHub at:
 
     https://github.com/dwincort/UISF
UISF.cabal view
@@ -1,5 +1,5 @@ name:           UISF
-version:        0.3.0.2
+version:        0.4.0.0
 Cabal-Version:  >= 1.8
 license:        BSD3
 license-file:   License
@@ -17,7 +17,6 @@ extra-source-files:
         ReadMe.txt,
         changelog.txt,
-        FRP/UISF/Examples/EnableGUI.hs
         FRP/UISF/Examples/SevenGuis.lhs
         FRP/UISF/Examples/Pinochle.hs
         FRP/UISF/Examples/fft.hs
@@ -32,13 +31,20 @@         FRP.UISF.Examples.Crud,
         FRP.UISF.Examples.Examples,
         FRP.UISF.AuxFunctions,
-        FRP.UISF.SOE,
-        FRP.UISF.UITypes,
+        FRP.UISF.Asynchrony,
+        FRP.UISF.Graphics,
+        FRP.UISF.Graphics.Color,
+        FRP.UISF.Graphics.Graphic,
+        FRP.UISF.Graphics.Text,
+        FRP.UISF.Graphics.Types,
+        FRP.UISF.Render.GLUT,
         FRP.UISF.UISF,
+        FRP.UISF.UITypes,
         FRP.UISF.Widget,
+        FRP.UISF.Widget.Construction,
         FRP.UISF
   other-modules:
   build-depends:
         base >= 4 && < 5, containers, transformers, 
-        arrows >= 0.4, GLFW >= 0.5, OpenGL >= 2.8, 
-        deepseq >= 1.3, stm >= 2.4
+        arrows >= 0.4, OpenGL >= 2.8, GLUT >= 2.5,
+        deepseq >= 1.3, stm >= 2.4, array
changelog.txt view
@@ -1,5 +1,38 @@ changelog
 
+0.4.0.0
+In big news, the whole GLFW back end was removed and replaced with GLUT.  
+This should hopefully improve stability and make future updates easier.  
+The code is also somewhat parameterized on the back end, meaning that it 
+can accept alternate rendering options fairly easily.  I encourage users 
+to write rendering functions for the new Graphic type and submit pull requests.
+
+NEW FEATURES
+* Added scrollable widget
+* Added displayField widget
+* Added new settable widgets (named ***S) that allow one to programmatically 
+    set data within widgets.
+* Extended the Color data type to include the common UI grays.
+* ArrowIO updated to include terminalIO function
+
+BUG FIXES/ADJUSTMENTS
+* removed implicit delay from accum (this also affects hold)
+* removed implicit delay from listbox
+* Restructured Layout data type (layout smart constructors are unaffected)
+* histogram widgets no longer normalize their input data by default (also 
+    fixed some graphical issues)
+
+RENAMED/MOVED
+* ArrowTime has been replaced with an ArrowReader DeltaT instance
+* Old asynchronous functions have been replaced with new ones
+* Widget constructors moved to their own module
+
+REMOVED/DEPRECATED:
+* textboxE has been deprecated and replaced with textbox
+* mergeE and (~++) have been deprecated
+* UIParams no longer export a constructor (use defaultUIParams with record syntax updates)
+
+
 0.3.0.2
 * Added changelog
 * Fixed a bug with nested focusable widgets incorrectly focusing