module Internal.UICircuit ( UICircuit (UICircuit), fromCircuit, run ) where -- Prelude import Prelude hiding ((.)) -- Control import Control.Category as Category hiding (id) import qualified Control.Category as Category import Control.Arrow as Arrow import Control.Arrow.Transformer.Reader as ReaderArrow import Control.Concurrent.MVar as MVar -- FRP.Grapefruit import FRP.Grapefruit.Circuit as Circuit import FRP.Grapefruit.Signal as Signal import FRP.Grapefruit.Signal.Discrete as DSignal -- Internal import {-# SOURCE #-} Internal.UIItem as UIItem -- Graphics.UI.Grapefruit import Graphics.UI.Grapefruit.Backend as UIBackend import Graphics.UI.Grapefruit.Comp as UIComp {-| The type of user interface circuits. The @item@ parameter is a phantom parameter which says which kind of items the circuit contains. It should be an instance of 'Item'. -} newtype UICircuit item uiBackend era i o = UICircuit (ReaderArrow (Placement item uiBackend) (Circuit era) i o) -- manual deriving because of GHC bug #1133 instance Category (UICircuit item uiBackend era) where id = UICircuit Category.id UICircuit arrow1 . UICircuit arrow2 = UICircuit (arrow1 . arrow2) instance Arrow (UICircuit item uiBackend era) where arr fun = UICircuit (arr fun) first (UICircuit arrow) = UICircuit (first arrow) second (UICircuit arrow) = UICircuit (second arrow) UICircuit arrow1 *** UICircuit arrow2 = UICircuit (arrow1 *** arrow2) UICircuit arrow1 &&& UICircuit arrow2 = UICircuit (arrow1 &&& arrow2) instance ArrowLoop (UICircuit item uiBackend era) where loop (UICircuit arrow) = UICircuit (Arrow.loop arrow) -- “really manual” instance (not just because of manual deriving) instance UIComp UICircuit where circuit |>> uiCircuit = fromCircuit circuit >>> uiCircuit uiCircuit >>| circuit = uiCircuit >>> fromCircuit circuit loop = Arrow.loop toUICircuit = id fromUIItem (UIItem uiCircuit) = uiCircuit -- |Converts an ordinary circuit into a user interface circuit that contains no items. fromCircuit :: Circuit era i o -> UICircuit item uiBackend era i o fromCircuit = liftReader >>> UICircuit {-| Runs a user interface circuit. @run@ quits when the output signal of the circuit has a first occurence. The universal quantification of the circuit’s era parameter ensures that the circuit does not use signals which are produced outside the circuit and therefore avoids era mismatches. -} run :: (UIBackend uiBackend) => uiBackend -- ^the user interface backend to use -> (forall era. UICircuit Window uiBackend era i (DSignal era o)) -- ^the circuit to run -> i -- ^the input of the ciruit -> IO o {-^ an action running the circuit and returning the value of the output signal’s first occurence -} run uiBackend uiCircuit i = run where run = do oMVar <- newEmptyMVar UIBackend.initialize uiBackend let circuitCreation = Circuit.create (proc _ -> do quittingReq <- runReader $ case uiCircuit of UICircuit arrow -> arrow -< (i,topLevel uiBackend) consume $ DSignal.consumer qReqHdlr -< quittingReq) () qReqHdlr o = do requestQuitting uiBackend putMVar oMVar o (_,finalizeCircuit) <- circuitCreation UIBackend.handleEvents uiBackend finalizeCircuit UIBackend.finalize uiBackend takeMVar oMVar -- Where are the top level windows removed?