Skip to content

Commit 5266610

Browse files
committed
Add captureImages and avoid nested runImageWriter for scrollable elements
1 parent f492633 commit 5266610

File tree

6 files changed

+97
-17
lines changed

6 files changed

+97
-17
lines changed

src-bin/example.hs

+26
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ data Example = Example_TextEditor
4141
| Example_ScrollableTextDisplay
4242
| Example_ClickButtonsGetEmojis
4343
| Example_CPUStat
44+
| Example_Scrollable
4445
deriving (Show, Read, Eq, Ord, Enum, Bounded)
4546

4647
withCtrlC :: (Monad m, HasInput t m, Reflex t) => m () -> m (Event t ())
@@ -75,12 +76,14 @@ main = mainWidget $ withCtrlC $ do
7576
c <- t $ textButtonStatic def "Scrollable text display"
7677
d <- t $ textButtonStatic def "Clickable buttons"
7778
e <- t $ textButtonStatic def "CPU Usage"
79+
f <- t $ textButtonStatic def "Scrollable"
7880
return $ leftmost
7981
[ Left Example_Todo <$ a
8082
, Left Example_TextEditor <$ b
8183
, Left Example_ScrollableTextDisplay <$ c
8284
, Left Example_ClickButtonsGetEmojis <$ d
8385
, Left Example_CPUStat <$ e
86+
, Left Example_Scrollable <$ f
8487
]
8588
let escapable w = do
8689
void w
@@ -94,9 +97,32 @@ main = mainWidget $ withCtrlC $ do
9497
Left Example_ScrollableTextDisplay -> escapable scrolling
9598
Left Example_ClickButtonsGetEmojis -> escapable easyExample
9699
Left Example_CPUStat -> escapable cpuStats
100+
Left Example_Scrollable -> escapable scrollingWithLayout
97101
Right () -> buttons
98102
return ()
99103

104+
scrollingWithLayout
105+
:: forall t m.
106+
( VtyExample t m
107+
, HasInput t m
108+
, MonadHold t m
109+
, Manager t m
110+
, PostBuild t m
111+
, MonadIO (Performable m)
112+
, TriggerEvent t m
113+
, PerformEvent t m
114+
) => m ()
115+
scrollingWithLayout = col $ do
116+
scrollable def $ do
117+
result <- boxTitle (constant def) (constant "Tracks") $ do
118+
col $ forM [0..10] $ \n -> do
119+
grout (fixed 1) $ do
120+
textButtonStatic def $ T.pack (show n)
121+
pure n
122+
pure $ (never, result)
123+
pure ()
124+
125+
100126
-- * Mouse button and emojis example
101127
easyExample :: (VtyExample t m, Manager t m, MonadHold t m) => m (Event t ())
102128
easyExample = do

src/Control/Monad/NodeId.hs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Description: Monad providing a supply of unique identifiers
66
module Control.Monad.NodeId
77
( NodeId
88
, MonadNodeId (..)
9-
, NodeIdT
9+
, NodeIdT (..)
1010
, runNodeIdT
1111
) where
1212

src/Reflex/Vty/Widget.hs

