on develop help sign in
Base key and mouse inputs on HasVtyInput; Improve focus request
commit7fa7aa91617bb8d53f569bd7150ee49a4d293e3f authorAli Abrar <aliabrar@gmail.com> dateSun 14 Mar 2021, 23:58 (6 years ago) parent18fa7f5
4 files changed+76−89
src/Reflex/Vty/Host.hs−1 src/Reflex/Vty/Widget.hs+18−16 src/Reflex/Vty/Widget/Input/Text.hs+14−15 src/Reflex/Vty/Widget/Layout.hs+44−57
src/Reflex/Vty/Host.hs−1
@@ -202,7 +202,6 @@ runVtyAppWithHandle vty vtyGuest = flip onException (V.shutdown vty) $
202202 updateVty
203203 loop
204204 where
205− -- TODO Some part of this is probably general enough to belong in reflex
206205 -- | Use the given 'FireCommand' to fire events that have subscribers
207206 -- and call the callback for the 'TriggerInvocation' of each.
208207 fireEventTriggerRefs
src/Reflex/Vty/Widget.hs+18−16
@@ -68,7 +68,6 @@ module Reflex.Vty.Widget
6868 ) where
6969
7070import Control.Applicative (liftA2)
71−import Control.Monad (join)
7271import Control.Monad.Fix (MonadFix)
7372import Control.Monad.IO.Class (MonadIO)
7473import Control.Monad.Reader (ReaderT, ask, asks, runReaderT)
@@ -213,6 +212,8 @@ instance HasDisplaySize t m => HasDisplaySize t (NodeIdT m)
213212-- | A class for things that can receive vty events as input
214213class HasVtyInput t m | m -> t where
215214 input :: m (Event t VtyEvent)
215+ default input :: (f m' ~ m, Monad m', MonadTrans f, HasVtyInput t m') => m (Event t VtyEvent)
216+ input = lift input
216217
217218instance (Reflex t, Monad m) => HasVtyInput t (VtyWidget t m) where
218219 input = VtyWidget . lift $ asks _vtyWidgetCtx_input
@@ -267,6 +268,7 @@ withinImage (Region left top width height)
267268-- * mouse inputs outside the region are ignored
268269-- * mouse inputs inside the region have their coordinates translated such
269270-- that (0,0) is the top-left corner of the region
271+-- TODO make this polymorphic
270272pane
271273 :: (Reflex t, Monad m, MonadNodeId m)
272274 => Dynamic t Region
@@ -287,7 +289,7 @@ pane dr foc child = VtyWidget $ do
287289 , _vtyWidgetCtx_width = _region_width <$> dr
288290 , _vtyWidgetCtx_height = _region_height <$> dr
289291 }
290− (result, images) <- lift . lift $ runVtyWidget ctx' child
292+ (result, images) <- lift . lift $ runVtyWidget ctx' child -- TODO can we use a local-based implementation?
291293 let images' = liftA2 (\r is -> map (withinImage r) is) reg images
292294 tellImages images'
293295 return result
@@ -319,9 +321,9 @@ data Drag = Drag
319321
320322-- | Converts raw vty mouse drag events into an event stream of 'Drag's
321323drag
322− :: (Reflex t, MonadFix m, MonadHold t m)
324+ :: (Reflex t, MonadFix m, MonadHold t m, HasVtyInput t m)
323325 => V.Button
324− -> VtyWidget t m (Event t Drag)
326+ -> m (Event t Drag)
325327drag btn = do
326328 inp <- input
327329 let f :: Maybe Drag -> V.Event -> Maybe Drag
@@ -350,9 +352,9 @@ drag btn = do
350352
351353-- | Mouse down events for a particular mouse button
352354mouseDown
353− :: (Reflex t, Monad m)
355+ :: (Reflex t, Monad m, HasVtyInput t m)
354356 => V.Button
355− -> VtyWidget t m (Event t MouseDown)
357+ -> m (Event t MouseDown)
356358mouseDown btn = do
357359 i <- input
358360 return $ fforMaybe i $ \case
@@ -363,8 +365,8 @@ mouseDown btn = do
363365
364366-- | Mouse up events for a particular mouse button
365367mouseUp
366− :: (Reflex t, Monad m)
367− => VtyWidget t m (Event t MouseUp)
368+ :: (Reflex t, Monad m, HasVtyInput t m)
369+ => m (Event t MouseUp)
368370mouseUp = do
369371 i <- input
370372 return $ fforMaybe i $ \case
@@ -392,8 +394,8 @@ data ScrollDirection = ScrollDirection_Up | ScrollDirection_Down
392394
393395-- | Produce an event that fires when the mouse wheel is scrolled
394396mouseScroll
395− :: (Reflex t, Monad m)
396− => VtyWidget t m (Event t ScrollDirection)
397+ :: (Reflex t, Monad m, HasVtyInput t m)
398+ => m (Event t ScrollDirection)
397399mouseScroll = do
398400 up <- mouseDown V.BScrollUp
399401 down <- mouseDown V.BScrollDown
@@ -406,25 +408,25 @@ mouseScroll = do
406408type KeyCombo = (V.Key, [V.Modifier])
407409
408410-- | Emits an event that fires on a particular key press (without modifiers)
409−key :: (Monad m, Reflex t) => V.Key -> VtyWidget t m (Event t KeyCombo)
411+key :: (Monad m, Reflex t, HasVtyInput t m) => V.Key -> m (Event t KeyCombo)
410412key = keyCombos . Set.singleton . (,[])
411413
412414-- | Emits an event that fires on particular key presses (without modifiers)
413−keys :: (Monad m, Reflex t) => [V.Key] -> VtyWidget t m (Event t KeyCombo)
415+keys :: (Monad m, Reflex t, HasVtyInput t m) => [V.Key] -> m (Event t KeyCombo)
414416keys = keyCombos . Set.fromList . fmap (,[])
415417
416418-- | Emit an event that fires whenever the provided key combination occurs
417419keyCombo
418− :: (Reflex t, Monad m)
420+ :: (Reflex t, Monad m, HasVtyInput t m)
419421 => KeyCombo
420− -> VtyWidget t m (Event t KeyCombo)
422+ -> m (Event t KeyCombo)
421423keyCombo = keyCombos . Set.singleton
422424
423425-- | Emit an event that fires whenever any of the provided key combinations occur
424426keyCombos
425− :: (Reflex t, Monad m)
427+ :: (Reflex t, Monad m, HasVtyInput t m)
426428 => Set KeyCombo
427− -> VtyWidget t m (Event t KeyCombo)
429+ -> m (Event t KeyCombo)
428430keyCombos ks = do
429431 i <- input
430432 return $ fforMaybe i $ \case
src/Reflex/Vty/Widget/Input/Text.hs+14−15
@@ -48,9 +48,9 @@ data TextInput t = TextInput
4848
4949-- | A widget that allows text input
5050textInput
51− :: (Reflex t, MonadHold t m, MonadFix m)
51+ :: (Reflex t, MonadHold t m, MonadFix m, HasVtyInput t m, HasFocus t m, HasDisplaySize t m, ImageWriter t m)
5252 => TextInputConfig t
53− -> VtyWidget t m (TextInput t)
53+ -> m (TextInput t)
5454textInput cfg = do
5555 i <- input
5656 f <- focus
@@ -103,19 +103,18 @@ multilineTextInput cfg = do
103103-- | Wraps a 'textInput' or 'multilineTextInput' in a tile. Uses
104104-- the computed line count to greedily size the tile when vertically
105105-- oriented, and uses the fallback width when horizontally oriented.
106−-- TODO
107−-- textInputTile
108−-- :: (Reflex t, MonadHold t m, MonadFix m, MonadNodeId m)
109−-- => VtyWidget t m (TextInput t)
110−-- -> Dynamic t Int
111−-- -> Layout t m (TextInput t)
112−-- textInputTile txt width = do
113−-- o <- askOrientation
114−-- rec t <- fixed sz txt
115−-- let sz = join $ ffor o $ \case
116−-- Orientation_Column -> _textInput_lines t
117−-- Orientation_Row -> width
118−-- return t
106+textInputTile
107+ :: (Monad m, MonadNodeId m, Reflex t, MonadFix m)
108+ => VtyWidget t m (TextInput t)
109+ -> Dynamic t Int
110+ -> Layout t (Focus t (VtyWidget t m)) (TextInput t)
111+textInputTile txt width = do
112+ o <- askOrientation
113+ rec (_, t) <- tileC (Constraint_Fixed <$> sz) txt
114+ let sz = join $ ffor o $ \case
115+ Orientation_Column -> _textInput_lines t
116+ Orientation_Row -> width
117+ return t
119118
120119-- | Default attributes for the text cursor
121120cursorAttributes :: V.Attr
src/Reflex/Vty/Widget/Layout.hs+44−57
@@ -11,27 +11,7 @@ Description: Monad transformer and tools for arranging widgets and building scre
1111{-# LANGUAGE OverloadedStrings #-}
1212{-# LANGUAGE UndecidableInstances #-}
1313
14−module Reflex.Vty.Widget.Layout
15− ( Orientation(..)
16− , Constraint(..)
17− , Layout(..)
18− , runLayout
19− , tile
20− , fixed
21− , stretch
22− , col
23− , row
24− , tabNavigation
25− , askOrientation
26− , Focus(..)
27− , runFocus
28− , focusId
29− , LForest(..)
30− , LTree
31− , FocusSet(..)
32−
33− , test
34− ) where
14+module Reflex.Vty.Widget.Layout where
3515
3616import Control.Monad.NodeId (MonadNodeId(..), NodeId)
3717import Control.Monad.Reader
@@ -67,10 +47,14 @@ shiftFS (FocusSet s) fid n = case OSet.findIndex <$> fid <*> pure s of
6747 Just Nothing -> OSet.elemAt s 0
6848 Just (Just ix) -> OSet.elemAt s $ mod (ix + n) (OSet.size s)
6949
50+data Refocus = Refocus_Shift Int
51+ | Refocus_Id FocusId
52+ | Refocus_Clear
53+
7054newtype Focus t m a = Focus
7155 { unFocus :: DynamicWriterT t FocusSet
7256 (ReaderT (Demux t (Maybe FocusId))
73− (EventWriterT t (First (Maybe FocusId)) m)) a
57+ (EventWriterT t (First Refocus) m)) a
7458 }
7559 deriving
7660 ( Functor
@@ -91,12 +75,14 @@ newtype Focus t m a = Focus
9175instance MonadTrans (Focus t) where
9276 lift = Focus . lift . lift . lift
9377
78+instance (HasVtyInput t m, Monad m) => HasVtyInput t (Focus t m)
79+
9480newtype FocusId = FocusId NodeId
9581 deriving (Eq, Ord)
9682
9783class (Monad m, Reflex t) => MonadFocus t m | m -> t where
9884 focusId :: m FocusId
99− requestFocus :: Event t (Maybe FocusId) -> m ()
85+ requestFocus :: Event t Refocus -> m ()
10086 isFocused :: FocusId -> m (Dynamic t Bool)
10187
10288instance (Reflex t, Monad m, MonadNodeId m) => MonadFocus t (Focus t m) where
@@ -115,20 +101,38 @@ runFocus
115101 -> m (a, Dynamic t FocusSet)
116102runFocus (Focus x) = do
117103 rec ((a, focusIds), focusRequests) <- runEventWriterT $ flip runReaderT (demux sel) $ runDynamicWriterT x
118− sel <- holdDyn Nothing $ getFirst <$> focusRequests
104+ sel <- foldDyn f Nothing $ attach (current focusIds) focusRequests
119105 pure (a, focusIds)
120−
121−tile
106+ where
107+ f :: (FocusSet, First Refocus) -> Maybe FocusId -> Maybe FocusId
108+ f (fs, rf) mf = case getFirst rf of
109+ Refocus_Clear -> Nothing
110+ Refocus_Id fid -> Just fid
111+ Refocus_Shift n -> shiftFS fs mf n
112+
113+-- TODO unify this with tile, perhaps
114+tileC
122115 :: (MonadNodeId m, MonadFix m, Reflex t)
123− => VtyWidget t m a
116+ => Dynamic t Constraint
117+ -> VtyWidget t m a
124118 -> Layout t (Focus t (VtyWidget t m)) (FocusId, a)
125−tile w = do
119+tileC c w = do
126120 fid <- focusId
127121 focused <- isFocused fid
128− r <- stretch 0
129− result <- lift $ lift $ pane r focused w
122+ r <- leaf c
123+ (click, result) <- lift $ lift $ pane r focused $ do
124+ click <- mouseDown V.BLeft
125+ result <- w
126+ pure (click, result)
127+ requestFocus $ Refocus_Id fid <$ click
130128 pure (fid, result)
131129
130+tile
131+ :: (MonadNodeId m, MonadFix m, Reflex t)
132+ => VtyWidget t m a
133+ -> Layout t (Focus t (VtyWidget t m)) (FocusId, a)
134+tile = tileC (pure $ Constraint_Min 0)
135+
132136tile_
133137 :: (MonadNodeId m, MonadFix m, Reflex t)
134138 => VtyWidget t m a
@@ -185,6 +189,9 @@ newtype Layout t m a = Layout
185189instance MonadTrans (Layout t) where
186190 lift = Layout . lift . lift
187191
192+instance (HasVtyInput t m, Monad m) => HasVtyInput t (Layout t m)
193+
194+-- TODO Naming: To Has or not to Has?
188195class Monad m => MonadLayout t m | m -> t where
189196 axis :: Dynamic t Orientation -> Dynamic t Constraint -> m a -> m a
190197 leaf :: Dynamic t Constraint -> m (Dynamic t Region)
@@ -193,7 +200,7 @@ class Monad m => MonadLayout t m | m -> t where
193200instance (Monad m, MonadNodeId m, Reflex t, MonadFix m) => MonadLayout t (Layout t m) where
194201 axis o c (Layout x) = Layout $ do
195202 nodeId <- getNextNodeId
196− (result, forest) <- lift $ local (\t -> (\(Just x) -> x) . lookupLF nodeId . ltreeForest <$> t) $ runDynamicWriterT x
203+ (result, forest) <- lift $ local (\t -> (\(Just a) -> a) . lookupLF nodeId . ltreeForest <$> t) $ runDynamicWriterT x
197204 tellDyn $ singletonLF nodeId <$> (LTree <$> ((,) <$> c <*> o) <*> forest)
198205 pure result
199206 leaf c = do
@@ -226,6 +233,7 @@ row, col
226233row = axis (pure Orientation_Row) (pure $ Constraint_Min 0)
227234col = axis (pure Orientation_Column) (pure $ Constraint_Min 0)
228235
236+-- TODO possibly get rid of this and stretch
229237fixed
230238 :: (Reflex t, MonadLayout t m)
231239 => Dynamic t Int
@@ -304,27 +312,11 @@ runLayout o r (Layout x) = do
304312 let solutions = solve <$> o <*> r <*> w
305313 return result
306314
307−{-
308−-- | Captures the click event in a 'VtyWidget' context and returns it. Useful for
309−-- requesting focus when using 'tile'.
310−clickable
311− :: (Reflex t, Monad m)
312− => VtyWidget t m a
313− -> VtyWidget t m (Event t (), a)
314−clickable child = do
315− click <- mouseDown V.BLeft
316− a <- child
317− return (() <$ click, a)
318−
319−-}
320−
321−-- | Produces an 'Event' that navigates forward one tile when the Tab key is pressed
322−-- and backward one tile when Shift+Tab is pressed.
323−tabNavigation :: (Reflex t, Monad m) => VtyWidget t m (Event t Int)
315+tabNavigation :: (Reflex t, MonadNodeId m, HasVtyInput t m, MonadFocus t m) => m ()
324316tabNavigation = do
325317 fwd <- fmap (const 1) <$> key (V.KChar '\t')
326318 back <- fmap (const (-1)) <$> key V.KBackTab
327− return $ leftmost [fwd, back]
319+ requestFocus $ Refocus_Shift <$> leftmost [fwd, back]
328320
329321test :: IO ()
330322test = mainWidget $ do
@@ -335,19 +327,14 @@ test = mainWidget $ do
335327 text' t = do
336328 f <- focus
337329 richText (RichTextConfig $ current $ (\x -> if x then V.withStyle V.defAttr V.bold else V.defAttr) <$> f) t
338− tab <- tabNavigation
339− rec (_, focusSet) <- runFocus $ do
340− rec sel <- holdDyn Nothing $ attachWith (\(fs, s) t -> shiftFS fs s t) (current $ (,) <$> focusSet <*> sel) tab
341− requestFocus $ updated sel
330+ rec (_, _) <- runFocus $ do
331+ tabNavigation
342332 runLayout (pure Orientation_Column) r $ do
343333 col $ do
344334 tile_ $ text' "asdf"
345335 tile_ $ text' "asdf"
346336 row $ do
347− (fid, click) <- tile $ do
348− text' "CLICK ME"
349− mouseDown V.BLeft
350− requestFocus $ Just fid <$ click
337+ tile_ $ text' "xyz"
351338 tile_ $ text' "xyz"
352339 tile_ $ text' "xyz"
353340 tile_ $ text' "xyz"