@@ -47,6 +47,7 @@ module Reflex.Vty.Widget
4747 , doubleBoxStyle
4848 , fill
4949 , hRule
50+ , filterKeys
5051 , KeyCombo
5152 , key
5253 , keys
@@ -82,12 +83,6 @@ data VtyWidgetCtx t = VtyWidgetCtx
8283 -- ^ The height of the region allocated to the widget.
8384 , _vtyWidgetCtx_focus :: Dynamic t Bool
8485 -- ^ Whether the widget should behave as if it has focus for keyboard input.
85− , _vtyWidgetCtx_input :: Event t VtyEvent
86− -- ^ User input events that the widget's parent chooses to share. These will generally
87− -- be filtered for relevance:
88− -- * Keyboard inputs are restricted to focused widgets
89− -- * Mouse inputs are restricted to the region in which the widget resides and are
90− -- translated into its internal coordinates.
9186 }
9287
9388-- | The output of a 'VtyWidget'
@@ -106,7 +101,9 @@ instance (Adjustable t m, MonadHold t m, Reflex t) => Adjustable t (VtyWidget t
106101
107102-- | A widget that can read its context and produce image output
108103newtype VtyWidget t m a = VtyWidget
109− { unVtyWidget :: BehaviorWriterT t [Image] (ReaderT (VtyWidgetCtx t) m) a
104+ { unVtyWidget :: BehaviorWriterT t [Image]
105+ (ReaderT (VtyWidgetCtx t)
106+ (ReaderT (Event t VtyEvent) m)) a
110107 } deriving
111108 ( Functor
112109 , Applicative
@@ -124,7 +121,7 @@ newtype VtyWidget t m a = VtyWidget
124121
125122deriving instance PerformEvent t m => PerformEvent t (VtyWidget t m)
126123instance MonadTrans (VtyWidget t) where
127− lift f = VtyWidget $ lift $ lift f
124+ lift f = VtyWidget $ lift $ lift $ lift f
128125
129126instance MonadNodeId m => MonadNodeId (VtyWidget t m) where
130127 getNextNodeId = VtyWidget $ do
@@ -147,10 +144,11 @@ instance (Monad m, Reflex t) => HasVtyWidgetCtx t (VtyWidget t m) where
147144-- | Runs a 'VtyWidget' with a given context
148145runVtyWidget
149146 :: (Reflex t, MonadNodeId m)
150− => VtyWidgetCtx t
147+ => Event t VtyEvent
148+ -> VtyWidgetCtx t
151149 -> VtyWidget t m a
152150 -> m (a, Behavior t [Image])
153−runVtyWidget ctx w = runReaderT (runBehaviorWriterT (unVtyWidget w)) ctx
151+runVtyWidget e ctx w = runReaderT (runReaderT (runBehaviorWriterT (unVtyWidget w)) ctx) e
154152
155153-- | Sets up the top-level context for a 'VtyWidget' and runs it with that context
156154mainWidgetWithHandle
@@ -168,10 +166,9 @@ mainWidgetWithHandle vty child =
168166 let ctx = VtyWidgetCtx
169167 { _vtyWidgetCtx_width = fmap fst size
170168 , _vtyWidgetCtx_height = fmap snd size
171− , _vtyWidgetCtx_input = inp'
172169 , _vtyWidgetCtx_focus = constDyn True
173170 }
174− (shutdown, images) <- runNodeIdT $ runVtyWidget ctx $ do
171+ (shutdown, images) <- runNodeIdT $ runVtyWidget inp' ctx $ do
175172 tellImages . ffor (current size) $ \(w, h) -> [V.charFill V.defAttr ' ' w h]
176173 child
177174 return $ VtyResult
@@ -206,7 +203,6 @@ instance HasDisplaySize t m => HasDisplaySize t (ReaderT x m)
206203instance HasDisplaySize t m => HasDisplaySize t (BehaviorWriterT t x m)
207204instance HasDisplaySize t m => HasDisplaySize t (DynamicWriterT t x m)
208205instance HasDisplaySize t m => HasDisplaySize t (EventWriterT t x m)
209−
210206instance HasDisplaySize t m => HasDisplaySize t (NodeIdT m)
211207
212208-- | A class for things that can receive vty events as input
@@ -214,9 +210,23 @@ class HasVtyInput t m | m -> t where
214210 input :: m (Event t VtyEvent)
215211 default input :: (f m' ~ m, Monad m', MonadTrans f, HasVtyInput t m') => m (Event t VtyEvent)
216212 input = lift input
213+ -- | User input events that the widget's parent chooses to share. These will generally
214+ -- be filtered for relevance.
215+ localInput :: (Event t VtyEvent -> Event t VtyEvent) -> m a -> m a
216+
217+-- | Filter the keyboard input that a child widget may receive
218+filterKeys :: (Reflex t, HasVtyInput t m) => (KeyCombo -> Bool) -> m a -> m a
219+filterKeys f x = localInput (ffilter (\case
220+ V.EvKey k mods -> f (k, mods)
221+ _ -> True)) x
217222
218223instance (Reflex t, Monad m) => HasVtyInput t (VtyWidget t m) where
219− input = VtyWidget . lift $ asks _vtyWidgetCtx_input
224+ input = VtyWidget . lift $ lift $ ask
225+ localInput f (VtyWidget m) = VtyWidget $ do
226+ ctx <- ask
227+ (a, images) <- lift $ lift $ local f $ runReaderT (runBehaviorWriterT m) ctx
228+ tellImages images
229+ pure a
220230
221231-- | A class for things that can dynamically gain and lose focus
222232class HasFocus t m | m -> t where
@@ -273,7 +283,7 @@ withinImage (Region left top width height)
273283-- * mouse inputs inside the region have their coordinates translated such
274284-- that (0,0) is the top-left corner of the region
275285pane
276− :: (Reflex t, Monad m, MonadNodeId m, HasVtyWidgetCtx t m)
286+ :: (Reflex t, Monad m, MonadNodeId m, HasVtyWidgetCtx t m, HasVtyInput t m)
277287 => Dynamic t Region
278288 -> Dynamic t Bool -- ^ Whether the widget should be focused when the parent is.
279289 -> m a
@@ -281,16 +291,15 @@ pane
281291pane dr foc child = do
282292 let reg = current dr
283293 let subContext ctx = VtyWidgetCtx
284− { _vtyWidgetCtx_input = fmapMaybe id $
285− attachWith (\(r,f) e -> filterInput r f e)
286− (liftA2 (,) reg (current foc))
287− (_vtyWidgetCtx_input ctx)
288− , _vtyWidgetCtx_focus = liftA2 (&&) (_vtyWidgetCtx_focus ctx) foc
294+ { _vtyWidgetCtx_focus = liftA2 (&&) (_vtyWidgetCtx_focus ctx) foc
289295 , _vtyWidgetCtx_width = _region_width <$> dr
290296 , _vtyWidgetCtx_height = _region_height <$> dr
291297 }
298+ inputFilter = fmapMaybe id .
299+ attachWith (\(r,f) e -> filterInput r f e)
300+ (liftA2 (,) reg (current foc))
292301 let imagesWithinRegion images = liftA2 (\is r -> map (withinImage r) is) images reg
293− localCtx subContext imagesWithinRegion child
302+ localInput inputFilter $ localCtx subContext imagesWithinRegion child
294303 where
295304 filterInput :: Region -> Bool -> VtyEvent -> Maybe VtyEvent
296305 filterInput (Region l t w h) focused e = case e of
@@ -435,7 +444,7 @@ keyCombos ks = do
435444
436445-- | A plain split of the available space into vertically stacked panes.
437446-- No visual separator is built in here.
438−splitV :: (Reflex t, Monad m, MonadNodeId m, HasVtyWidgetCtx t m, HasDisplaySize t m)
447+splitV :: (Reflex t, Monad m, MonadNodeId m, HasVtyWidgetCtx t m, HasDisplaySize t m, HasVtyInput t m)
439448 => Dynamic t (Int -> Int)
440449 -- ^ Function used to determine size of first pane based on available size
441450 -> Dynamic t (Bool, Bool)
@@ -456,7 +465,7 @@ splitV sizeFunD focD wA wB = do
456465
457466-- | A plain split of the available space into horizontally stacked panes.
458467-- No visual separator is built in here.
459−splitH :: (Reflex t, Monad m, MonadNodeId m, HasDisplaySize t m, HasVtyWidgetCtx t m)
468+splitH :: (Reflex t, Monad m, MonadNodeId m, HasDisplaySize t m, HasVtyWidgetCtx t m, HasVtyInput t m)
460469 => Dynamic t (Int -> Int)
461470 -- ^ Function used to determine size of first pane based on available size
462471 -> Dynamic t (Bool, Bool)
@@ -563,7 +572,7 @@ roundedBoxStyle :: BoxStyle
563572roundedBoxStyle = BoxStyle '╭' '─' '╮' '│' '╯' '─' '╰' '│'
564573
565574-- | Draws a titled box in the provided style and a child widget inside of that box
566−boxTitle :: (Monad m, Reflex t, MonadNodeId m, HasDisplaySize t m, ImageWriter t m, HasVtyWidgetCtx t m)
575+boxTitle :: (Monad m, Reflex t, MonadNodeId m, HasDisplaySize t m, ImageWriter t m, HasVtyWidgetCtx t m, HasVtyInput t m)
567576 => Behavior t BoxStyle
568577 -> Text
569578 -> m a
@@ -615,7 +624,7 @@ boxTitle boxStyle title child = do
615624 right = mkHalf delta
616625
617626-- | A box without a title
618−box :: (Monad m, Reflex t, MonadNodeId m, HasDisplaySize t m, ImageWriter t m, HasVtyWidgetCtx t m)
627+box :: (Monad m, Reflex t, MonadNodeId m, HasDisplaySize t m, ImageWriter t m, HasVtyWidgetCtx t m, HasVtyInput t m)
619628 => Behavior t BoxStyle
620629 -> m a
621630 -> m a
@@ -623,7 +632,7 @@ box boxStyle = boxTitle boxStyle mempty
623632
624633-- | A box whose style is static
625634boxStatic
626− :: (Monad m, Reflex t, MonadNodeId m, HasDisplaySize t m, ImageWriter t m, HasVtyWidgetCtx t m)
635+ :: (Monad m, Reflex t, MonadNodeId m, HasDisplaySize t m, ImageWriter t m, HasVtyWidgetCtx t m, HasVtyInput t m)
627636 => BoxStyle
628637 -> m a
629638 -> m a