-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Library for Arrowized Graphical User Interfaces. -- @package UISF @version 0.3.0.2 -- | Auxiliary functions for use with UISF or other arrows. module FRP.UISF.AuxFunctions -- | SEvent is short for "Stream Event" and is a type synonym for Maybe. type SEvent = Maybe -- | Time is simply represented as a Double. type Time = Double -- | 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 time :: ArrowTime a => a () Time -- | Instances of the ArrowIO class have an arrowized ability to perform IO -- actions. class Arrow a => ArrowIO a liftAIO :: ArrowIO a => (b -> IO c) -> a b c initialAIO :: ArrowIO a => IO d -> (d -> a b c) -> a b c -- | constA is an arrowized version of const. constA :: Arrow a => c -> a b c -- | constSF is a convenience composing constA with the given SF. constSF :: Arrow a => b -> a b d -> a c d -- | edge generates an event whenever the Boolean input signal changes from -- False to True -- in signal processing this is called an ``edge -- detector,'' and thus the name chosen here. edge :: ArrowCircuit a => a Bool (SEvent ()) -- | The signal function (accum v) starts with the value v, but then -- applies the function attached to the first event to that value to get -- the next value, and so on. accum :: ArrowCircuit a => b -> a (SEvent (b -> b)) b -- | The signal function unique will produce an event each time its input -- signal changes. unique :: Eq e => ArrowCircuit a => a e (SEvent e) -- | hold is a signal function whose output starts as the value of the -- static argument. This value is held until the first input event -- 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 -- | 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 ()) -- | mergeE merges two events with the given resolution function. mergeE :: (a -> a -> a) -> SEvent a -> SEvent a -> SEvent a -- | This is an infix specialization of mergeE to lists. (~++) :: SEvent [a] -> SEvent [a] -> SEvent [a] -- | Combines the input list of arrows into one arrow that takes a list of -- inputs and returns a list of outputs. concatA :: Arrow a => [a b c] -> a [b] [c] -- | This is a special case of foldA for lists. runDynamic :: ArrowChoice a => a b c -> a [b] [c] -- | This essentially allows an arrow that processes b to c to take [b] and -- recursively generate cs, combining them all into a final output d. foldA :: ArrowChoice a => (c -> d -> d) -> d -> a b c -> a [b] d -- | For folding results of a list of signal functions. foldSF :: Arrow a => (b -> c -> c) -> c -> [a () b] -> a () c -- | This behaves much like the maybe function except lifted to the -- ArrowChoice level. The arrow behaves like its first argument when the -- input stream is Nothing and like its second when it is a Just value. maybeA :: ArrowChoice a => a () c -> a b c -> a (Maybe b) c -- | This lifts the arrow to an event-based arrow that behaves as a -- constant stream of Nothing when there is no event. evMap :: ArrowChoice a => a b c -> a (SEvent b) (SEvent c) -- | A delay component. delay :: ArrowCircuit a => forall b. b -> a b b -- | vdelay is a delay function that delays for a variable amount of time. -- It takes the current time, an amount of time to delay, and an event -- stream and delays the event stream by the delay amount. vdelay, like -- fdelay, guarantees that the order of events in is the 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) -- | fdelay is a delay function that delays for a fixed amount of time, -- given as the static argument. It returns a signal function that takes -- the current time and an event stream and delays the event stream by -- the delay amount. fdelay guarantees that the order of events in is the -- same as the order of events out and that no event will be skipped. -- However, 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) -- | vcdelay is a continuous version of vdelay. It will always emit the -- value that was produced dt seconds earlier (erring on the side of an -- older value if necessary). Be warned that this version of delay can -- both omit some data entirely and emit the same data multiple times. As -- such, it is usually inappropriate for events (use vdelay). vcdelay -- takes a maxDT argument that stands for the maximum delay time -- that it can handle. This is to prevent a space leak. -- -- Implementation note: Rather than keep a single buffer, we keep two -- sequences that act to produce a sort of lens for a buffer. qlow has -- all the values that are older than what we currently need, and qhigh -- has all of the newer ones. Obviously, as time moves forward and the -- 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 -- | fcdelay is a continuous version of fdelay. It takes an initial value -- to emit for the first dt seconds. After that, the delay will always 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 -- | timer is a variable duration timer. This timer takes the current time -- as well as the (variable) time between events and returns an SEvent -- steam. When the second argument is non-positive, the output will be a -- steady stream of events. As long as the clock speed is 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 ()) -- | 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) -- | Tempo is just a Double. type Tempo = Double -- | The BufferOperation data type wraps up the data and operational -- commands to control an eventbuffer. data BufferOperation b -- | No Buffer Operation NoBOp :: BufferOperation b -- | Erase the buffer ClearBuffer :: BufferOperation b -- | Skip ahead a certain amount of time in the buffer SkipAheadInBuffer :: DeltaT -> BufferOperation b -- | Merge data into the buffer MergeInBuffer :: [(DeltaT, b)] -> BufferOperation b -- | Append data to the end of the buffer AppendToBuffer :: [(DeltaT, b)] -> BufferOperation b -- | Set a new play status (True = Playing, False = Paused) SetBufferPlayStatus :: Bool -> (BufferOperation b) -> BufferOperation b -- | Set the buffer's tempo SetBufferTempo :: Tempo -> (BufferOperation b) -> BufferOperation b -- | eventBuffer allows for a timed series of events to be prepared and -- emitted. The streaming input is a BufferOperation, described above. -- Note that the default play status is playing and the default tempo is -- 1. Just as MIDI files have events timed based on ticks since the last -- event, the events here are timed based on seconds since the last -- event. If an event is to occur 0.0 seconds after the last event, then -- it is assumed to be played at the same time as the last event, and all -- simultaneous events are emitted 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' is a version that takes Time explicitly rather than with -- ArrowTime. eventBuffer' :: ArrowCircuit a => a (BufferOperation b, Time) (SEvent [b], Bool) -- | An arrow type comprising Mealy-style automata, each step of which is -- is a computation in the original arrow type. newtype Automaton (a :: * -> * -> *) b c :: (* -> * -> *) -> * -> * -> * Automaton :: a b (c, Automaton a b c) -> Automaton b c -- | 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 -> DeltaT -> (ThreadId -> a () ()) -> (Automaton (->) b c) -> a (b, Time) [(c, Time)] -- | 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 () ()) -> (Automaton (->) b c) -> a (SEvent b) (SEvent c) -- | 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 () ()) -> (Automaton (->) b c) -> a b [c] -- | 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 () ()) -> (b -> IO d, e -> IO ()) -> (Automaton (->) (b, d) (c, e)) -> a b [c] module FRP.UISF.SOE runGraphics :: IO () -> IO () type Title = String type Size = (Int, Int) data Window openWindow :: Title -> Size -> IO Window getMainWindowSize :: IO Size clearWindow :: Window -> IO () drawInWindow :: Window -> Graphic -> IO () drawInWindowNow :: Window -> Graphic -> IO () -- | setGraphic set the given Graphic over empty (black) background for -- display in current Window. setGraphic :: Window -> Graphic -> IO () setGraphic' :: Window -> Graphic -> IO () setDirty :: Window -> IO () closeWindow :: Window -> IO () openWindowEx :: Title -> Maybe Point -> Maybe Size -> RedrawMode -> IO Window data RedrawMode drawGraphic :: RedrawMode drawBufferedGraphic :: RedrawMode -- | Graphic is just a wrapper for OpenGL IO data Graphic nullGraphic :: Graphic emptyGraphic :: Graphic overGraphic :: Graphic -> Graphic -> Graphic overGraphics :: [Graphic] -> Graphic translateGraphic :: (Int, Int) -> Graphic -> Graphic data Color Black :: Color Blue :: Color Green :: Color Cyan :: Color Red :: Color Magenta :: Color Yellow :: Color White :: Color type RGB = Color3 GLfloat type RGBA = Color4 GLfloat rgb :: (Integral r, Integral g, Integral b) => r -> g -> b -> RGB rgba :: (Integral r, Integral g, Integral b, Integral a) => r -> g -> b -> a -> RGBA withColor :: Color -> Graphic -> Graphic withColor' :: Color a => a -> Graphic -> Graphic text :: Point -> String -> Graphic type Point = (Int, Int) ellipse :: Point -> Point -> Graphic shearEllipse :: Point -> Point -> Point -> Graphic line :: Point -> Point -> Graphic polygon :: [Point] -> Graphic polyline :: [Point] -> Graphic polyBezier :: [Point] -> Graphic type Angle = GLfloat arc :: Point -> Point -> Angle -> Angle -> Graphic scissorGraphic :: (Point, Size) -> Graphic -> Graphic -- | Key is represented by either a character key or a special key. data Key :: * CharKey :: Char -> Key SpecialKey :: SpecialKey -> Key -- | Special key is a key not represented in the 32 - 127 printable ASCII -- range. data SpecialKey :: * UNKNOWN :: SpecialKey ESC :: SpecialKey F1 :: SpecialKey F2 :: SpecialKey F3 :: SpecialKey F4 :: SpecialKey F5 :: SpecialKey F6 :: SpecialKey F7 :: SpecialKey F8 :: SpecialKey F9 :: SpecialKey F10 :: SpecialKey F11 :: SpecialKey F12 :: SpecialKey F13 :: SpecialKey F14 :: SpecialKey F15 :: SpecialKey F16 :: SpecialKey F17 :: SpecialKey F18 :: SpecialKey F19 :: SpecialKey F20 :: SpecialKey F21 :: SpecialKey F22 :: SpecialKey F23 :: SpecialKey F24 :: SpecialKey F25 :: SpecialKey UP :: SpecialKey DOWN :: SpecialKey LEFT :: SpecialKey RIGHT :: SpecialKey LSHIFT :: SpecialKey RSHIFT :: SpecialKey LCTRL :: SpecialKey RCTRL :: SpecialKey LALT :: SpecialKey RALT :: SpecialKey TAB :: SpecialKey ENTER :: SpecialKey BACKSPACE :: SpecialKey INSERT :: SpecialKey DEL :: SpecialKey PAGEUP :: SpecialKey PAGEDOWN :: SpecialKey HOME :: SpecialKey END :: SpecialKey KP_0 :: SpecialKey KP_1 :: SpecialKey KP_2 :: SpecialKey KP_3 :: SpecialKey KP_4 :: SpecialKey KP_5 :: SpecialKey KP_6 :: SpecialKey KP_7 :: SpecialKey KP_8 :: SpecialKey KP_9 :: SpecialKey KP_DIVIDE :: SpecialKey KP_MULTIPLY :: SpecialKey KP_SUBTRACT :: SpecialKey KP_ADD :: SpecialKey KP_DECIMAL :: SpecialKey KP_EQUAL :: SpecialKey KP_ENTER :: SpecialKey data UIEvent Key :: Char -> ([Char], [SpecialKey]) -> Bool -> UIEvent char :: UIEvent -> Char modifiers :: UIEvent -> ([Char], [SpecialKey]) isDown :: UIEvent -> Bool SKey :: SpecialKey -> ([Char], [SpecialKey]) -> Bool -> UIEvent skey :: UIEvent -> SpecialKey modifiers :: UIEvent -> ([Char], [SpecialKey]) isDown :: UIEvent -> Bool Button :: Point -> Bool -> Bool -> UIEvent pt :: UIEvent -> Point isLeft :: UIEvent -> Bool isDown :: UIEvent -> Bool MouseMove :: Point -> UIEvent pt :: UIEvent -> Point Resize :: Size -> UIEvent Refresh :: UIEvent Closed :: UIEvent NoUIEvent :: UIEvent hasShiftModifier :: ([Char], [SpecialKey]) -> Bool hasCtrlModifier :: ([Char], [SpecialKey]) -> Bool hasAltModifier :: ([Char], [SpecialKey]) -> Bool maybeGetWindowEvent :: Double -> Window -> IO (Maybe UIEvent) -- | 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 -- | 32-bit unsigned integer type data Word32 :: * -- | use GLFW's high resolution timer timeGetTime :: IO Double word32ToInt :: Word32 -> Int -- | Designed to be used with Key, CharKey, or SpecialKey isKeyPressed :: Enum a => a -> IO Bool instance Eq Color instance Ord Color instance Bounded Color instance Enum Color instance Ix Color instance Show Color instance Read Color instance Show UIEvent module FRP.UISF.UITypes -- | The termination procedure is simply a potential IO action. type TerminationProc = Maybe (IO ()) -- | The null termination procedure is no action. nullTP :: TerminationProc -- | A method for merging two termination procedures. mergeTP :: TerminationProc -> TerminationProc -> TerminationProc -- | A rendering context specifies the following: data CTX CTX :: Flow -> Rect -> Bool -> CTX -- | A layout direction to flow widgets. flow :: CTX -> Flow -- | A rectangle bound of current drawing area to render a UI component. It -- specifies the max size of a widget, not the actual size. It's up to -- each individual widget to decide where in this bound to put itself. bounds :: CTX -> Rect -- | A flag to tell whether we are in a conjoined state or not. A conjoined -- context will duplicate itself for subcomponents rather than splitting. -- This can be useful for making compound widgets when one widget takes -- up space and the other performs some side effect having to do with -- that space. isConjoined :: CTX -> Bool -- | Flow determines widget ordering. data Flow TopDown :: Flow BottomUp :: Flow LeftRight :: Flow RightLeft :: Flow -- | A dimension specifies size. type Dimension = (Int, Int) -- | A rectangle has a corner point and a dimension. type Rect = (Point, Dimension) -- | Layouts for individual widgets typically come in a few standard -- flavors, so we have this convenience function for their creation. This -- function takes layout information for first the horizontal dimension -- and then the vertical. makeLayout :: LayoutType -> LayoutType -> Layout -- | A dimension can either be: data LayoutType -- | Stretchy with a minimum size in pixels Stretchy :: Int -> LayoutType minSize :: LayoutType -> Int -- | Fixed with a size measured in pixels Fixed :: Int -> LayoutType fixedSize :: LayoutType -> Int -- | The null layout is useful for "widgets" that do not appear or take up -- space on the screen. nullLayout :: Layout -- | 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.
  2. --
  3. hFixed and vFixed specify how much non-stretching space (in -- pixels) of width and height should be allocated for this widget.
  4. --
  5. minW and minH specify minimum values (in pixels) of width and -- height for the widget's stretchy dimensions.
  6. --