+56-12
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,23 @@
22
Module: Reflex.Vty.Widget
33
Description: Basic set of widgets and building blocks for reflex-vty applications
44
-}
5+
{-# Language ScopedTypeVariables #-}
56
{-# Language UndecidableInstances #-}
7+
{-# Language PolyKinds #-}
8+
{-# Language RankNTypes #-}
69

710
module Reflex.Vty.Widget where
811

912
import Control.Applicative (liftA2)
10-
import Control.Monad.Catch (MonadCatch, MonadThrow, MonadMask)
13+
import Control.Monad.Catch (MonadCatch, MonadMask, MonadThrow)
1114
import Control.Monad.Fix (MonadFix)
1215
import Control.Monad.IO.Class (MonadIO)
1316
import Control.Monad.Morph (MFunctor(..))
1417
import Control.Monad.NodeId
15-
import Control.Monad.Reader (ReaderT, ask, local, runReaderT)
18+
import Control.Monad.Reader (ReaderT(..), ask, local, runReaderT)
1619
import Control.Monad.Ref
1720
import Control.Monad.Trans (MonadTrans, lift)
21+
import Control.Monad.Trans.State.Strict
1822
import Data.Set (Set)
1923
import qualified Data.Set as Set
2024
import Graphics.Vty (Image)
@@ -129,7 +133,10 @@ deriving instance NotReady t m => NotReady t (Input t m)
129133
deriving instance PerformEvent t m => PerformEvent t (Input t m)
130134
deriving instance PostBuild t m => PostBuild t (Input t m)
131135
deriving instance TriggerEvent t m => TriggerEvent t (Input t m)
132-
instance HasImageWriter t m => HasImageWriter t (Input t m)
136+
instance HasImageWriter t m => HasImageWriter t (Input t m) where
137+
captureImages x = do
138+
a <- input
139+
lift $ captureImages $ runInput a x
133140
instance HasDisplayRegion t m => HasDisplayRegion t (Input t m)
134141
instance HasFocusReader t m => HasFocusReader t (Input t m)
135142

@@ -354,7 +361,10 @@ deriving instance NotReady t m => NotReady t (DisplayRegion t m)
354361
deriving instance PerformEvent t m => PerformEvent t (DisplayRegion t m)
355362
deriving instance PostBuild t m => PostBuild t (DisplayRegion t m)
356363
deriving instance TriggerEvent t m => TriggerEvent t (DisplayRegion t m)
357-
instance HasImageWriter t m => HasImageWriter t (DisplayRegion t m)
364+
instance HasImageWriter t m => HasImageWriter t (DisplayRegion t m) where
365+
captureImages x = do
366+
reg <- askRegion
367+
lift $ captureImages $ runDisplayRegion reg x
358368
instance HasFocusReader t m => HasFocusReader t (DisplayRegion t m)
359369

360370
instance (Adjustable t m, MonadFix m, MonadHold t m) => Adjustable t (DisplayRegion t m) where
@@ -422,7 +432,10 @@ deriving instance NotReady t m => NotReady t (FocusReader t m)
422432
deriving instance PerformEvent t m => PerformEvent t (FocusReader t m)
423433
deriving instance PostBuild t m => PostBuild t (FocusReader t m)
424434
deriving instance TriggerEvent t m => TriggerEvent t (FocusReader t m)
425-
instance HasImageWriter t m => HasImageWriter t (FocusReader t m)
435+
instance HasImageWriter t m => HasImageWriter t (FocusReader t m) where
436+
captureImages x = do
437+
a <- focus
438+
lift $ captureImages $ runFocusReader a x
426439

427440
instance (Adjustable t m, MonadFix m, MonadHold t m) => Adjustable t (FocusReader t m) where
428441
runWithReplace (FocusReader a) e = FocusReader $ runWithReplace a $ fmap unFocusReader e
@@ -449,7 +462,7 @@ runFocusReader b = flip runReaderT b . unFocusReader
449462
-- * "Image" output
450463

451464
-- | A class for widgets that can produce images to draw to the display
452-
class (Reflex t, Monad m) => HasImageWriter t m | m -> t where
465+
class (Reflex t, Monad m) => HasImageWriter (t :: *) m | m -> t where
453466
-- | Send images upstream for rendering
454467
tellImages :: Behavior t [Image] -> m ()
455468
default tellImages :: (f m' ~ m, Monad m', MonadTrans f, HasImageWriter t m') => Behavior t [Image] -> m ()
@@ -458,6 +471,8 @@ class (Reflex t, Monad m) => HasImageWriter t m | m -> t where
458471
mapImages :: (Behavior t [Image] -> Behavior t [Image]) -> m a -> m a
459472
default mapImages :: (f m' ~ m, Monad m', MFunctor f, HasImageWriter t m') => (Behavior t [Image] -> Behavior t [Image]) -> m a -> m a
460473
mapImages f = hoist (mapImages f)
474+
-- | Capture images, preventing them from being drawn
475+
captureImages :: m a -> m (a, Behavior t [Image])
461476

462477
-- | A widget that can produce images to draw onto the display
463478
newtype ImageWriter t m a = ImageWriter
@@ -493,18 +508,44 @@ instance (Adjustable t m, MonadFix m, MonadHold t m) => Adjustable t (ImageWrite
493508
traverseDMapWithKeyWithAdjust f m e = ImageWriter $ traverseDMapWithKeyWithAdjust (\k v -> unImageWriter $ f k v) m e
494509
traverseDMapWithKeyWithAdjustWithMove f m e = ImageWriter $ traverseDMapWithKeyWithAdjustWithMove (\k v -> unImageWriter $ f k v) m e
495510

496-
instance HasImageWriter t m => HasImageWriter t (ReaderT x m)
497-
instance HasImageWriter t m => HasImageWriter t (BehaviorWriterT t x m)
498-
instance HasImageWriter t m => HasImageWriter t (DynamicWriterT t x m)
499-
instance HasImageWriter t m => HasImageWriter t (EventWriterT t x m)
500-
instance HasImageWriter t m => HasImageWriter t (NodeIdT m)
511+
instance HasImageWriter t m => HasImageWriter t (ReaderT x m) where
512+
captureImages x = do
513+
a <- ask
514+
lift $ captureImages $ runReaderT x a
515+
instance HasImageWriter t m => HasImageWriter t (BehaviorWriterT t x m) where
516+
captureImages (BehaviorWriterT x) = BehaviorWriterT $ do
517+
s <- get
518+
((result, s'), images) <- lift $ captureImages $ runStateT x s
519+
put s'
520+
return (result, images)
521+
instance HasImageWriter t m => HasImageWriter t (DynamicWriterT t x m) where
522+
captureImages (DynamicWriterT x) = DynamicWriterT $ do
523+
s <- get
524+
((result, s'), images) <- lift $ captureImages $ runStateT x s
525+
put s'
526+
return (result, images)
527+
528+
instance HasImageWriter t m => HasImageWriter t (EventWriterT t x m) where
529+
captureImages (EventWriterT x) = EventWriterT $ do
530+
s <- get
531+
((result, s'), images) <- lift $ captureImages $ runStateT x s
532+
put s'
533+
return (result, images)
534+
535+
instance HasImageWriter t m => HasImageWriter t (NodeIdT m) where
536+
captureImages x = NodeIdT $ do
537+
ref <- ask
538+
lift $ captureImages $ flip runReaderT ref . unNodeIdT $ x
501539

502540
instance (Monad m, Reflex t) => HasImageWriter t (ImageWriter t m) where
503541
tellImages = ImageWriter . tellBehavior
504542
mapImages f (ImageWriter x) = ImageWriter $ do
505543
(a, images) <- lift $ runBehaviorWriterT x
506544
tellBehavior $ f images
507545
pure a
546+
captureImages (ImageWriter x) = ImageWriter $ do
547+
lift $ runBehaviorWriterT x
548+
508549

509550
instance HasDisplayRegion t m => HasDisplayRegion t (ImageWriter t m)
510551
instance HasFocusReader t m => HasFocusReader t (ImageWriter t m)
@@ -563,7 +604,10 @@ deriving instance NotReady t m => NotReady t (ThemeReader t m)
563604
deriving instance PerformEvent t m => PerformEvent t (ThemeReader t m)
564605
deriving instance PostBuild t m => PostBuild t (ThemeReader t m)
565606
deriving instance TriggerEvent t m => TriggerEvent t (ThemeReader t m)
566-
instance HasImageWriter t m => HasImageWriter t (ThemeReader t m)
607+
instance HasImageWriter t m => HasImageWriter t (ThemeReader t m) where
608+
captureImages x = ThemeReader $ do
609+
a <- ask
610+
lift $ captureImages $ flip runReaderT a $ unThemeReader x
567611

568612
instance (Adjustable t m, MonadFix m, MonadHold t m) => Adjustable t (ThemeReader t m) where
569613
runWithReplace (ThemeReader a) e = ThemeReader $ runWithReplace a $ fmap unThemeReader e

src/Reflex/Vty/Widget/Layout.hs

+9
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ instance (Reflex t, MonadFix m, HasInput t m) => HasInput t (Focus t m) where
135135

136136
instance (HasImageWriter t m, MonadFix m) => HasImageWriter t (Focus t m) where
137137
mapImages f = hoist (mapImages f)
138+
captureImages (Focus x) = Focus $ do
139+
((a, fs), images) <- lift $ captureImages $ runDynamicWriterT x
140+
tellDyn fs
141+
return (a, images)
138142

139143
instance (HasFocusReader t m, Monad m) => HasFocusReader t (Focus t m)
140144

@@ -437,6 +441,11 @@ instance (HasInput t m, HasDisplayRegion t m, MonadFix m, Reflex t) => HasInput
437441

438442
instance (HasDisplayRegion t m, HasImageWriter t m, MonadFix m) => HasImageWriter t (Layout t m) where
439443
mapImages f = hoistRunLayout (mapImages f)
444+
captureImages (Layout x) = Layout $ do
445+
y <- ask
446+
((a, w), images) <- lift $ lift $ captureImages $ flip runReaderT y $ runDynamicWriterT x
447+
tellDyn w
448+
pure (a, images)
440449

441450
instance (HasFocusReader t m, Monad m) => HasFocusReader t (Layout t m)
442451

src/Reflex/Vty/Widget/Scroll.hs

+3-2
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,11 @@ scrollable
5151
( Reflex t, MonadHold t m, MonadFix m
5252
, HasDisplayRegion t m, HasInput t m, HasImageWriter t m, HasTheme t m)
5353
=> ScrollableConfig t
54-
-> (m (Behavior t V.Image, Event t (), a))
54+
-> (m (Event t (), a))
5555
-> m (Scrollable t, a)
5656
scrollable (ScrollableConfig scrollBy scrollTo startingPos onAppend) mkImg = do
57-
(img, update, a) <- mkImg
57+
((update, a), imgs) <- captureImages mkImg
58+
let img = V.vertCat <$> imgs
5859
let sz = V.imageHeight <$> img
5960
kup <- key V.KUp
6061
kdown <- key V.KDown

src/Reflex/Vty/Widget/Text.hs

+2-2
Original file line numberDiff line numberDiff line change
@@ -80,5 +80,5 @@ scrollableText
8080
-> Dynamic t Text
8181
-> m (Scrollable t)
8282
scrollableText cfg t = fmap fst $ scrollable cfg $ do
83-
((), images) <- runImageWriter $ text (current t)
84-
pure $ (V.vertCat <$> images, () <$ updated t, ())
83+
text (current t)
84+
pure (() <$ updated t, ())

0 commit comments

Comments
 (0)