reactivity-0.2.3.0: src/FRP/Reactivity/Hook.hs
{-# LANGUAGE Unsafe, GeneralizedNewtypeDeriving, DeriveFunctor, FlexibleInstances, Rank2Types, FunctionalDependencies, OverlappingInstances, UndecidableInstances #-}
module FRP.Reactivity.Hook (Resource(..), Hook,
-- | * Versions of UI functions that can be modified by hooks.
hCreate, hCreate',
-- | * Hook readers
getFilter, window, getResource, tellReturn, getReturns, getReturns1,
-- | * Running
ty, runHook) where
import Foreign.Ptr
import Control.Applicative
import Control.Monad.Reader
import Control.Monad.Writer
import Control.Monad.Trans
import Control.Monad
import Data.Monoid
import FRP.Reactivity.Combinators
import FRP.Reactivity.UI
import FRP.Reactivity.Draw
import Codec.BMP
import Graphics.Win32
newtype Hook r x m t = Hook { unHook :: WriterT (Event Window) (ReaderT (Window, r, x -> x) m) t } deriving (Monad, MonadPlus, MonadFix, Functor, Applicative, Alternative, MonadIO)
instance MonadTrans (Hook r x) where
lift = Hook . lift . lift
instance (MonadPlus m) => Monoid (Hook r x m t) where
mempty = mzero
mappend = mplus
-- | Versions of UI functions that can be modified by hooks.
{-# INLINE hCreate #-}
hCreate :: (Monoid r, Resource r) => WndClass -> r -> Hook r (Event Message) Act t -> Hook r (Event Message) Act t
hCreate wndclass r hook = do
parent <- window
r2 <- getResource
f <- getFilter
w <- lift $ create parent wndclass (toBeh (r2 <> r))
tellReturn (return w)
(x, y) <- lift $ runReaderT (runWriterT (unHook hook)) (w { event = f (event w) }, r2, f)
tellReturn y
return x
{-# INLINE hCreate' #-}
hCreate' wndclass r = hCreate wndclass r window
-- | Filters are hooks modifying the event stream the caller sees.
{-# INLINE getFilter #-}
getFilter :: (Monad m) => Hook r x m (x -> x)
getFilter = Hook $ liftM (\(_, _, f) -> f) $ lift ask
-- | Resources are a way of augmenting the behaviors used by subsequent controls.
{-# INLINE getResource #-}
getResource :: (Monad m) => Hook r x m r
getResource = Hook $ liftM (\(_, r, _) -> r) $ lift ask
{-# INLINE tellReturn #-}
tellReturn r = Hook $ tell r
{-# INLINE getReturns #-}
getReturns :: (Monoid r, Monad m) => r -> (x -> x) -> Hook r x m t -> Hook r x m (t, Event Window)
getReturns r f h = do
w <- window
r2 <- getResource
f2 <- getFilter
lift $ runReaderT (runWriterT (unHook h)) (w, r <> r2, f . f2)
{-# INLINE getReturns1 #-}
getReturns1 r f h = do
w <- window
lift $ runReaderT (runWriterT (unHook h)) (w, r, f)
-- | 'window' gives the identity of the parent window.
{-# INLINE window #-}
window :: (Monad m) => Hook r x m Window
window = Hook $ liftM (\(w, _, _) -> w) $ lift ask
-- | Functional that fixes the usual type parameters.
{-# INLINE ty #-}
ty :: Hook (Behavior Appearance) (Event Message) Act t -> Hook (Behavior Appearance) (Event Message) Act t
ty = id
-- | Runs a procedure in the Hook monad transformer.
{-# INLINE runHook #-}
runHook ~(Hook h) = runReaderT (runWriterT h) (desktop, mempty, id)
err = error "FRP.Hook.toBeh: no start value"
-- | These are common ways of converting data to a behavior.
class Resource r where
toBeh :: r -> Behavior Appearance
instance Resource (Behavior Appearance) where
toBeh = id
instance Resource (Event Appearance) where
toBeh = stepper err
instance Resource (Appearance, Event (Appearance -> Appearance)) where
toBeh (x, e) = stepper x $ corec (\x f t -> (f x, f x, t)) x e
instance Resource Appearance where
toBeh = pure
instance Resource (Draw ()) where
toBeh d = pure (Appearance d "" (0, 0, 0, 0))
instance Resource BMP where
toBeh = toBeh . mask 1 (0, 0)
instance Resource String where
toBeh s = pure (Appearance (return ()) s (0, 0, 0, 0))
instance Monoid BMP where
mempty = newBitmap (0, 0)
mappend b b2 = fst $ onNewBitmap (wid `max` wid2, ht + ht2) $ do
mask 1 (0, 0) b
mask 1 (0, ht) b2 where
(wid, ht) = askDims' b
(wid2, ht2) = askDims' b2
instance Monoid Appearance where
mempty = Appearance (return ()) "" (0, 0, 0, 0)
mappend a a2 = Appearance (draw a >> draw a2)
(text a ++ text a2)
(x1 `max` x3, y1 `max` y3, x2 `max` x4, y2 `max` y4) where
(x1, y1, x2, y2) = rect a
(x3, y3, x4, y4) = rect a2
instance Monoid (Draw ()) where
mempty = return ()
mappend = (>>)