module Reflex.Dom.Internal where
import Prelude hiding (mapM, mapM_, concat, sequence, sequence_)
import Reflex.Dom.Internal.Foreign
import Reflex.Dom.Class
import GHCJS.DOM hiding (runWebGUI)
import GHCJS.DOM.Types hiding (Widget, unWidget, Event)
import GHCJS.DOM.Node
import GHCJS.DOM.HTMLElement
import GHCJS.DOM.Document
import Reflex.Class
import Reflex.Host.Class
import Reflex.Spider (Spider, SpiderHost (..))
import Control.Lens
import Control.Monad hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
import Control.Monad.Reader hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
import Control.Monad.Ref
import Control.Monad.State.Strict hiding (mapM, mapM_, forM, forM_, sequence, sequence_, get)
import Control.Monad.Exception
import Control.Concurrent
import Control.Applicative
import Data.ByteString (ByteString)
import Data.Dependent.Sum (DSum (..))
import Data.Foldable
import Data.Traversable
import qualified Data.Text as T
import Data.Text.Encoding
import Data.Monoid ((<>))
data GuiEnv t h
= GuiEnv { _guiEnvDocument :: !HTMLDocument
, _guiEnvPostGui :: !(h () -> IO ())
, _guiEnvRunWithActions :: !([DSum (EventTrigger t)] -> h ())
, _guiEnvWebView :: !WebView
}
newtype Gui t h m a = Gui { unGui :: ReaderT (GuiEnv t h) m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadException, MonadAsyncException)
runGui :: Gui t h m a -> GuiEnv t h -> m a
runGui (Gui g) env = runReaderT g env
instance MonadTrans (Gui t h) where
lift = Gui . lift
instance MonadRef m => MonadRef (Gui t h m) where
type Ref (Gui t h m) = Ref m
newRef = lift . newRef
readRef = lift . readRef
writeRef r = lift . writeRef r
instance MonadAtomicRef m => MonadAtomicRef (Gui t h m) where
atomicModifyRef r f = lift $ atomicModifyRef r f
instance MonadSample t m => MonadSample t (Gui t h m) where
sample b = lift $ sample b
instance MonadHold t m => MonadHold t (Gui t h m) where
hold a0 e = lift $ hold a0 e
instance (Reflex t, MonadReflexCreateTrigger t m) => MonadReflexCreateTrigger t (Gui t h m) where
newEventWithTrigger = lift . newEventWithTrigger
newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
data WidgetEnv
= WidgetEnv { _widgetEnvParent :: !Node
}
data WidgetState t m
= WidgetState { _widgetStatePostBuild :: !(m ())
, _widgetStateVoidActions :: ![Event t (m ())]
}
liftM concat $ mapM makeLenses
[ ''WidgetEnv
, ''WidgetState
, ''GuiEnv
]
instance Monad m => HasDocument (Gui t h m) where
askDocument = Gui $ view guiEnvDocument
instance HasDocument m => HasDocument (Widget t m) where
askDocument = lift askDocument
instance Monad m => HasWebView (Gui t h m) where
askWebView = Gui $ view guiEnvWebView
instance MonadIORestore m => MonadIORestore (Gui t h m) where
askRestore = Gui $ do
r <- askRestore
return $ Restore $ restore r . unGui
instance HasWebView m => HasWebView (Widget t m) where
askWebView = lift askWebView
instance (MonadRef h, Ref h ~ Ref m, MonadRef m) => HasPostGui t h (Gui t h m) where
askPostGui = Gui $ view guiEnvPostGui
askRunWithActions = Gui $ view guiEnvRunWithActions
instance HasPostGui t h m => HasPostGui t h (Widget t m) where
askPostGui = lift askPostGui
askRunWithActions = lift askRunWithActions
type WidgetInternal t m a = ReaderT WidgetEnv (StateT (WidgetState t m) m) a
instance MonadTrans (Widget t) where
lift = Widget . lift . lift
newtype Widget t m a = Widget { unWidget :: WidgetInternal t m a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
instance MonadSample t m => MonadSample t (Widget t m) where
sample b = lift $ sample b
instance MonadHold t m => MonadHold t (Widget t m) where
hold v0 e = lift $ hold v0 e
instance MonadRef m => MonadRef (Widget t m) where
type Ref (Widget t m) = Ref m
newRef = lift . newRef
readRef = lift . readRef
writeRef r = lift . writeRef r
instance MonadAtomicRef m => MonadAtomicRef (Widget t m) where
atomicModifyRef r f = lift $ atomicModifyRef r f
instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (Widget t m) where
newEventWithTrigger = lift . newEventWithTrigger
newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
instance ( MonadRef m, Ref m ~ Ref IO, MonadRef h, Ref h ~ Ref IO
, MonadIO m, MonadAsyncException m, MonadIO h, MonadAsyncException h, Functor m
, ReflexHost t, MonadReflexCreateTrigger t m, MonadSample t m, MonadHold t m
, MonadFix m, HasWebView h, HasPostGui t h h
) => MonadWidget t (Widget t (Gui t h m)) where
type WidgetHost (Widget t (Gui t h m)) = Gui t h m
type GuiAction (Widget t (Gui t h m)) = h
askParent = Widget $ view widgetEnvParent
liftWidgetHost = Widget . lift . lift
schedulePostBuild a = Widget $ widgetStatePostBuild %= (a>>)
addVoidAction a = Widget $ widgetStateVoidActions %= (a:)
subWidget n child = Widget $ local (widgetEnvParent .~ toNode n) $ unWidget child
subWidgetWithVoidActions n child = Widget $ do
oldActions <- use widgetStateVoidActions
widgetStateVoidActions .= []
result <- local (widgetEnvParent .~ toNode n) $ unWidget child
actions <- use widgetStateVoidActions
widgetStateVoidActions .= oldActions
return (result, mergeWith (>>) actions)
getRunWidget = return runWidget
runWidget :: (Monad m, Reflex t, IsNode n) => n -> Widget t (Gui t h m) a -> WidgetHost (Widget t (Gui t h m)) (a, WidgetHost (Widget t (Gui t h m)) (), Event t (WidgetHost (Widget t (Gui t h m)) ()))
runWidget rootElement w = do
(result, WidgetState postBuild voidActions) <- runStateT (runReaderT (unWidget w) (WidgetEnv $ toNode rootElement)) (WidgetState (return ()) [])
let voidAction = mergeWith (>>) voidActions
return (result, postBuild, voidAction)
holdOnStartup :: MonadWidget t m => a -> WidgetHost m a -> m (Behavior t a)
holdOnStartup a0 ma = do
(startupDone, startupDoneTriggerRef) <- newEventWithTriggerRef
schedulePostBuild $ do
a <- ma
runFrameWithTriggerRef startupDoneTriggerRef a
hold a0 startupDone
mainWidget :: Widget Spider (Gui Spider (WithWebView SpiderHost) (HostFrame Spider)) () -> IO ()
mainWidget w = runWebGUI $ \webView -> do
Just doc <- liftM (fmap castToHTMLDocument) $ webViewGetDomDocument webView
Just body <- documentGetBody doc
attachWidget body webView w
mainWidgetWithHead :: Widget Spider (Gui Spider (WithWebView SpiderHost) (HostFrame Spider)) () -> Widget Spider (Gui Spider (WithWebView SpiderHost) (HostFrame Spider)) () -> IO ()
mainWidgetWithHead h b = runWebGUI $ \webView -> do
Just doc <- liftM (fmap castToHTMLDocument) $ webViewGetDomDocument webView
Just headElement <- liftM (fmap castToHTMLElement) $ documentGetHead doc
attachWidget headElement webView h
Just body <- documentGetBody doc
attachWidget body webView b
mainWidgetWithCss :: ByteString -> Widget Spider (Gui Spider (WithWebView SpiderHost) (HostFrame Spider)) () -> IO ()
mainWidgetWithCss css w = runWebGUI $ \webView -> do
Just doc <- liftM (fmap castToHTMLDocument) $ webViewGetDomDocument webView
Just headElement <- liftM (fmap castToHTMLElement) $ documentGetHead doc
htmlElementSetInnerHTML headElement $ "<style>" <> T.unpack (decodeUtf8 css) <> "</style>"
Just body <- documentGetBody doc
attachWidget body webView w
newtype WithWebView m a = WithWebView { unWithWebView :: ReaderT WebView m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadException, MonadAsyncException)
instance (Monad m) => HasWebView (WithWebView m) where
askWebView = WithWebView ask
instance HasPostGui t h m => HasPostGui t (WithWebView h) (WithWebView m) where
askPostGui = do
postGui <- lift askPostGui
webView <- askWebView
return $ \h -> postGui $ runWithWebView h webView
askRunWithActions = do
runWithActions <- lift askRunWithActions
return $ lift . runWithActions
instance HasPostGui Spider SpiderHost SpiderHost where
askPostGui = return $ \h -> liftIO $ runSpiderHost h
askRunWithActions = return fireEvents
instance MonadTrans WithWebView where
lift = WithWebView . lift
instance MonadRef m => MonadRef (WithWebView m) where
type Ref (WithWebView m) = Ref m
newRef = lift . newRef
readRef = lift . readRef
writeRef r = lift . writeRef r
instance MonadAtomicRef m => MonadAtomicRef (WithWebView m) where
atomicModifyRef r = lift . atomicModifyRef r
instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (WithWebView m) where
newEventWithTrigger = lift . newEventWithTrigger
newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
instance MonadSubscribeEvent t m => MonadSubscribeEvent t (WithWebView m) where
subscribeEvent = lift . subscribeEvent
instance MonadReflexHost t m => MonadReflexHost t (WithWebView m) where
type ReadPhase (WithWebView m) = ReadPhase m
fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
runHostFrame = lift . runHostFrame
runWithWebView :: WithWebView m a -> WebView -> m a
runWithWebView = runReaderT . unWithWebView
attachWidget :: (IsHTMLElement e) => e -> WebView -> Widget Spider (Gui Spider (WithWebView SpiderHost) (HostFrame Spider)) a -> IO a
attachWidget rootElement wv w = runSpiderHost $ flip runWithWebView wv $ do
Just doc <- liftM (fmap castToHTMLDocument) $ liftIO $ nodeGetOwnerDocument rootElement
frames <- liftIO newChan
rec let guiEnv = GuiEnv doc (writeChan frames . runSpiderHost . flip runWithWebView wv) runWithActions wv :: GuiEnv Spider (WithWebView SpiderHost)
runWithActions dm = do
voidActionNeeded <- fireEventsAndRead dm $ do
sequence =<< readEvent voidActionHandle
runHostFrame $ runGui (sequence_ voidActionNeeded) guiEnv
Just df <- liftIO $ documentCreateDocumentFragment doc
(result, voidAction) <- runHostFrame $ flip runGui guiEnv $ do
(r, postBuild, va) <- runWidget df w
postBuild
return (r, va)
liftIO $ htmlElementSetInnerHTML rootElement ""
_ <- liftIO $ nodeAppendChild rootElement $ Just df
voidActionHandle <- subscribeEvent voidAction
_ <- liftIO $ forkIO $ forever $ postGUISync =<< readChan frames
return result