on develop help sign in
Merge branch 'fragments-only' into jsaddle

# Conflicts: # reflex-dom-core/src/Foreign/JavaScript/TH.hs # reflex-dom-core/src/Reflex/Dom/Builder/Immediate.hs

commit6244fa69f115d5ae01aadc6b41b956347315bdf0 authorRyan Trinkle <ryan.trinkle@gmail.com> dateThu 20 Apr 2017, 08:39 (10 years ago) parent31adfb3a1b9ffe
7 files changed+491−101
reflex-dom-core/src/Foreign/JavaScript/TH.hs+1−1 reflex-dom-core/src/Reflex/Dom/Builder/Class.hs+24 reflex-dom-core/src/Reflex/Dom/Builder/Immediate.hs+302−81 reflex-dom-core/src/Reflex/Dom/Builder/Static.hs+44−16 reflex-dom-core/src/Reflex/Dom/Main.hs+6 reflex-dom-core/src/Reflex/Dom/Widget/Basic.hs+7−3 test/prebuild.hs+107
reflex-dom-core/src/Foreign/JavaScript/TH.hs+1−1
@@ -178,7 +178,7 @@ instance MonadTransControl (WithJSContextSingleton x) where
178178 restoreT = defaultRestoreT WithJSContextSingleton
179179
180180instance PerformEvent t m => PerformEvent t (WithJSContextSingleton x m) where
181− type Performable (WithJSContextSingleton x m) = WithJSContextSingleton x (Performable m)
181+ type Performable (WithJSContextSingleton x m) = WithJSContextSingleton x (Performable m) --TODO: Can we eliminate this wrapper?
182182 {-# INLINABLE performEvent_ #-}
183183 performEvent_ e = liftWith $ \run -> performEvent_ $ fmap run e
184184 {-# INLINABLE performEvent #-}
reflex-dom-core/src/Reflex/Dom/Builder/Class.hs+24
@@ -149,6 +149,25 @@ class (Monad m, Reflex t, DomSpace (DomBuilderSpace m), MonadAdjust t m) => DomB
149149 { _rawElementConfig_eventSpec = _rawElementConfig_eventSpec cfg
150150 }
151151 {-# INLINABLE wrapRawElement #-}
152+ notReadyUntil :: Event t a -> m ()
153+ default notReadyUntil :: ( MonadTrans f
154+ , m ~ f m'
155+ , DomBuilder t m'
156+ )
157+ => Event t a -> m ()
158+ notReadyUntil = lift . notReadyUntil
159+ notReady :: m ()
160+ default notReady :: ( MonadTrans f
161+ , m ~ f m'
162+ , DomBuilder t m'
163+ )
164+ => m ()
165+ notReady = lift notReady
166+
167+class DomBuilder t m => MountableDomBuilder t m where
168+ type DomFragment m :: *
169+ buildDomFragment :: m a -> m (DomFragment m, a)
170+ mountDomFragment :: DomFragment m -> Event t (DomFragment m) -> m ()
152171
153172type Namespace = Text
154173
@@ -512,6 +531,11 @@ instance (DomBuilder t m, PerformEvent t m, MonadFix m, MonadHold t m) => DomBui
512531 type DomBuilderSpace (PostBuildT t m) = DomBuilderSpace m
513532 wrapRawElement e cfg = liftWith $ \run -> wrapRawElement e $ fmap1 run cfg
514533
534+instance (MountableDomBuilder t m, PerformEvent t m, MonadFix m, MonadHold t m) => MountableDomBuilder t (PostBuildT t m) where
535+ type DomFragment (PostBuildT t m) = DomFragment m
536+ buildDomFragment = liftThrough buildDomFragment
537+ mountDomFragment f0 f' = lift $ mountDomFragment f0 f'
538+
515539instance (DomBuilder t m, Monoid w, MonadHold t m, MonadFix m) => DomBuilder t (DynamicWriterT t w m) where
516540 type DomBuilderSpace (DynamicWriterT t w m) = DomBuilderSpace m
517541 textNode = liftTextNode
reflex-dom-core/src/Reflex/Dom/Builder/Immediate.hs+302−81
@@ -5,6 +5,7 @@
55{-# LANGUAGE EmptyDataDecls #-}
66{-# LANGUAGE FlexibleContexts #-}
77{-# LANGUAGE FlexibleInstances #-}
8+{-# LANGUAGE GADTs #-}
89{-# LANGUAGE GeneralizedNewtypeDeriving #-}
910{-# LANGUAGE LambdaCase #-}
1011{-# LANGUAGE MultiParamTypeClasses #-}
@@ -83,6 +84,8 @@ import Reflex.Class as Reflex
8384import Reflex.Dom.Builder.Class
8485import Reflex.Dynamic
8586import Reflex.Host.Class
87+import qualified Reflex.Patch.DMap as PatchDMap
88+import qualified Reflex.Patch.DMapWithMove as PatchDMapWithMove
8689import Reflex.PerformEvent.Class
8790import Reflex.PostBuild.Class
8891import Reflex.TriggerEvent.Base hiding (askEvents)
@@ -105,10 +108,12 @@ import Data.Dependent.Sum
105108import Data.Functor.Compose
106109import Data.Functor.Constant
107110import Data.Functor.Misc
111+import Data.Functor.Product
108112import Data.IORef
109113import qualified Data.Map as Map
110114import Data.Maybe
111−import Data.Monoid
115+import Data.Monoid hiding (Product)
116+import Data.Some (Some)
112117import qualified Data.Some as Some
113118import Data.Text (Text)
114119import qualified Data.Text as T
@@ -142,13 +147,15 @@ import Reflex.Requester.Class
142147
143148data TriggerRef t a = TriggerRef { unTriggerRef :: IORef (Maybe (EventTrigger t a)) }
144149
145−data ImmediateDomBuilderEnv t m
150+data ImmediateDomBuilderEnv t
146151 = ImmediateDomBuilderEnv { _immediateDomBuilderEnv_document :: Document
147152 , _immediateDomBuilderEnv_parent :: Node
148153 , _immediateDomBuilderEnv_events :: Chan [DSum (TriggerRef t) TriggerInvocation]
154+ , _immediateDomBuilderEnv_unreadyChildren :: IORef Word -- Number of children who still aren't fully rendered
155+ , _immediateDomBuilderEnv_commitAction :: JSM () -- Action to take when all children are ready --TODO: we should probably get rid of this once we invoke it
149156 }
150157
151−newtype ImmediateDomBuilderT t m a = ImmediateDomBuilderT { unImmediateDomBuilderT :: ReaderT (ImmediateDomBuilderEnv t m) (RequesterT t JSM Identity m) a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
158+newtype ImmediateDomBuilderT t m a = ImmediateDomBuilderT { unImmediateDomBuilderT :: ReaderT (ImmediateDomBuilderEnv t) (RequesterT t JSM Identity m) a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
152159
153160#ifndef __GHCJS__
154161instance MonadJSM m => MonadJSM (ImmediateDomBuilderT t m) where
@@ -172,7 +179,7 @@ instance (PerformEvent t m, PrimMonad m) => DomRenderHook t (ImmediateDomBuilder
172179 requestDomAction_ = ImmediateDomBuilderT . lift . requesting_
173180
174181{-# INLINABLE runImmediateDomBuilderT #-}
175−runImmediateDomBuilderT :: (Reflex t, MonadFix m, PerformEvent t m, MonadJSM (Performable m)) => ImmediateDomBuilderT t m a -> ImmediateDomBuilderEnv t m -> m a
182+runImmediateDomBuilderT :: (Reflex t, MonadFix m, PerformEvent t m, MonadJSM (Performable m)) => ImmediateDomBuilderT t m a -> ImmediateDomBuilderEnv t -> m a
176183runImmediateDomBuilderT (ImmediateDomBuilderT a) env = do
177184 rec (x, req) <- runRequesterT (runReaderT a env) rsp
178185 rsp <- performEvent $ ffor req $ \rm -> liftJSM $ DMap.traverseWithKey (\_ r -> Identity <$> r) rm
@@ -274,9 +281,9 @@ newtype EventFilterTriggerRef t er (en :: EventTag) = EventFilterTriggerRef (IOR
274281wrap :: forall m er t. SupportsImmediateDomBuilder t m => RawElement GhcjsDomSpace -> RawElementConfig er t (ImmediateDomBuilderT t m) -> ImmediateDomBuilderT t m (Element er GhcjsDomSpace t)
275282wrap e cfg = do
276283 events <- askEvents
277− mapM_ (requestDomAction_ . fmap (imapM_ $ \(AttributeName mAttrNamespace n) mv -> case mAttrNamespace of
284+ forM_ (_rawElementConfig_modifyAttributes cfg) $ \modifyAttrs -> requestDomAction_ $ ffor modifyAttrs $ imapM_ $ \(AttributeName mAttrNamespace n) mv -> case mAttrNamespace of
278285 Nothing -> maybe (removeAttribute e n) (setAttribute e n) mv
279− Just ns -> maybe (removeAttributeNS e (Just ns) n) (setAttributeNS e (Just ns) n) mv)) (_rawElementConfig_modifyAttributes cfg)
286+ Just ns -> maybe (removeAttributeNS e (Just ns) n) (setAttributeNS e (Just ns) n) mv
280287 eventTriggerRefs :: DMap EventName (EventFilterTriggerRef t er) <- liftJSM $ fmap DMap.fromList $ forM (DMap.toList $ _ghcjsEventSpec_filters $ _rawElementConfig_eventSpec cfg) $ \(en :=> GhcjsEventFilter f) -> do
281288 triggerRef <- liftIO $ newIORef Nothing
282289 _ <- elementOnEventName en e $ do
@@ -309,13 +316,16 @@ wrap e cfg = do
309316 --TODO: I don't think this is quite right: if a new trigger is created between when this is enqueued and when it fires, this may not work quite right
310317 ref <- newIORef $ Just t
311318 writeChan events [TriggerRef ref :=> TriggerInvocation v (return ())])
312− return $ Element es e
319+ return $ Element
320+ { _element_events = es
321+ , _element_raw = e
322+ }
313323
314324{-# INLINABLE makeElement #-}
315325makeElement :: forall er t m a. SupportsImmediateDomBuilder t m => Text -> ElementConfig er t (ImmediateDomBuilderT t m) -> ImmediateDomBuilderT t m a -> ImmediateDomBuilderT t m ((Element er GhcjsDomSpace t, a), DOM.Element)
316326makeElement elementTag cfg child = do
317− doc <- askDocument
318327 env <- ImmediateDomBuilderT ask
328+ let doc = _immediateDomBuilderEnv_document env
319329 e <- liftJSM $ case cfg ^. namespace of
320330 Nothing -> createElementUnchecked doc (Just elementTag)
321331 Just ens -> createElementNSUnchecked doc (Just ens) (Just elementTag)
@@ -507,96 +517,307 @@ instance SupportsImmediateDomBuilder t m => DomBuilder t (ImmediateDomBuilderT t
507517 return (wrapped, result)
508518 placeRawElement = append
509519 wrapRawElement = wrap
520+ notReadyUntil e = do
521+ eOnce <- headE e
522+ env <- ImmediateDomBuilderT ask
523+ let unreadyChildren = _immediateDomBuilderEnv_unreadyChildren env
524+ liftIO $ modifyIORef' unreadyChildren succ
525+ let ready = do
526+ old <- liftIO $ readIORef unreadyChildren
527+ let new = pred old
528+ liftIO $ writeIORef unreadyChildren $! new
529+ when (new == 0) $ _immediateDomBuilderEnv_commitAction env
530+ requestDomAction_ $ ready <$ eOnce
531+ notReady = do
532+ env <- ImmediateDomBuilderT ask
533+ let unreadyChildren = _immediateDomBuilderEnv_unreadyChildren env
534+ liftIO $ modifyIORef' unreadyChildren succ
535+
536+data FragmentState
537+ = FragmentState_Unmounted
538+ | FragmentState_Mounted (DOM.Text, DOM.Text)
539+
540+data ImmediateDomFragment = ImmediateDomFragment
541+ { _immediateDomFragment_document :: DOM.DocumentFragment
542+ , _immediateDomFragment_state :: IORef FragmentState
543+ }
544+
545+extractFragment :: MonadJSM m => ImmediateDomFragment -> m ()
546+extractFragment fragment = do
547+ state <- liftIO $ readIORef $ _immediateDomFragment_state fragment
548+ case state of
549+ FragmentState_Unmounted -> return ()
550+ FragmentState_Mounted (before, after) -> do
551+ extractBetweenExclusive (_immediateDomFragment_document fragment) before after
552+ liftIO $ writeIORef (_immediateDomFragment_state fragment) FragmentState_Unmounted
553+
554+instance SupportsImmediateDomBuilder t m => MountableDomBuilder t (ImmediateDomBuilderT t m) where
555+ type DomFragment (ImmediateDomBuilderT t m) = ImmediateDomFragment
556+ buildDomFragment w = do
557+ initialEnv <- ImmediateDomBuilderT ask
558+ df <- createDocumentFragmentUnchecked $ _immediateDomBuilderEnv_document initialEnv
559+ result <- lift $ runImmediateDomBuilderT w $ initialEnv
560+ { _immediateDomBuilderEnv_parent = toNode df
561+ }
562+ state <- liftIO $ newIORef FragmentState_Unmounted
563+ return (ImmediateDomFragment df state, result)
564+ mountDomFragment fragment setFragment = do
565+ parent <- askParent
566+ extractFragment fragment
567+ before <- textNodeInternal ("" :: Text)
568+ appendChild_ parent $ Just $ _immediateDomFragment_document fragment
569+ after <- textNodeInternal ("" :: Text)
570+ xs <- foldDyn (\new (previous, _) -> (new, Just previous)) (fragment, Nothing) setFragment
571+ performEvent_ $ ffor (updated xs) $ \(childFragment, Just previousFragment) -> do
572+ extractFragment previousFragment
573+ extractFragment childFragment
574+ insertBefore (_immediateDomFragment_document childFragment) after
575+ liftIO $ writeIORef (_immediateDomFragment_state childFragment) $ FragmentState_Mounted (before, after)
576+ liftIO $ writeIORef (_immediateDomFragment_state fragment) $ FragmentState_Mounted (before, after)
510577
511578instance (Reflex t, MonadAdjust t m, MonadJSM m, MonadHold t m, PerformEvent t m, MonadJSM (Performable m), MonadFix m, PrimMonad m) => MonadAdjust t (ImmediateDomBuilderT t m) where
512579 runWithReplace a0 a' = do
513580 initialEnv <- ImmediateDomBuilderT ask
514581 before <- textNodeInternal ("" :: Text)
582+ let parentUnreadyChildren = _immediateDomBuilderEnv_unreadyChildren initialEnv
583+ haveEverBeenReady <- liftIO $ newIORef False
584+ let myCommitAction = do
585+ liftIO (readIORef haveEverBeenReady) >>= \case
586+ True -> return ()
587+ False -> do
588+ liftIO $ writeIORef haveEverBeenReady True
589+ old <- liftIO $ readIORef parentUnreadyChildren
590+ let new = pred old
591+ liftIO $ writeIORef parentUnreadyChildren $! new
592+ when (new == 0) $ _immediateDomBuilderEnv_commitAction initialEnv
515593 -- We draw 'after' in this roundabout way to avoid using MonadFix
516594 after <- createTextNodeUnchecked (_immediateDomBuilderEnv_document initialEnv) ("" :: Text)
517595 let drawInitialChild = do
518− result <- a0
519− append after
596+ unreadyChildren <- liftIO $ newIORef 0
597+ let f = do
598+ result <- a0
599+ append after
600+ return result
601+ result <- runImmediateDomBuilderT f $ initialEnv
602+ { _immediateDomBuilderEnv_unreadyChildren = unreadyChildren
603+ , _immediateDomBuilderEnv_commitAction = myCommitAction
604+ }
605+ liftIO $ readIORef unreadyChildren >>= \case
606+ 0 -> writeIORef haveEverBeenReady True
607+ _ -> modifyIORef' parentUnreadyChildren succ
520608 return result
521− (result0, result') <- ImmediateDomBuilderT $ lift $ runWithReplace (runReaderT (unImmediateDomBuilderT drawInitialChild) initialEnv) $ ffor a' $ \child -> do
609+ currentCohort <- liftIO $ newIORef (0 :: Int)
610+ a'' <- numberOccurrences a'
611+ (result0, result') <- lift $ runWithReplace drawInitialChild $ ffor a'' $ \(cohortId, child) -> do
522612 df <- createDocumentFragmentUnchecked $ _immediateDomBuilderEnv_document initialEnv
523− result <- runReaderT (unImmediateDomBuilderT child) $ initialEnv
613+ unreadyChildren <- liftIO $ newIORef 0
614+ let commitAction = do
615+ c <- liftIO $ readIORef currentCohort
616+ when (c < cohortId) $ do -- If a newer cohort has already been committed, just ignore this
617+ deleteBetweenExclusive before after
618+ insertBefore df after
619+ liftIO $ writeIORef currentCohort cohortId
620+ myCommitAction
621+ result <- runImmediateDomBuilderT child $ initialEnv
524622 { _immediateDomBuilderEnv_parent = toNode df
623+ , _immediateDomBuilderEnv_unreadyChildren = unreadyChildren
624+ , _immediateDomBuilderEnv_commitAction = commitAction
525625 }
526− let update = do
527− deleteBetweenExclusive before after
528− insertBefore df after
529− return (result, update)
530− requestDomAction_ $ snd <$> result'
531− return (result0, fst <$> result')
532− traverseDMapWithKeyWithAdjust (f :: forall a. k a -> v a -> ImmediateDomBuilderT t m (v' a)) (dm0 :: DMap k v) dm' = do
533− initialEnv <- ImmediateDomBuilderT ask
534− (children0, children') <- ImmediateDomBuilderT $ lift $ traverseDMapWithKeyWithAdjust (\k v -> drawChildUpdate initialEnv $ f k v) dm0 dm'
535− let result0 = DMap.map (\(Compose (_, _, v)) -> v) children0
536− placeholders0 = weakenDMapWith (\(Compose (_, ph, _)) -> ph) children0
537− result' = ffor children' $ mapPatchDMap $ \(Compose (_, _, r)) -> r
538− placeholders' = fforCheap children' $ weakenPatchDMapWith $ \(Compose (_, ph, _)) -> ph
539− placeholders <- current . incrementalToDynamic <$> holdIncremental placeholders0 placeholders'
540− _ <- DMap.traverseWithKey (\_ (Compose (df, _, _)) -> Constant () <$ append df) children0
541− lastPlaceholder <- textNodeInternal ("" :: Text)
542− requestDomAction_ $ flip push children' $ \(PatchDMap p) -> do
543− phs <- sample placeholders
544− if DMap.null p then return Nothing else return $ Just $ do
545− let g :: DSum k (ComposeMaybe (Compose ((,,) DOM.DocumentFragment DOM.Text) v')) -> JSM ()
546− g (k :=> ComposeMaybe mv) = do
547− let nextPlaceholder = maybe lastPlaceholder snd $ Map.lookupGT (Some.This k) phs
548− forM_ (Map.lookup (Some.This k) phs) $ \thisPlaceholder -> thisPlaceholder `deleteUpTo` nextPlaceholder
549− forM_ mv $ \(Compose (df, _, _)) -> df `insertBefore` nextPlaceholder
550− mapM_ g $ DMap.toList p
626+
627+ liftIO (readIORef unreadyChildren) >>= \case
628+ 0 -> liftJSM commitAction
629+ _ -> return () -- Whoever decrements it to 0 will handle it
630+ return result
551631 return (result0, result')
552− traverseDMapWithKeyWithAdjustWithMove (f :: forall a. k a -> v a -> ImmediateDomBuilderT t m (v' a)) (dm0 :: DMap k v) dm' = do
553− initialEnv <- ImmediateDomBuilderT ask
554− (children0, children') <- ImmediateDomBuilderT $ lift $ traverseDMapWithKeyWithAdjustWithMove (\k v -> drawChildUpdate initialEnv $ f k v) dm0 dm'
555− let result0 = DMap.map (\(Compose (_, _, v)) -> v) children0
556− placeholders0 = weakenDMapWith (\(Compose (_, ph, _)) -> ph) children0
557− result' = ffor children' $ mapPatchDMapWithMove $ \(Compose (_, _, r)) -> r
558− placeholders' = fforCheap children' $ weakenPatchDMapWithMoveWith $ \(Compose (_, ph, _)) -> ph
559− placeholders <- current . incrementalToDynamic <$> holdIncremental placeholders0 placeholders'
560− _ <- DMap.traverseWithKey (\_ (Compose (df, _, _)) -> Constant () <$ append df) children0
561− lastPlaceholder <- textNodeInternal ("" :: Text)
562− requestDomAction_ $ flip push children' $ \p_ -> do
632+ traverseDMapWithKeyWithAdjust = do
633+ let updateChildUnreadiness (p :: PatchDMap k (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v')) old = do
634+ let new :: forall a. k a -> ComposeMaybe (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v') a -> IO (ComposeMaybe (Constant (IORef (ChildReadyState k))) a)
635+ new k (ComposeMaybe m) = ComposeMaybe <$> case m of
636+ Nothing -> return Nothing
637+ Just (Compose (_, _, sRef, _)) -> do
638+ readIORef sRef >>= \case
639+ ChildReadyState_Ready -> return Nothing -- Delete this child, since it's ready
640+ ChildReadyState_Unready _ -> do
641+ writeIORef sRef $ ChildReadyState_Unready $ Just $ Some.This k
642+ return $ Just $ Constant sRef
643+ delete _ (Constant sRef) = do
644+ writeIORef sRef $ ChildReadyState_Unready Nothing
645+ return $ Constant ()
646+ p' <- fmap PatchDMap $ DMap.traverseWithKey new $ unPatchDMap p
647+ _ <- DMap.traverseWithKey delete $ PatchDMap.getDeletions p old
648+ return $ applyAlways p' old
649+ hoistTraverseWithKeyWithAdjust traverseDMapWithKeyWithAdjust mapPatchDMap updateChildUnreadiness $ \placeholders lastPlaceholderRef (PatchDMap p) -> do
650+ phs <- liftIO $ readIORef placeholders
651+ forM_ (DMap.toList p) $ \(k :=> ComposeMaybe mv) -> do
652+ lastPlaceholder <- liftIO $ readIORef lastPlaceholderRef
653+ let nextPlaceholder = maybe lastPlaceholder snd $ Map.lookupGT (Some.This k) phs
654+ forM_ (Map.lookup (Some.This k) phs) $ \thisPlaceholder -> thisPlaceholder `deleteUpTo` nextPlaceholder
655+ forM_ mv $ \(Compose (df, _, _, _)) -> df `insertBefore` nextPlaceholder
656+ liftIO $ writeIORef placeholders $! fromMaybe phs $ apply (weakenPatchDMapWith (\(Compose (_, ph, _, _)) -> ph) $ PatchDMap p) phs
657+ traverseDMapWithKeyWithAdjustWithMove = do
658+ let updateChildUnreadiness (p :: PatchDMapWithMove k (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v')) old = do
659+ let new :: forall a. k a -> PatchDMapWithMove.NodeInfo k (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v') a -> IO (PatchDMapWithMove.NodeInfo k (Constant (IORef (ChildReadyState k))) a)
660+ new k = PatchDMapWithMove.nodeInfoMapFromM $ \case
661+ PatchDMapWithMove.From_Insert (Compose (_, _, sRef, _)) -> do
662+ readIORef sRef >>= \case
663+ ChildReadyState_Ready -> return PatchDMapWithMove.From_Delete
664+ ChildReadyState_Unready _ -> do
665+ writeIORef sRef $ ChildReadyState_Unready $ Just $ Some.This k
666+ return $ PatchDMapWithMove.From_Insert $ Constant sRef
667+ PatchDMapWithMove.From_Delete -> return PatchDMapWithMove.From_Delete
668+ PatchDMapWithMove.From_Move fromKey -> return $ PatchDMapWithMove.From_Move fromKey
669+ deleteOrMove :: forall a. k a -> Product (Constant (IORef (ChildReadyState k))) (ComposeMaybe k) a -> IO (Constant () a)
670+ deleteOrMove _ (Pair (Constant sRef) (ComposeMaybe mToKey)) = do
671+ writeIORef sRef $ ChildReadyState_Unready $ Some.This <$> mToKey -- This will be Nothing if deleting, and Just if moving, so it works out in both cases
672+ return $ Constant ()
673+ p' <- fmap unsafePatchDMapWithMove $ DMap.traverseWithKey new $ unPatchDMapWithMove p
674+ _ <- DMap.traverseWithKey deleteOrMove $ PatchDMapWithMove.getDeletionsAndMoves p old
675+ return $ applyAlways p' old
676+ hoistTraverseWithKeyWithAdjust traverseDMapWithKeyWithAdjustWithMove mapPatchDMapWithMove updateChildUnreadiness $ \placeholders lastPlaceholderRef (p_ :: PatchDMapWithMove k (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v')) -> do
563677 let p = unPatchDMapWithMove p_
564− phsBefore <- sample placeholders
565− if DMap.null p then return Nothing else return $ Just $ do
566− let collectIfMoved :: forall a. k a -> DMapEdit k (Compose ((,,) DOM.DocumentFragment DOM.Text) v') a -> JSM (Constant (Maybe DOM.DocumentFragment) a)
567− collectIfMoved k e = do
568− let mThisPlaceholder = Map.lookup (Some.This k) phsBefore -- Will be Nothing if this element wasn't present before
569− nextPlaceholder = maybe lastPlaceholder snd $ Map.lookupGT (Some.This k) phsBefore
570− case dmapEditMoved e of
571− False -> do
572− mapM_ (`deleteUpTo` nextPlaceholder) mThisPlaceholder
573− return $ Constant Nothing
574− True -> do
575− Constant <$> mapM (`collectUpTo` nextPlaceholder) mThisPlaceholder
576− collected <- DMap.traverseWithKey collectIfMoved p
577− let phsAfter = fromMaybe phsBefore $ apply (weakenPatchDMapWithMoveWith (\(Compose (_, ph, _)) -> ph) p_) phsBefore --TODO: Don't recompute this
578− let placeFragment :: forall a. k a -> DMapEdit k (Compose ((,,) DOM.DocumentFragment DOM.Text) v') a -> JSM (Constant () a)
579− placeFragment k e = do
580− let nextPlaceholder = maybe lastPlaceholder snd $ Map.lookupGT (Some.This k) phsAfter
581− case e of
582− DMapEdit_Insert _ (Compose (df, _, _)) -> do
583− df `insertBefore` nextPlaceholder
584− DMapEdit_Delete _ -> do
585− return ()
586− DMapEdit_Move _ fromKey -> do
587− Just (Constant mdf) <- return $ DMap.lookup fromKey collected
588− mapM_ (`insertBefore` nextPlaceholder) mdf
589− return $ Constant ()
590− mapM_ (\(k :=> v) -> void $ placeFragment k v) $ DMap.toDescList p -- We need to go in reverse order here, to make sure the placeholders are in the right spot at the right time
591− return ()
592− return (result0, result')
593−
594−drawChildUpdate :: (MonadJSM m, PerformEvent t m, MonadFix m, MonadJSM (Performable m)) => ImmediateDomBuilderEnv t m -> ImmediateDomBuilderT t m (v' a) -> RequesterT t JSM Identity m (Compose ((,,) DOM.DocumentFragment DOM.Text) v' a)
595−drawChildUpdate initialEnv child = do
678+ phsBefore <- liftIO $ readIORef placeholders
679+ lastPlaceholder <- liftIO $ readIORef lastPlaceholderRef
680+ let collectIfMoved :: forall a. k a -> PatchDMapWithMove.NodeInfo k (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v') a -> JSM (Constant (Maybe DOM.DocumentFragment) a)
681+ collectIfMoved k e = do
682+ let mThisPlaceholder = Map.lookup (Some.This k) phsBefore -- Will be Nothing if this element wasn't present before
683+ nextPlaceholder = maybe lastPlaceholder snd $ Map.lookupGT (Some.This k) phsBefore
684+ case isJust $ getComposeMaybe $ PatchDMapWithMove._nodeInfo_to e of
685+ False -> do
686+ mapM_ (`deleteUpTo` nextPlaceholder) mThisPlaceholder
687+ return $ Constant Nothing
688+ True -> do
689+ Constant <$> mapM (`collectUpTo` nextPlaceholder) mThisPlaceholder
690+ collected <- DMap.traverseWithKey collectIfMoved p
691+ let !phsAfter = fromMaybe phsBefore $ apply (weakenPatchDMapWithMoveWith (\(Compose (_, ph, _, _)) -> ph) p_) phsBefore --TODO: Don't recompute this
692+ let placeFragment :: forall a. k a -> PatchDMapWithMove.NodeInfo k (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v') a -> JSM (Constant () a)
693+ placeFragment k e = do
694+ let nextPlaceholder = maybe lastPlaceholder snd $ Map.lookupGT (Some.This k) phsAfter
695+ case PatchDMapWithMove._nodeInfo_from e of
696+ PatchDMapWithMove.From_Insert (Compose (df, _, _, _)) -> do
697+ df `insertBefore` nextPlaceholder
698+ PatchDMapWithMove.From_Delete -> do
699+ return ()
700+ PatchDMapWithMove.From_Move fromKey -> do
701+ Just (Constant mdf) <- return $ DMap.lookup fromKey collected
702+ mapM_ (`insertBefore` nextPlaceholder) mdf
703+ return $ Constant ()
704+ mapM_ (\(k :=> v) -> void $ placeFragment k v) $ DMap.toDescList p -- We need to go in reverse order here, to make sure the placeholders are in the right spot at the right time
705+ liftIO $ writeIORef placeholders $! phsAfter
706+
707+data ChildReadyState k
708+ = ChildReadyState_Ready
709+ | ChildReadyState_Unready !(Maybe (Some k))
710+ deriving (Show, Read, Eq, Ord)
711+
712+hoistTraverseWithKeyWithAdjust :: forall (k :: * -> *) v v' t m p.
713+ ( PerformEvent t m
714+ , MonadAdjust t m
715+ , MonadHold t m
716+ , DMap.GCompare k
717+ , MonadIO (Performable m)
718+ , MonadIO m
719+ , MonadJSM m
720+ , PrimMonad m
721+ , MonadFix m
722+ , Patch (p k v)
723+ , PatchTarget (p k (Constant Int)) ~ DMap k (Constant Int)
724+ , Monoid (p k (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v'))
725+ , Patch (p k (Constant Int))
726+ )
727+ => (forall vv vv'.
728+ (forall a. k a -> vv a -> RequesterT t JSM Identity m (vv' a))
729+ -> DMap k vv
730+ -> Event t (p k vv)
731+ -> RequesterT t JSM Identity m (DMap k vv', Event t (p k vv'))
732+ ) -- ^ The base monad's traversal
733+ -> (forall vv vv'. (forall a. vv a -> vv' a) -> p k vv -> p k vv') -- ^ A way of mapping over the patch type
734+ -> (p k (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v') -> DMap k (Constant (IORef (ChildReadyState k))) -> IO (DMap k (Constant (IORef (ChildReadyState k))))) -- ^ Given a patch for the children DOM elements, produce a patch for the childrens' unreadiness state
735+ -> (IORef (Map.Map (Some.Some k) DOM.Text) -> IORef DOM.Text -> p k (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v') -> JSM ()) -- ^ Apply a patch to the DOM
736+ -> (forall a. k a -> v a -> ImmediateDomBuilderT t m (v' a))
737+ -> DMap k v
738+ -> Event t (p k v)
739+ -> ImmediateDomBuilderT t m (DMap k v', Event t (p k v'))
740+hoistTraverseWithKeyWithAdjust base mapPatch updateChildUnreadiness applyDomUpdate_ (f :: forall a. k a -> v a -> ImmediateDomBuilderT t m (v' a)) (dm0 :: DMap k v) dm' = do
741+ initialEnv <- ImmediateDomBuilderT ask
742+ let parentUnreadyChildren = _immediateDomBuilderEnv_unreadyChildren initialEnv
743+ pendingChange :: IORef (DMap k (Constant (IORef (ChildReadyState k))), p k (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v')) <- liftIO $ newIORef mempty
744+ haveEverBeenReady <- liftIO $ newIORef False
745+ placeholders <- liftIO $ newIORef $ error "placeholders not yet initialized"
746+ lastPlaceholderRef <- liftIO $ newIORef $ error "lastPlaceholderRef not yet initialized"
747+ let applyDomUpdate = applyDomUpdate_ placeholders lastPlaceholderRef
748+ let markSelfReady = do
749+ liftIO (readIORef haveEverBeenReady) >>= \case
750+ True -> return ()
751+ False -> do
752+ liftIO $ writeIORef haveEverBeenReady True
753+ old <- liftIO $ readIORef parentUnreadyChildren
754+ let new = pred old
755+ liftIO $ writeIORef parentUnreadyChildren $! new
756+ when (new == 0) $ _immediateDomBuilderEnv_commitAction initialEnv
757+ markChildReady :: IORef (ChildReadyState k) -> JSM ()
758+ markChildReady childReadyState = do
759+ liftIO (readIORef childReadyState) >>= \case
760+ ChildReadyState_Ready -> return ()
761+ ChildReadyState_Unready countedAt -> do
762+ liftIO $ writeIORef childReadyState ChildReadyState_Ready
763+ case countedAt of
764+ Nothing -> return ()
765+ Just (Some.This k) -> do -- This child has been counted as unready, so we need to remove it from the unready set
766+ (oldUnready, p) <- liftIO $ readIORef pendingChange
767+ when (not $ DMap.null oldUnready) $ do -- This shouldn't actually ever be null
768+ let newUnready = DMap.delete k oldUnready
769+ liftIO $ writeIORef pendingChange (newUnready, p)
770+ when (DMap.null newUnready) $ do
771+ applyDomUpdate p
772+ markSelfReady
773+ (children0, children') <- ImmediateDomBuilderT $ lift $ base (\k v -> drawChildUpdate initialEnv markChildReady $ f k v) dm0 dm'
774+ let processChild k (Compose (_, _, sRef, _)) = ComposeMaybe <$> do
775+ readIORef sRef >>= \case
776+ ChildReadyState_Ready -> return Nothing
777+ ChildReadyState_Unready _ -> do
778+ writeIORef sRef $ ChildReadyState_Unready $ Just $ Some.This k
779+ return $ Just $ Constant sRef
780+ initialUnready <- liftIO $ DMap.mapMaybeWithKey (\_ -> getComposeMaybe) <$> DMap.traverseWithKey processChild children0
781+ liftIO $ if DMap.null initialUnready
782+ then writeIORef haveEverBeenReady True
783+ else do
784+ modifyIORef' parentUnreadyChildren succ
785+ writeIORef pendingChange (initialUnready, mempty) -- The patch is always empty because it got applied implicitly when we ran the children the first time
786+ let result0 = DMap.map (\(Compose (_, _, _, v)) -> v) children0
787+ placeholders0 = weakenDMapWith (\(Compose (_, ph, _, _)) -> ph) children0
788+ result' = ffor children' $ mapPatch $ \(Compose (_, _, _, r)) -> r
789+ liftIO $ writeIORef placeholders $! placeholders0
790+ _ <- DMap.traverseWithKey (\_ (Compose (df, _, _, _)) -> Constant () <$ append df) children0
791+ liftIO . writeIORef lastPlaceholderRef =<< textNodeInternal ("" :: Text)
792+ -- Note: the numbering here must match the numbering of dm''
793+ requestDomAction_ $ ffor children' $ \p -> do
794+ (oldUnready, oldP) <- liftIO $ readIORef pendingChange
795+ newUnready <- liftIO $ updateChildUnreadiness p oldUnready
796+ let !newP = p <> oldP
797+ liftIO $ writeIORef pendingChange (newUnready, newP)
798+ when (DMap.null newUnready) $ do
799+ applyDomUpdate newP
800+ markSelfReady
801+ liftIO $ writeIORef pendingChange $! mempty
802+ return (result0, result')
803+
804+drawChildUpdate :: (MonadIO m, MonadJSM m)
805+ => ImmediateDomBuilderEnv t
806+ -> (IORef (ChildReadyState k) -> JSM ()) -- This will NOT be called if the child is ready at initialization time; instead, the ChildReadyState return value will be ChildReadyState_Ready
807+ -> ImmediateDomBuilderT t m (v' a)
808+ -> RequesterT t JSM Identity m (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v' a)
809+drawChildUpdate initialEnv markReady child = do
810+ childReadyState <- liftIO $ newIORef $ ChildReadyState_Unready Nothing
811+ unreadyChildren <- liftIO $ newIORef 0
596812 df <- createDocumentFragmentUnchecked $ _immediateDomBuilderEnv_document initialEnv
597− runReaderT (unImmediateDomBuilderT $ Compose <$> ((,,) <$> pure df <*> textNodeInternal ("" :: Text) <*> child)) $ initialEnv
813+ (placeholder, result) <- runReaderT (unImmediateDomBuilderT $ (,) <$> textNodeInternal ("" :: Text) <*> child) $ initialEnv
598814 { _immediateDomBuilderEnv_parent = toNode df
815+ , _immediateDomBuilderEnv_unreadyChildren = unreadyChildren
816+ , _immediateDomBuilderEnv_commitAction = markReady childReadyState
599817 }
818+ u <- liftIO $ readIORef unreadyChildren
819+ when (u == 0) $ liftIO $ writeIORef childReadyState ChildReadyState_Ready
820+ return $ Compose (df, placeholder, childReadyState, result)
600821
601822mkHasFocus :: (MonadHold t m, Reflex t) => Element er d t -> m (Dynamic t Bool)
602823mkHasFocus e = do
reflex-dom-core/src/Reflex/Dom/Builder/Static.hs+44−16
@@ -150,21 +150,43 @@ instance (Reflex t, MonadAdjust t m, MonadHold t m) => MonadAdjust t (StaticDomB
150150 o <- hold (snd result0) $ fmapCheap snd result'
151151 StaticDomBuilderT $ modify $ (:) $ join o
152152 return (fst result0, fmapCheap fst result')
153− traverseDMapWithKeyWithAdjust f (dm0 :: DMap k v) dm' = do
154− e <- StaticDomBuilderT ask
155− (children0, children') <- lift $ traverseDMapWithKeyWithAdjust (\k v -> fmap (Compose . swap) (runStaticDomBuilderT (f k v) e)) dm0 dm'
156− let result0 = DMap.map (snd . getCompose) children0
157− result' = ffor children' $ mapPatchDMap $ snd . getCompose
158− outputs0 :: DMap k (Constant (Behavior t Builder))
159− outputs0 = DMap.map (Constant . fst . getCompose) children0
160− outputs' :: Event t (PatchDMap k (Constant (Behavior t Builder)))
161− outputs' = ffor children' $ mapPatchDMap $ Constant . fst . getCompose
162− outputs <- holdIncremental outputs0 outputs'
163− StaticDomBuilderT $ modify $ (:) $ pull $ do
164− os <- sample $ currentIncremental outputs
165− fmap mconcat $ forM (DMap.toList os) $ \(_ :=> Constant o) -> do
166− sample o
167− return (result0, result')
153+ traverseDMapWithKeyWithAdjust = hoistDMapWithKeyWithAdjust traverseDMapWithKeyWithAdjust mapPatchDMap
154+ traverseDMapWithKeyWithAdjustWithMove = hoistDMapWithKeyWithAdjust traverseDMapWithKeyWithAdjustWithMove mapPatchDMapWithMove
155+
156+hoistDMapWithKeyWithAdjust :: forall (k :: * -> *) v v' t m p.
157+ ( MonadAdjust t m
158+ , MonadHold t m
159+ , DMap.GCompare k
160+ , Patch (p k v)
161+ , PatchTarget (p k (Constant (Behavior t Builder))) ~ DMap k (Constant (Behavior t Builder))
162+ , Patch (p k (Constant (Behavior t Builder)))
163+ )
164+ => (forall vv vv'.
165+ (forall a. k a -> vv a -> m (vv' a))
166+ -> DMap k vv
167+ -> Event t (p k vv)
168+ -> m (DMap k vv', Event t (p k vv'))
169+ ) -- ^ The base monad's traversal
170+ -> (forall vv vv'. (forall a. vv a -> vv' a) -> p k vv -> p k vv') -- ^ A way of mapping over the patch type
171+ -> (forall a. k a -> v a -> StaticDomBuilderT t m (v' a))
172+ -> DMap k v
173+ -> Event t (p k v)
174+ -> StaticDomBuilderT t m (DMap k v', Event t (p k v'))
175+hoistDMapWithKeyWithAdjust base mapPatch f dm0 dm' = do
176+ e <- StaticDomBuilderT ask
177+ (children0, children') <- lift $ base (\k v -> fmap (Compose . swap) (runStaticDomBuilderT (f k v) e)) dm0 dm'
178+ let result0 = DMap.map (snd . getCompose) children0
179+ result' = ffor children' $ mapPatch $ snd . getCompose
180+ outputs0 :: DMap k (Constant (Behavior t Builder))
181+ outputs0 = DMap.map (Constant . fst . getCompose) children0
182+ outputs' :: Event t (p k (Constant (Behavior t Builder)))
183+ outputs' = ffor children' $ mapPatch $ Constant . fst . getCompose
184+ outputs <- holdIncremental outputs0 outputs'
185+ StaticDomBuilderT $ modify $ (:) $ pull $ do
186+ os <- sample $ currentIncremental outputs
187+ fmap mconcat $ forM (DMap.toList os) $ \(_ :=> Constant o) -> do
188+ sample o
189+ return (result0, result')
168190
169191-- TODO: the uses of illegal lenses in this instance causes it to be somewhat less efficient than it can be. replacing them with explicit cases to get the underlying Maybe Event and working with those is ideal.
170192instance SupportsStaticDomBuilder t m => DomBuilder t (StaticDomBuilderT t m) where
@@ -204,7 +226,11 @@ instance SupportsStaticDomBuilder t m => DomBuilder t (StaticDomBuilderT t m) wh
204226 let open = mconcat [constant ("<" <> byteString tagBS), attrs2, constant (byteString ">")]
205227 let close = constant $ byteString $ "</" <> tagBS <> ">"
206228 modify $ (:) $ mconcat [open, innerHtml, close]
207− return (Element es (), result)
229+ let e = Element
230+ { _element_events = es
231+ , _element_raw = ()
232+ }
233+ return (e, result)
208234 {-# INLINABLE inputElement #-}
209235 inputElement cfg = do
210236 (e, _result) <- element "input" (cfg ^. inputElementConfig_elementConfig) $ return ()
@@ -250,6 +276,8 @@ instance SupportsStaticDomBuilder t m => DomBuilder t (StaticDomBuilderT t m) wh
250276 return (wrapped, result)
251277 placeRawElement () = return ()
252278 wrapRawElement () _ = return $ Element (EventSelector $ const never) ()
279+ notReadyUntil _ = return () --TODO: Do we need to support this somehow?
280+ notReady = return () --TODO: Do we need to support this somehow?
253281
254282--TODO: Make this more abstract --TODO: Put the WithWebView underneath PerformEventT - I think this would perform better
255283type StaticWidget x = PostBuildT Spider (StaticDomBuilderT Spider (PerformEventT Spider (SpiderHost Global)))
reflex-dom-core/src/Reflex/Dom/Main.hs+6
@@ -90,10 +90,13 @@ mainWidgetWithHead' widgets = withJSContextSingletonMono $ \jsSing -> do
9090 (postBuild, postBuildTriggerRef) <- newEventWithTriggerRef
9191 let go :: forall c. Widget () c -> DOM.DocumentFragment -> PerformEventT Spider (SpiderHost Global) c
9292 go w df = do
93+ unreadyChildren <- liftIO $ newIORef 0
9394 let builderEnv = ImmediateDomBuilderEnv
9495 { _immediateDomBuilderEnv_document = toDocument doc
9596 , _immediateDomBuilderEnv_parent = toNode df
9697 , _immediateDomBuilderEnv_events = events
98+ , _immediateDomBuilderEnv_unreadyChildren = unreadyChildren
99+ , _immediateDomBuilderEnv_commitAction = return () --TODO
97100 }
98101 runWithJSContextSingleton (runImmediateDomBuilderT (runPostBuildT w postBuild) builderEnv) jsSing
99102 rec b <- go (headWidget a) headFragment
@@ -116,10 +119,13 @@ attachWidget' rootElement jsSing w = do
116119 df <- createDocumentFragmentUnchecked doc
117120 ((a, events), fc) <- liftIO . attachWidget'' $ \events -> do
118121 (postBuild, postBuildTriggerRef) <- newEventWithTriggerRef
122+ unreadyChildren <- liftIO $ newIORef 0
119123 let builderEnv = ImmediateDomBuilderEnv
120124 { _immediateDomBuilderEnv_document = toDocument doc
121125 , _immediateDomBuilderEnv_parent = toNode df
122126 , _immediateDomBuilderEnv_events = events
127+ , _immediateDomBuilderEnv_unreadyChildren = unreadyChildren
128+ , _immediateDomBuilderEnv_commitAction = return () --TODO
123129 }
124130 a <- runWithJSContextSingleton (runImmediateDomBuilderT (runPostBuildT w postBuild) builderEnv) jsSing
125131 return ((a, events), postBuildTriggerRef)
reflex-dom-core/src/Reflex/Dom/Widget/Basic.hs+7−3
@@ -144,6 +144,7 @@ dynText t = do
144144 [ updated t
145145 , tag (current t) postBuild
146146 ]
147+ notReadyUntil postBuild
147148
148149display :: (PostBuild t m, DomBuilder t m, Show a) => Dynamic t a -> m ()
149150display = dynText . fmap (T.pack . show)
@@ -161,7 +162,7 @@ dyn :: (DomBuilder t m, PostBuild t m) => Dynamic t (m a) -> m (Event t a)
161162dyn child = do
162163 postBuild <- getPostBuild
163164 let newChild = leftmost [updated child, tagCheap (current child) postBuild]
164− snd <$> widgetHoldInternal (return ()) newChild
165+ snd <$> widgetHoldInternal notReady newChild
165166
166167-- | Given an initial widget and an Event of widget-creating actions, create a widget that is recreated whenever the Event fires.
167168-- The returned Dynamic of widget results occurs when the Event does.
@@ -341,8 +342,11 @@ elDynAttrNS' mns elementTag attrs child = do
341342 modifyAttrs <- dynamicAttributesToModifyAttributes attrs
342343 let cfg = def
343344 & elementConfig_namespace .~ mns
344− & modifyAttributes .~ fmap mapKeysToAttributeName modifyAttrs
345− element elementTag cfg child
345+ & modifyAttributes .~ fmapCheap mapKeysToAttributeName modifyAttrs
346+ result <- element elementTag cfg child
347+ postBuild <- getPostBuild
348+ notReadyUntil postBuild
349+ return result
346350
347351dynamicAttributesToModifyAttributes :: (Ord k, PostBuild t m) => Dynamic t (Map k Text) -> m (Event t (Map k (Maybe Text)))
348352dynamicAttributesToModifyAttributes = dynamicAttributesToModifyAttributesWithInitial mempty
test/prebuild.hsnew file+107
@@ -0,0 +1,107 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE ScopedTypeVariables #-}
4+{-# LANGUAGE TypeFamilies #-}
5+{-# LANGUAGE LambdaCase #-}
6+{-# LANGUAGE FlexibleInstances #-}
7+{-# LANGUAGE MultiParamTypeClasses #-}
8+import Control.Monad
9+import Control.Monad.Trans
10+import Reflex.Dom
11+import qualified Data.Text as T
12+import Control.Concurrent
13+
14+import Control.Monad.State.Strict
15+import Data.Functor.Misc
16+import Data.Monoid
17+import Data.Word
18+import qualified Data.Map as Map
19+import qualified Data.Dependent.Map as DMap
20+import Data.Dependent.Sum
21+import qualified Reflex.Patch.DMapWithMove as PatchDMapWithMove
22+
23+main :: IO ()
24+main = mainWidget w
25+
26+w :: forall t m. (MonadWidget t m, MountableDomBuilder t m) => m ()
27+w = do
28+ let slow :: forall m'. MonadWidget t m' => m' ()
29+ {-
30+ performEventChain = do
31+ postBuild <- delay 0 =<< getPostBuild
32+ rec let maxN = 10000
33+ n = leftmost [0 <$ postBuild, ffilter (<=maxN) $ succ <$> n']
34+ n' <- performEvent $ return <$> n
35+ pausedUntil $ ffilter (==maxN) n'
36+ _ <- widgetHold (text "Starting") $ text . T.pack . show <$> n
37+ return ()
38+ -}
39+ {- Many dyns
40+ slow = elAttr "div" ("style" =: "position:relative;width:256px;height:256px") $ go maxDepth
41+ where maxDepth = 6 :: Int
42+ go 0 = blank
43+ go n = void $ dyn $ pure $ do
44+ let bgcolor = "rgba(0,0,0," <> T.pack (show (1 - (fromIntegral n / fromIntegral maxDepth) :: Double)) <> ")"
45+ s pos = pos <> ";position:absolute;border:1px solid white;background-color:" <> bgcolor
46+ elAttr "div" ("style" =: s "left:0;right:50%;top:0;bottom:50%") $ go $ pred n
47+ elAttr "div" ("style" =: s "left:50%;right:0;top:0;bottom:50%") $ go $ pred n
48+ elAttr "div" ("style" =: s "left:50%;right:0;top:50%;bottom:0") $ go $ pred n
49+ elAttr "div" ("style" =: s "left:0;right:50%;top:50%;bottom:0") $ go $ pred n
50+ -}
51+ {- Many elDynAttrs
52+ slow = do
53+ let size = 64
54+ replicateM_ size $ elAttr "div" ("style" =: ("height:4px;width:" <> T.pack (show (size*4)) <> "px;line-height:0;background-color:gray")) $ do
55+ replicateM_ size $ elDynAttr "div" (pure $ "style" =: "display:inline-block;width:4px;height:4px;background-color:black") blank
56+ -}
57+ {- Many dynTexts
58+ slow = el "table" $ do
59+ let size = 64
60+ replicateM_ size $ el "tr" $ do
61+ replicateM_ size $ el "td" $ do
62+ dynText $ pure "."
63+ -}
64+ {- Many simultaneous performEvents
65+ slow = do
66+ postBuild <- getPostBuild
67+ replicateM_ ((2 :: Int) ^ (14 :: Int)) $ performEvent_ $ return () <$ postBuild
68+ done <- performEvent $ return () <$ postBuild
69+ _ <- widgetHold (text "Doing performEvent") $ text "Done" <$ done
70+ return ()
71+ -}
72+ {-
73+ slow = do
74+ postBuild <- getPostBuild
75+ postBuild' <- performEvent . (return () <$) =<< performEvent . (return () <$) =<< getPostBuild
76+ let f x = Map.fromList [(n, x) | n <- [1 :: Int .. 4096]]
77+ listHoldWithKey (f False) (f (Just True) <$ postBuild') $ \k -> \case
78+ False -> do
79+ text "X "
80+ notReadyUntil =<< getPostBuild
81+ True -> do
82+ text ". "
83+ return ()
84+ -}
85+ slow = do
86+ let f :: forall a. EitherTag () () a -> Const2 () () a -> m' (Const2 () () a)
87+ f _ (Const2 ()) = do
88+ notReadyUntil =<< delay 0.5 =<< getPostBuild
89+ text "Done"
90+ return $ Const2 ()
91+ postBuild <- getPostBuild
92+ traverseDMapWithKeyWithAdjustWithMove f (DMap.singleton LeftTag $ Const2 ()) $ (PatchDMapWithMove.moveDMapKey LeftTag RightTag) <$ postBuild
93+ return ()
94+ el "div" $ do
95+ draw <- button "Draw"
96+ widgetHold blank $ ffor draw $ \_ -> do
97+ postBuild <- getPostBuild
98+ widgetHold (text "Loading...") $ slow <$ postBuild
99+ return ()
100+ return ()
101+
102+#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
103+instance {-# INCOHERENT #-} (Show (f a), Show (f b)) => ShowTag (EitherTag a b) f where
104+ showTagToShow e _ = case e of
105+ LeftTag -> id
106+ RightTag -> id
107+#endif