@@ -47,6 +47,7 @@ module Reflex.Vty.Widget
47 , doubleBoxStyle
47 , doubleBoxStyle
50 , KeyCombo
51 , KeyCombo
@@ -82,12 +83,6 @@ data VtyWidgetCtx t = VtyWidgetCtx
82 -- ^ The height of the region allocated to the widget.
83 -- ^ The height of the region allocated to the widget.
83 , _vtyWidgetCtx_focus :: Dynamic t Bool
84 , _vtyWidgetCtx_focus :: Dynamic t Bool
84 -- ^ Whether the widget should behave as if it has focus for keyboard input.
85 -- ^ 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.
93-- | The output of a 'VtyWidget'
88-- | The output of a 'VtyWidget'
@@ -106,7 +101,9 @@ instance (Adjustable t m, MonadHold t m, Reflex t) => Adjustable t (VtyWidget t
107-- | A widget that can read its context and produce image output
102-- | A widget that can read its context and produce image output
108newtype VtyWidget t m a = VtyWidget
103newtype 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
110 } deriving
107 } deriving
111 ( Functor
108 ( Functor
112 , Applicative
109 , Applicative
@@ -124,7 +121,7 @@ newtype VtyWidget t m a = VtyWidget
125deriving instance PerformEvent t m => PerformEvent t (VtyWidget t m)
122deriving instance PerformEvent t m => PerformEvent t (VtyWidget t m)
126instance MonadTrans (VtyWidget t) where
123instance MonadTrans (VtyWidget t) where
127− lift f = VtyWidget $ lift $ lift f
124+ lift f = VtyWidget $ lift $ lift $ lift f
129instance MonadNodeId m => MonadNodeId (VtyWidget t m) where
126instance MonadNodeId m => MonadNodeId (VtyWidget t m) where
130 getNextNodeId = VtyWidget $ do
127 getNextNodeId = VtyWidget $ do
@@ -147,10 +144,11 @@ instance (Monad m, Reflex t) => HasVtyWidgetCtx t (VtyWidget t m) where
147-- | Runs a 'VtyWidget' with a given context
144-- | Runs a 'VtyWidget' with a given context
148runVtyWidget
145runVtyWidget
149 :: (Reflex t, MonadNodeId m)
146 :: (Reflex t, MonadNodeId m)
150− => VtyWidgetCtx t
148+ -> VtyWidgetCtx t
151 -> VtyWidget t m a
149 -> VtyWidget t m a
152 -> m (a, Behavior t [Image])
150 -> 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
155-- | Sets up the top-level context for a 'VtyWidget' and runs it with that context
153-- | Sets up the top-level context for a 'VtyWidget' and runs it with that context
156mainWidgetWithHandle
154mainWidgetWithHandle
@@ -168,10 +166,9 @@ mainWidgetWithHandle vty child =
168 let ctx = VtyWidgetCtx
166 let ctx = VtyWidgetCtx
169 { _vtyWidgetCtx_width = fmap fst size
167 { _vtyWidgetCtx_width = fmap fst size
170 , _vtyWidgetCtx_height = fmap snd size
168 , _vtyWidgetCtx_height = fmap snd size
171− , _vtyWidgetCtx_input = inp'
172 , _vtyWidgetCtx_focus = constDyn True
169 , _vtyWidgetCtx_focus = constDyn True
174− (shutdown, images) <- runNodeIdT $ runVtyWidget ctx $ do
171+ (shutdown, images) <- runNodeIdT $ runVtyWidget inp' ctx $ do
175 tellImages . ffor (current size) $ \(w, h) -> [V.charFill V.defAttr ' ' w h]
172 tellImages . ffor (current size) $ \(w, h) -> [V.charFill V.defAttr ' ' w h]
177 return $ VtyResult
174 return $ VtyResult
@@ -206,7 +203,6 @@ instance HasDisplaySize t m => HasDisplaySize t (ReaderT x m)
206instance HasDisplaySize t m => HasDisplaySize t (BehaviorWriterT t x m)
203instance HasDisplaySize t m => HasDisplaySize t (BehaviorWriterT t x m)
207instance HasDisplaySize t m => HasDisplaySize t (DynamicWriterT t x m)
204instance HasDisplaySize t m => HasDisplaySize t (DynamicWriterT t x m)
208instance HasDisplaySize t m => HasDisplaySize t (EventWriterT t x m)
205instance HasDisplaySize t m => HasDisplaySize t (EventWriterT t x m)
210instance HasDisplaySize t m => HasDisplaySize t (NodeIdT m)
206instance HasDisplaySize t m => HasDisplaySize t (NodeIdT m)
212-- | A class for things that can receive vty events as input
208-- | A class for things that can receive vty events as input
@@ -214,9 +210,23 @@ class HasVtyInput t m | m -> t where
214 input :: m (Event t VtyEvent)
210 input :: m (Event t VtyEvent)
215 default input :: (f m' ~ m, Monad m', MonadTrans f, HasVtyInput t m') => m (Event t VtyEvent)
211 default input :: (f m' ~ m, Monad m', MonadTrans f, HasVtyInput t m') => m (Event t VtyEvent)
216 input = lift input
212 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
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)
218instance (Reflex t, Monad m) => HasVtyInput t (VtyWidget t m) where
223instance (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
227+ (a, images) <- lift $ lift $ local f $ runReaderT (runBehaviorWriterT m) ctx
221-- | A class for things that can dynamically gain and lose focus
231-- | A class for things that can dynamically gain and lose focus
222class HasFocus t m | m -> t where
232class HasFocus t m | m -> t where
@@ -273,7 +283,7 @@ withinImage (Region left top width height)
273-- * mouse inputs inside the region have their coordinates translated such
283-- * mouse inputs inside the region have their coordinates translated such
274-- that (0,0) is the top-left corner of the region
284-- that (0,0) is the top-left corner of the region
276− :: (Reflex t, Monad m, MonadNodeId m, HasVtyWidgetCtx t m)
286+ :: (Reflex t, Monad m, MonadNodeId m, HasVtyWidgetCtx t m, HasVtyInput t m)
277 => Dynamic t Region
287 => Dynamic t Region
278 -> Dynamic t Bool -- ^ Whether the widget should be focused when the parent is.
288 -> Dynamic t Bool -- ^ Whether the widget should be focused when the parent is.
@@ -281,16 +291,15 @@ pane
281pane dr foc child = do
291pane dr foc child = do
282 let reg = current dr
292 let reg = current dr
283 let subContext ctx = VtyWidgetCtx
293 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
289 , _vtyWidgetCtx_width = _region_width <$> dr
295 , _vtyWidgetCtx_width = _region_width <$> dr
290 , _vtyWidgetCtx_height = _region_height <$> dr
296 , _vtyWidgetCtx_height = _region_height <$> dr
298+ inputFilter = fmapMaybe id .
299+ attachWith (\(r,f) e -> filterInput r f e)
300+ (liftA2 (,) reg (current foc))
292 let imagesWithinRegion images = liftA2 (\is r -> map (withinImage r) is) images reg
301 let imagesWithinRegion images = liftA2 (\is r -> map (withinImage r) is) images reg
293− localCtx subContext imagesWithinRegion child
302+ localInput inputFilter $ localCtx subContext imagesWithinRegion child
295 filterInput :: Region -> Bool -> VtyEvent -> Maybe VtyEvent
304 filterInput :: Region -> Bool -> VtyEvent -> Maybe VtyEvent
296 filterInput (Region l t w h) focused e = case e of
305 filterInput (Region l t w h) focused e = case e of
@@ -435,7 +444,7 @@ keyCombos ks = do
436-- | A plain split of the available space into vertically stacked panes.
445-- | A plain split of the available space into vertically stacked panes.
437-- No visual separator is built in here.
446-- 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)
439 => Dynamic t (Int -> Int)
448 => Dynamic t (Int -> Int)
440 -- ^ Function used to determine size of first pane based on available size
449 -- ^ Function used to determine size of first pane based on available size
441 -> Dynamic t (Bool, Bool)
450 -> Dynamic t (Bool, Bool)
@@ -456,7 +465,7 @@ splitV sizeFunD focD wA wB = do
457-- | A plain split of the available space into horizontally stacked panes.
466-- | A plain split of the available space into horizontally stacked panes.
458-- No visual separator is built in here.
467-- 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)
460 => Dynamic t (Int -> Int)
469 => Dynamic t (Int -> Int)
461 -- ^ Function used to determine size of first pane based on available size
470 -- ^ Function used to determine size of first pane based on available size
462 -> Dynamic t (Bool, Bool)
471 -> Dynamic t (Bool, Bool)
@@ -563,7 +572,7 @@ roundedBoxStyle :: BoxStyle
563roundedBoxStyle = BoxStyle '╭' '─' '╮' '│' '╯' '─' '╰' '│'
572roundedBoxStyle = BoxStyle '╭' '─' '╮' '│' '╯' '─' '╰' '│'
565-- | Draws a titled box in the provided style and a child widget inside of that box
574-- | 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)
567 => Behavior t BoxStyle
576 => Behavior t BoxStyle
@@ -615,7 +624,7 @@ boxTitle boxStyle title child = do
615 right = mkHalf delta
624 right = mkHalf delta
617-- | A box without a title
626-- | 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)
619 => Behavior t BoxStyle
628 => Behavior t BoxStyle
@@ -623,7 +632,7 @@ box boxStyle = boxTitle boxStyle mempty
624-- | A box whose style is static
633-- | A box whose style is static
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)
627 => BoxStyle
636 => BoxStyle