module Reflex.Monad.Time ( MonadTime (..) , delay_ , animate , animateClip , animateOn , play , playClip , playClamp , playOn , match , matchBy , pushFor ) where import Reflex.Animation import Reflex.Monad import Reflex import Data.VectorSpace import Data.Functor import Control.Applicative import qualified Data.List.NonEmpty as NE import Data.List.NonEmpty (NonEmpty) import Control.Monad class (MonadReflex t m, RealFrac time) => MonadTime t time m | m -> t time where -- | A behavior for time, must be up to date -- (i.e. represents current time not previous time) getTime :: m (Behavior t time) -- | Fire an event at or just after a period of time after :: time -> m (Event t ()) -- | Delay an event by a period of time, returns a list in case -- two delayed events occur within the sampling rate of the framework delay :: Event t (a, time) -> m (Event t (NonEmpty a)) -- | Delay a void event stream delay_ :: (MonadTime t time m) => Event t time -> m (Event t ()) delay_ e = void <$> delay (((), ) <$> e) -- | Sample a Clip during it's period, outside it's period return Nothing animateClip :: (Reflex t, RealFrac time) => Clip time a -> Behavior t time -> Behavior t (Maybe a) animateClip clip = animate $ toMaybe clip -- | Animate an infinite animation using framework time animate :: (Reflex t, RealFrac time) => Animation time a -> Behavior t time -> Behavior t a animate anim time = sampleAt anim <$> time -- | Helper for animateOn using the underlying representation (time -> a) sampleOn :: (Reflex t, RealFrac time) => Event t (time -> a) -> Behavior t time -> Event t (Behavior t a) sampleOn e t = attachWith startAt t e where startAt start f = f . subtract start <$> t -- | Create a Behavior from an infinite animation on the occurance of the event animateOn :: (Reflex t, RealFrac time) => Event t (Animation time a) -> Behavior t time -> Event t (Behavior t a) animateOn e = sampleOn (sampleAt <$> e) -- | Record time offset from the current time fromNow :: MonadTime t time m => m (Behavior t time) fromNow = do time <- getTime start <- sample time return (subtract start <$> time) -- | Play an animation clip, giving a Behavior of it's value and an Event firing as it finishes playClip :: MonadTime t time m => Clip time a -> m (Behavior t (Maybe a), Event t ()) playClip clip = do (b, done) <- playClamp clip b' <- switcher (Just <$> b) (constant Nothing <$ done) return (b', done) -- | Play an animation clip, except clamp the ends so the Behavior is no longer 'Maybe a' playClamp :: MonadTime t time m => Clip time a -> m (Behavior t a, Event t ()) playClamp clip = do b <- play (clamped clip) done <- after (period clip) return (b, done) -- | Play an infinite animation starting now play :: MonadTime t time m => Animation time a -> m (Behavior t a) play anim = do time <- fromNow return (sampleAt anim <$> time) -- | Play an animation clip starting on the occurance of an Event -- if another play event occurs before the last one has finished, switch to that one instead. playOn :: MonadTime t time m => Event t (Clip time a) -> m (Behavior t (Maybe a), Event t ()) playOn e = do time <- getTime done <- delay_ (period <$> e) b <- hold (constant Nothing) $ leftmost [constant Nothing <$ done, fmap Just <$> animateOn (clamped <$> e) time] return (join b, done) -- | Helper functions using filter with Eq match :: (Reflex t, Eq a) => a -> Event t a -> Event t () match a = matchBy (== a) matchBy :: (Reflex t) => (a -> Bool) -> Event t a -> Event t () matchBy f = void . ffilter f -- | Helper for pushAlways pushFor :: Reflex t => Event t a -> (a -> PushM t b) -> Event t b pushFor = flip pushAlways