data Layout Layout :: Int -> Int -> Int -> Int -> Int -> Int -> Layout hFill :: Layout -> Int vFill :: Layout -> Int hFixed :: Layout -> Int vFixed :: Layout -> Int minW :: Layout -> Int minH :: Layout -> Int -- | Divides the CTX among the two given layouts. divideCTX :: CTX -> Layout -> Layout -> (CTX, CTX) -- | Merge two layouts into one. mergeLayout :: Flow -> Layout -> Layout -> Layout -- | Merging two graphics can be achieved with overGraphic, but the -- mergeGraphic function additionally constrains the graphics based on -- their layouts and the context. TODO: Make sure this works as well as -- it should mergeGraphics :: CTX -> (Graphic, Layout) -> (Graphic, Layout) -> Graphic -- | The dirty bit is a bit to indicate if the widget needs to be redrawn. type DirtyBit = Bool -- | The Focus type helps focusable widgets communicate with each other -- about which widget is in focus. It consists of a WidgetID and a -- FocusInfo. type Focus = (WidgetID, FocusInfo) -- | The WidgetID for any given widget is dynamic based on how many -- focusable widgets are active at the moment. It is designed basically -- as a counter that focusable widgets will automatically (via the -- focusable function) increment. type WidgetID = Int -- | The FocusInfo means one of the following: data FocusInfo -- | Indicates that this widget is a subwidget of a widget that is in -- focus. Thus, this widget too is in focus, and this widget should pass -- HasFocus forward. HasFocus :: FocusInfo -- | Indicates that there is no focus information to communicate between -- widgets. NoFocus :: FocusInfo -- | Indicates that the widget whose id is given should take focus. That -- widget should then pass NoFocus onward. SetFocusTo :: WidgetID -> FocusInfo -- | Any widget that sees this value should recognize that they are no -- longer in focus. This is useful for nested focus. DenyFocus :: FocusInfo instance Eq Flow instance Show Flow instance Show CTX instance Eq Layout instance Show Layout instance Show FocusInfo instance Eq FocusInfo -- | A simple Graphical User Interface with concepts borrowed from Phooey -- by Conal Elliot. module FRP.UISF.UISF data UISF b c UISF :: (Flow -> Layout) -> ((CTX, Focus, Time, UIEvent, b) -> IO (DirtyBit, Focus, Graphic, TerminationProc, c, UISF b c)) -> UISF b c uisfLayout :: UISF b c -> Flow -> Layout uisfFun :: UISF b c -> (CTX, Focus, Time, UIEvent, b) -> IO (DirtyBit, Focus, Graphic, TerminationProc, c, UISF b c) -- | Lift an IO source to UISF. uisfSource :: IO b -> UISF () b -- | Lift an IO sink to UISF. uisfSink :: (a -> IO ()) -> UISF a () -- | Lift an IO pipe to UISF. uisfPipe :: (a -> IO b) -> UISF a b -- | Lift an IO source to an event-based UISF. uisfSourceE :: IO b -> UISF (SEvent ()) (SEvent b) -- | Lift an IO sink to an event-based UISF. uisfSinkE :: (a -> IO ()) -> UISF (SEvent a) (SEvent ()) -- | Lift an IO pipe to an event-based UISF. uisfPipeE :: (a -> IO b) -> UISF (SEvent a) (SEvent b) -- | Get the time signal from a UISF. getTime :: UISF () Time -- | Get the context signal from a UISF. getCTX :: UISF () CTX -- | Get the UIEvent signal from a UISF. getEvents :: UISF () UIEvent -- | Get the focus data from a UISF. getFocusData :: UISF () Focus -- | Add a termination procedure to a UISF. addTerminationProc :: IO () -> UISF a a -- | Get the mouse position from a UISF. getMousePosition :: UISF () Point -- | This function creates a UISF with the given parameters. mkUISF :: Layout -> ((CTX, Focus, Time, UIEvent, a) -> (DirtyBit, Focus, Graphic, TerminationProc, b)) -> UISF a b -- | We can also lift a signal function to a UISF asynchronously. asyncUISFE :: NFData b => Automaton (->) a b -> UISF (SEvent a) (SEvent b) -- | 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)] leftRight :: UISF a b -> UISF a b rightLeft :: UISF a b -> UISF a b topDown :: UISF a b -> UISF a b bottomUp :: UISF a b -> UISF a b conjoin :: UISF a b -> UISF a b unconjoin :: UISF a b -> UISF a b -- | Set a new layout for this widget. setLayout :: Layout -> UISF a b -> UISF a b -- | A convenience function for setLayout, setSize sets the layout to a -- fixed size (in pixels). setSize :: Dimension -> UISF a b -> UISF a b -- | Add space padding around a widget. pad :: (Int, Int, Int, Int) -> UISF a b -> UISF a b -- | The UIParams data type provides an interface for modifying some of the -- settings for runUI without forcing runUI to take a zillion arguments. -- Typical usage will be to modify the below defaultUIParams using record -- syntax. data UIParams UIParams :: IO () -> IO () -> String -> Dimension -> Flow -> Double -> UIParams -- | An initialization action. uiInitialize :: UIParams -> IO () -- | A termination action. uiClose :: UIParams -> IO () -- | The UI window's title. uiTitle :: UIParams -> String -- | The size of the UI window. uiSize :: UIParams -> Dimension -- | The initial Flow setting. uiInitFlow :: UIParams -> Flow -- | 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) uiTickDelay :: UIParams -> Double -- | This is the default UIParams value and what is used in runUI'. defaultUIParams :: UIParams -- | Run the UISF with the given parameters. runUI :: UIParams -> UISF () () -> IO () -- | Run the UISF with the default settings. runUI' :: UISF () () -> IO () instance [overlap ok] ArrowTime UISF instance [overlap ok] ArrowIO UISF instance [overlap ok] ArrowCircuit UISF instance [overlap ok] ArrowChoice UISF instance [overlap ok] ArrowLoop UISF instance [overlap ok] Arrow UISF instance [overlap ok] Category UISF -- | 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. module FRP.UISF.Widget -- | Labels are always left aligned and vertically centered. label :: String -> UISF a a -- | DisplayStr is an output widget showing the instantaneous value of a -- signal of strings. displayStr :: UISF String () -- | display is a widget that takes any show-able value and displays it. display :: Show a => UISF a () -- | withDisplay is a widget modifier that modifies the given widget so -- that it also displays its output value. withDisplay :: Show b => UISF a b -> UISF a b -- | 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 -- | 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 -- | Title frames a UI by borders, and displays a static title text. title :: String -> UISF a b -> UISF a b -- | 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. -- -- 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 -- | 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 -- | Checkbox allows selection or deselection of an item. It has a static -- label as well as an initial state. checkbox :: String -> Bool -> UISF () Bool -- | The checkGroup widget creates a group of checkboxes that all -- send their outputs to the same output stream. It takes a static list -- of labels for the check boxes and assumes they all start unchecked. -- -- 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] -- | Radio button presents a list of choices and only one of them can be -- selected at a time. It takes a static list of choices (as Strings) 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 -- | 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 -- | Horizontal Continuous Slider hSlider :: RealFrac a => (a, a) -> a -> UISF () a -- | Vertical Continuous Slider vSlider :: RealFrac a => (a, a) -> a -> UISF () a -- | Horizontal Discrete Slider hiSlider :: Integral a => a -> (a, a) -> a -> UISF () a -- | Vertical Discrete Slider viSlider :: Integral a => a -> (a, a) -> a -> UISF () a -- | The realtimeGraph widget creates a graph of the data with trailing -- values. It takes a dimension parameter, the length of the history of -- the graph measured in time, and a color for the graphed line. The -- signal function then takes an input stream of (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)] () -- | The histogram widget creates a histogram of the input map. It assumes -- that the elements are to be displayed linearly and evenly spaced. histogram :: RealFrac a => Layout -> UISF (SEvent [a]) () -- | The histogramWithScale widget creates a histogram and an x coordinate -- scale. histogramWithScale :: RealFrac a => Layout -> UISF (SEvent [(a, String)]) () -- | 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 UITypes 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 -> Layout -> (a -> s -> Rect -> UIEvent -> (b, s, DirtyBit)) -> (Rect -> Bool -> s -> Graphic) -> UISF a b -- | 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 -> (Rect -> Graphic) -> UISF a a -- | The toggle is useful in the creation of both checkboxes 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 -> Layout -> (Rect -> Bool -> s -> Graphic) -> UISF s Bool -- | The mkSlider widget builder is useful in the creation of all sliders. mkSlider :: Eq a => Bool -> (a -> Int -> Int) -> (Int -> Int -> a) -> (Int -> Int -> a -> a) -> a -> UISF () a -- | Canvas displays any graphics. The input is a signal of graphics events -- because we only want to redraw the screen when the input is there. canvas :: Dimension -> UISF (SEvent Graphic) () -- | canvas' uses a layout and a graphic generator. This allows it to -- behave similarly to canvas, but it can adjust in cases with -- stretchy layouts. canvas' :: Layout -> (a -> Dimension -> Graphic) -> UISF (SEvent a) () -- | 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' 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 -- | 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 -- | 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 module FRP.UISF data UISF b c -- | Run the UISF with the default settings. runUI' :: UISF () () -> IO () -- | Run the UISF with the given parameters. runUI :: UIParams -> UISF () () -> IO () -- | The UIParams data type provides an interface for modifying some of the -- settings for runUI without forcing runUI to take a zillion arguments. -- Typical usage will be to modify the below defaultUIParams using record -- syntax. data UIParams UIParams :: IO () -> IO () -> String -> Dimension -> Flow -> Double -> UIParams -- | An initialization action. uiInitialize :: UIParams -> IO () -- | A termination action. uiClose :: UIParams -> IO () -- | The UI window's title. uiTitle :: UIParams -> String -- | The size of the UI window. uiSize :: UIParams -> Dimension -- | The initial Flow setting. uiInitFlow :: UIParams -> Flow -- | 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) uiTickDelay :: UIParams -> Double -- | This is the default UIParams value and what is used in runUI'. defaultUIParams :: UIParams -- | A dimension specifies size. type Dimension = (Int, Int) -- | Labels are always left aligned and vertically centered. label :: String -> UISF a a -- | DisplayStr is an output widget showing the instantaneous value of a -- signal of strings. displayStr :: UISF String () -- | display is a widget that takes any show-able value and displays it. display :: Show a => UISF a () -- | withDisplay is a widget modifier that modifies the given widget so -- that it also displays its output value. withDisplay :: Show b => UISF a b -> UISF a b -- | 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 -- | 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 -- | Title frames a UI by borders, and displays a static title text. title :: String -> UISF a b -> UISF a b -- | 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. -- -- 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 -- | 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 -- | Checkbox allows selection or deselection of an item. It has a static -- label as well as an initial state. checkbox :: String -> Bool -> UISF () Bool -- | The checkGroup widget creates a group of checkboxes that all -- send their outputs to the same output stream. It takes a static list -- of labels for the check boxes and assumes they all start unchecked. -- -- 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] -- | Radio button presents a list of choices and only one of them can be -- selected at a time. It takes a static list of choices (as Strings) 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 -- | Horizontal Continuous Slider hSlider :: RealFrac a => (a, a) -> a -> UISF () a -- | Vertical Continuous Slider vSlider :: RealFrac a => (a, a) -> a -> UISF () a -- | Horizontal Discrete Slider hiSlider :: Integral a => a -> (a, a) -> a -> UISF () a -- | Vertical Discrete Slider viSlider :: Integral a => a -> (a, a) -> a -> UISF () a -- | The realtimeGraph widget creates a graph of the data with trailing -- values. It takes a dimension parameter, the length of the history of -- the graph measured in time, and a color for the graphed line. The -- signal function then takes an input stream of (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)] () data Color Black :: Color Blue :: Color Green :: Color Cyan :: Color Red :: Color Magenta :: Color Yellow :: Color White :: Color -- | The histogram widget creates a histogram of the input map. It assumes -- that the elements are to be displayed linearly and evenly spaced. histogram :: RealFrac a => Layout -> UISF (SEvent [a]) () -- | The histogramWithScale widget creates a histogram and an x coordinate -- scale. histogramWithScale :: RealFrac a => Layout -> UISF (SEvent [(a, String)]) () -- | 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 -- | Canvas displays any graphics. The input is a signal of graphics events -- because we only want to redraw the screen when the input is there. canvas :: Dimension -> UISF (SEvent Graphic) () -- | canvas' uses a layout and a graphic generator. This allows it to -- behave similarly to canvas, but it can adjust in cases with -- stretchy layouts. canvas' :: Layout -> (a -> Dimension -> Graphic) -> UISF (SEvent a) () topDown :: UISF a b -> UISF a b bottomUp :: UISF a b -> UISF a b leftRight :: UISF a b -> UISF a b rightLeft :: UISF a b -> UISF a b -- | Add space padding around a widget. pad :: (Int, Int, Int, Int) -> UISF a b -> UISF a b -- | A convenience function for setLayout, setSize sets the layout to a -- fixed size (in pixels). setSize :: Dimension -> UISF a b -> UISF a b -- | Set a new layout for this widget. setLayout :: Layout -> UISF a b -> UISF a b -- | Layouts for individual widgets typically come in a few standard -- flavors, so we have this convenience function for their creation. This -- function takes layout information for first the horizontal dimension -- and then the vertical. makeLayout :: LayoutType -> LayoutType -> Layout -- | A dimension can either be: data LayoutType -- | Stretchy with a minimum size in pixels Stretchy :: Int -> LayoutType minSize :: LayoutType -> Int -- | Fixed with a size measured in pixels Fixed :: Int -> LayoutType fixedSize :: LayoutType -> Int -- | Get the time signal from a UISF. getTime :: UISF () Time -- | 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)] -- | We can also lift a signal function to a UISF asynchronously. asyncUISFE :: NFData b => Automaton (->) a b -> UISF (SEvent a) (SEvent b) module FRP.UISF.Examples.Examples -- | This example displays the time from the start of the GUI application. timeEx :: UISF () () -- | This example shows off buttons and state by presenting a plus -- and minus button with a counter that is adjusted by them. buttonEx :: UISF () () -- | This example shows off the checkbox widgets. checkboxEx :: UISF () () -- | This example shows off the radio button widget. radioButtonEx :: UISF () () -- | This example shows off integral sliders (horizontal hiSliders -- in this case). shoppinglist :: UISF () () -- | 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 () () -- | This example shows off the textboxE widget. Text can be typed -- in, and that text is transferred to the display widget below -- when the button is pressed. textboxdemo :: UISF () () -- | 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 () module FRP.UISF.Examples.Crud type Database a = [a] data NameEntry NameEntry :: String -> String -> NameEntry firstName :: NameEntry -> String lastName :: NameEntry -> String defaultnames :: Database NameEntry -- | This function will run the crud GUI with the default names. crud :: IO () -- | main = crud main :: IO () -- | This is the main function that creates the crud GUI. It takes an -- initial database of names as an argument. See notes below on the use -- of banana brackets and nested do blocks. crudUISF :: Database NameEntry -> UISF () () instance Eq NameEntry instance Show NameEntry