packages feed

reflex-gi-gtk-0.1.0.0: src/Reflex/GI/Gtk/Output.hs

-- This Source Code Form is subject to the terms of the Mozilla Public
-- License, v. 2.0. If a copy of the MPL was not distributed with this
-- file, You can obtain one at https://mozilla.org/MPL/2.0/.

{-# LANGUAGE RankNTypes, KindSignatures, DataKinds, ConstraintKinds, FlexibleContexts, GADTs #-}
{-# LANGUAGE FunctionalDependencies, FlexibleInstances #-}

module Reflex.GI.Gtk.Output
  ( Sinkable(toSinkEvent)
  , MonadGtkSink
  , sink1
  , sink
  , ReactiveAttrOp(..)
  ) where

import Data.GI.Base.Attributes ( AttrBaseTypeConstraint
                               , AttrGetType
                               , AttrInfo
                               , AttrLabelProxy
                               , AttrOp ( (:=)
                                        , (:=>)
                                        , (:~)
                                        , (:~>)
                                        )
                               , AttrOpAllowed
                               , AttrOpTag( AttrGet
                                          , AttrSet
                                          )
                               , AttrSetTypeConstraint
                               , set
                               )
import Data.GI.Base.Overloading ( HasAttributeList
                                , ResolveAttribute
                                )
import GHC.TypeLits (Symbol)
import Reflex ( Dynamic
              , Event
              , PerformEvent
              , Performable
              , PostBuild
              , (<@)
              , current
              , getPostBuild
              , leftmost
              , performEvent_
              , updated
              )
import Reflex.GI.Gtk.Run.Class ( MonadRunGtk
                               , runGtk
                               )

type MonadGtkSink t m = ( PerformEvent t m
                        , PostBuild t m
                        , MonadRunGtk (Performable m)
                        )

class Sinkable t s | s -> t where
  toSinkEvent :: (MonadGtkSink t m) => s a -> m (Event t a)

instance Sinkable t (Event t) where
  toSinkEvent = pure

instance Sinkable t (Dynamic t) where
  toSinkEvent d = do
    postBuild <- getPostBuild
    pure $ leftmost
      [ updated d
      , current d <@ postBuild
      ]

sink1 :: (MonadGtkSink t m)
      => object
      -> ReactiveAttrOp t object 'AttrSet
      -> m ()
sink1 object reactiveOp =
  withReactiveAttrOp reactiveOp $ \plainOp updates ->
      toSinkEvent updates >>= performEvent_ . fmap (\x -> runGtk $ set object [plainOp x])

infixr 0 :==, :==>, :~~, :~~>
data ReactiveAttrOp t obj (tag :: AttrOpTag) where
  (:==) :: ( HasAttributeList obj
           , info ~ ResolveAttribute attr obj
           , AttrInfo info
           , AttrBaseTypeConstraint info obj
           , AttrOpAllowed tag info obj
           , AttrSetTypeConstraint info a
           , Sinkable t s
           )
        => AttrLabelProxy (attr :: Symbol)
        -> s a
        -> ReactiveAttrOp t obj tag
  (:==>) :: ( HasAttributeList obj
            , info ~ ResolveAttribute attr obj
            , AttrInfo info
            , AttrBaseTypeConstraint info obj
            , AttrOpAllowed tag info obj
            , AttrSetTypeConstraint info a
            , Sinkable t s
            )
         => AttrLabelProxy (attr :: Symbol)
         -> s (IO a)
         -> ReactiveAttrOp t obj tag
  (:~~) :: ( HasAttributeList obj
           , info ~ ResolveAttribute attr obj
           , AttrInfo info
           , AttrBaseTypeConstraint info obj
           , tag ~ 'AttrSet
           , AttrOpAllowed 'AttrSet info obj
           , AttrOpAllowed 'AttrGet info obj
           , AttrSetTypeConstraint info a
           , a ~ AttrGetType info
           , Sinkable t s
           )
        => AttrLabelProxy (attr :: Symbol)
        -> s (a -> a)
        -> ReactiveAttrOp t obj tag
  (:~~>) :: ( HasAttributeList obj
            , info ~ ResolveAttribute attr obj
            , AttrInfo info
            , AttrBaseTypeConstraint info obj
            , tag ~ 'AttrSet
            , AttrOpAllowed 'AttrSet info obj
            , AttrOpAllowed 'AttrGet info obj
            , AttrSetTypeConstraint info a
            , a ~ AttrGetType info
            , Sinkable t s
            )
         => AttrLabelProxy (attr :: Symbol)
         -> s (a -> IO a)
         -> ReactiveAttrOp t obj tag

withReactiveAttrOp :: ReactiveAttrOp t obj tag
                   -> (forall a s. Sinkable t s => (a -> AttrOp obj tag) -> s a -> b)
                   -> b
withReactiveAttrOp (attr :== updates) f = f (attr :=) updates
withReactiveAttrOp (attr :==> updates) f = f (attr :=>) updates
withReactiveAttrOp (attr :~~ updates) f = f (attr :~) updates
withReactiveAttrOp (attr :~~> updates) f = f (attr :~>) updates

sink :: ( MonadGtkSink t m
        )
     => object -> [ReactiveAttrOp t object 'AttrSet] -> m ()
sink object = mapM_ (sink1 object)