@@ -23,6 +23,19 @@ import Reflex.Host.Class (MonadReflexCreateTrigger)
23import Reflex.Vty.Widget
23import Reflex.Vty.Widget
29+-- The focus monad tracks which element is currently focused and processes
30+-- requests to change focus. Focusable elements are assigned a 'FocusId' and
31+-- can manually request focus or receive focus due to some other action (e.g.,
32+-- a tab press in a sibling element, a click event).
34+-- Focusable elements will usually be created via 'tile', but can also be
35+-- constructed via 'makeFocus' in 'MonadFocus'. The latter option allows for
36+-- more find-grained control of focus behavior.
38+-- ** Storing focus state
27-- | Identifies an element that is focusable. Can be created using 'makeFocus'.
40-- | Identifies an element that is focusable. Can be created using 'makeFocus'.
28newtype FocusId = FocusId NodeId
41newtype FocusId = FocusId NodeId
@@ -42,6 +55,13 @@ instance Monoid FocusSet where
42singletonFS :: FocusId -> FocusSet
55singletonFS :: FocusId -> FocusSet
43singletonFS = FocusSet . OSet.singleton
56singletonFS = FocusSet . OSet.singleton
58+-- ** Changing focus state
60+-- | Operations that change the currently focused element.
61+data Refocus = Refocus_Shift Int -- ^ Shift the focus by a certain number of positions (see 'shiftFS')
62+ | Refocus_Id FocusId -- ^ Focus a particular element
63+ | Refocus_Clear -- ^ Remove focus from all elements
45-- | Given a 'FocusSet', a currently focused element, and a number of positions
65-- | Given a 'FocusSet', a currently focused element, and a number of positions
46-- to move by, determine the newly focused element.
66-- to move by, determine the newly focused element.
47shiftFS :: FocusSet -> Maybe FocusId -> Int -> Maybe FocusId
67shiftFS :: FocusSet -> Maybe FocusId -> Int -> Maybe FocusId
@@ -50,10 +70,20 @@ shiftFS (FocusSet s) fid n = case OSet.findIndex <$> fid <*> pure s of
50 Just Nothing -> OSet.elemAt s 0
70 Just Nothing -> OSet.elemAt s 0
51 Just (Just ix) -> OSet.elemAt s $ mod (ix + n) (OSet.size s)
71 Just (Just ix) -> OSet.elemAt s $ mod (ix + n) (OSet.size s)
73+-- ** The focus management monad
75+-- | A class for things that can produce focusable elements.
76+class (Monad m, Reflex t) => MonadFocus t m | m -> t where
77+ -- | Create a focusable element.
78+ makeFocus :: m FocusId
79+ -- | Emit an 'Event' of requests to change the focus.
80+ requestFocus :: Event t Refocus -> m ()
81+ -- | Produce a 'Dynamic' that indicates whether the given 'FocusId' is focused.
82+ isFocused :: FocusId -> m (Dynamic t Bool)
83+ -- | Run an action, additionally returning the focusable elements it produced.
84+ subFoci :: m a -> m (a, Dynamic t FocusSet)
53−-- | Operations that change the currently focused element.
85+ -- | Get a 'Dynamic' of the currently focused element identifier.
54−data Refocus = Refocus_Shift Int -- ^ Shift the focus by a certain number of positions (see 'shiftFS')
86+ focusedId :: m (Dynamic t (Maybe FocusId))
55− | Refocus_Id FocusId -- ^ Focus a particular element
56− | Refocus_Clear -- ^ Remove focus from all elements
58-- | A monad transformer that keeps track of the set of focusable elements and
88-- | A monad transformer that keeps track of the set of focusable elements and
59-- which, if any, are currently focused, and allows focus requests.
89-- which, if any, are currently focused, and allows focus requests.
@@ -78,6 +108,7 @@ newtype Focus t m a = Focus
78 , MonadNodeId
108 , MonadNodeId
81instance MonadTrans (Focus t) where
112instance MonadTrans (Focus t) where
82 lift = Focus . lift . lift . lift
113 lift = Focus . lift . lift . lift
@@ -101,19 +132,6 @@ instance ImageWriter t m => ImageWriter t (Focus t m)
102instance (HasFocus t m, Monad m) => HasFocus t (Focus t m)
133instance (HasFocus t m, Monad m) => HasFocus t (Focus t m)
104−-- | A class for things that can produce focusable elements.
105−class (Monad m, Reflex t) => MonadFocus t m | m -> t where
106− -- | Create a focusable element.
107− makeFocus :: m FocusId
108− -- | Emit an 'Event' of requests to change the focus.
109− requestFocus :: Event t Refocus -> m ()
110− -- | Produce a 'Dynamic' that indicates whether the given 'FocusId' is focused.
111− isFocused :: FocusId -> m (Dynamic t Bool)
112− -- | Run an action, additionally returning the focusable elements it produced.
113− subFoci :: m a -> m (a, Dynamic t FocusSet)
114− -- | Get a 'Dynamic' of the currently focused element identifier.
115− focusedId :: m (Dynamic t (Maybe FocusId))
117instance (Reflex t, MonadFix m, MonadNodeId m) => MonadFocus t (Focus t m) where
135instance (Reflex t, MonadFix m, MonadNodeId m) => MonadFocus t (Focus t m) where
118 makeFocus = do
136 makeFocus = do
119 fid <- FocusId <$> lift getNextNodeId
137 fid <- FocusId <$> lift getNextNodeId
@@ -145,51 +163,81 @@ runFocus (Focus x) = do
145 Refocus_Id fid -> Just fid
163 Refocus_Id fid -> Just fid
146 Refocus_Shift n -> shiftFS fs mf n
164 Refocus_Shift n -> shiftFS fs mf n
148−-- | A widget that is focusable and occupies a layout region based on the
166+-- ** Focus controls
149−-- provided constraint. Returns the 'FocusId' allowing for manual focus
152− :: (MonadNodeId m, MonadFix m, Reflex t, HasVtyWidgetCtx t m, HasVtyInput t m, MonadFocus t m, MonadLayout t m)
153− => Dynamic t Constraint
160− parentFocused <- isFocused fid
161− rec (click, result, subs) <- pane r focused $ do
162− m <- mouseDown V.BLeft
163− (x, sf) <- subFoci w
165− let childFocused = liftA2 (\f s -> case f of
167− Just f' -> OSet.member f' $ unFocusSet s) fid' subs
168− let focused = (||) <$> parentFocused <*> childFocused
169− requestFocus $ Refocus_Id fid <$ click
172−-- | A widget that is focusable and occupies a layout region based on the
168+-- | Request focus be shifted backward and forward based on tab presses. <Tab>
173−-- provided constraint.
169+-- shifts focus forward and <Shift+Tab> shifts focus backward.
175− :: (MonadNodeId m, MonadFix m, Reflex t, HasVtyWidgetCtx t m, HasVtyInput t m, MonadFocus t m, MonadLayout t m)
170+tabNavigation :: (Reflex t, MonadNodeId m, HasVtyInput t m, MonadFocus t m) => m ()
176− => Dynamic t Constraint
171+tabNavigation = do
177− -> m a
172+ fwd <- fmap (const 1) <$> key (V.KChar '\t')
178− -> m a
173+ back <- fmap (const (-1)) <$> key V.KBackTab
179−tile c = fmap snd . tile' c
174+ requestFocus $ Refocus_Shift <$> leftmost [fwd, back]
181−-- | A widget that is not focusable and occupies a layout region based on the
176+-- * Layout
182−-- provided constraint.
177+--
183−unfocusable
178+-- $layout
184− :: (Reflex t, MonadNodeId m, HasVtyWidgetCtx t m, MonadLayout t m)
179+-- The layout monad keeps track of a tree of elements, each having its own
180+-- layout constraints and orientation. Given the available rendering space, it
181+-- computes a layout solution and provides child elements with their particular
182+-- layout solution (the width and height of their rendering space).
184+-- Complex layouts are built up though some combination of:
186+-- - 'axis', which lays out its children in a particular orientation, and
187+-- - 'region', which "claims" some part of the screen according to its constraints
190+-- ** Layout restrictions
194+-- | Datatype representing constraints on a widget's size along the main axis (see 'Orientation')
195+data Constraint = Constraint_Fixed Int
196+ | Constraint_Min Int
197+ deriving (Show, Read, Eq, Ord)
199+-- | Shorthand for constructing a fixed constraint
203+ -> Dynamic t Constraint
204+fixed = fmap Constraint_Fixed
206+-- | Shorthand for constructing a minimum size constraint
210+ -> Dynamic t Constraint
211+stretch = fmap Constraint_Min
213+-- | Shorthand for constructing a constraint of no minimum size
185 => Dynamic t Constraint
216 => Dynamic t Constraint
217+flex = pure $ Constraint_Min 0
221+-- | The main-axis orientation of a 'Layout' widget
222+data Orientation = Orientation_Column
224+ deriving (Show, Read, Eq, Ord)
226+-- | Create a row-oriented 'axis'
228+ :: (Reflex t, MonadNodeId m, MonadFix m, MonadLayout t m)
231+row = axis (pure Orientation_Row) flex
233+-- | Create a column-oriented 'axis'
235+ :: (Reflex t, MonadNodeId m, MonadFix m, MonadLayout t m)
188−unfocusable c w = do
238+col = axis (pure Orientation_Column) flex
190− pane r (pure False) w
192−-- * Layout
240+-- ** Layout management data
194-- | A collection of information related to the layout of the screen. The root
242-- | A collection of information related to the layout of the screen. The root
195-- node is a "parent" widget, and the contents of the 'LayoutForest' are its
243-- node is a "parent" widget, and the contents of the 'LayoutForest' are its
@@ -228,6 +276,75 @@ rootLT (LayoutTree a _) = a
228childrenLT :: LayoutTree a -> LayoutForest a
276childrenLT :: LayoutTree a -> LayoutForest a
229childrenLT (LayoutTree _ a) = a
277childrenLT (LayoutTree _ a) = a
279+-- | Produce a layout solution given a starting orientation, the overall screen
280+-- size, and a set of constraints.
284+ -> LayoutForest (Constraint, Orientation)
285+ -> LayoutTree (Region, Orientation)
286+solve o0 r0 (LayoutForest cs) =
287+ let a = map (\(x, t@(LayoutTree (c, _) _)) -> ((x, t), c)) $ OMap.assocs cs
289+ Orientation_Row -> _region_width r0
290+ Orientation_Column -> _region_height r0
291+ sizes = computeEdges $ computeSizes extent a
292+ chunks = [ (nodeId, solve o1 r1 f)
293+ | ((nodeId, LayoutTree (_, o1) f), sz) <- sizes
294+ , let r1 = chunk o0 r0 sz
296+ in LayoutTree (r0, o0) $ fromListLF chunks
298+ computeEdges :: [(a, Int)] -> [(a, (Int, Int))]
299+ computeEdges = ($ []) . fst . foldl (\(m, offset) (a, sz) ->
300+ (((a, (offset, sz)) :) . m, sz + offset)) (id, 0)
303+ -> [(a, Constraint)]
305+ computeSizes available constraints =
306+ let minTotal = sum $ ffor constraints $ \case
307+ (_, Constraint_Fixed n) -> n
308+ (_, Constraint_Min n) -> n
309+ leftover = max 0 (available - minTotal)
310+ numStretch = length $ filter (isMin . snd) constraints
311+ szStretch = floor $ leftover % max numStretch 1
312+ adjustment = max 0 $ available - minTotal - szStretch * numStretch
313+ in snd $ mapAccumL (\adj (a, c) -> case c of
314+ Constraint_Fixed n -> (adj, (a, n))
315+ Constraint_Min n -> (0, (a, n + szStretch + adj))) adjustment constraints
316+ isMin (Constraint_Min _) = True
319+-- | Produce a 'Region' given a starting orientation and region, and the offset
320+-- and main-axis size of the chunk.
321+chunk :: Orientation -> Region -> (Int, Int) -> Region
322+chunk o r (offset, sz) = case o of
323+ Orientation_Column -> r
324+ { _region_top = _region_top r + offset
325+ , _region_height = sz
327+ Orientation_Row -> r
328+ { _region_left = _region_left r + offset
329+ , _region_width = sz
334+-- ** The layout monad
336+-- | A class of operations for creating screen layouts.
337+class Monad m => MonadLayout t m | m -> t where
338+ -- | Creates a parent element in the current layout with the given size
339+ -- constraint, which lays out its children according to the provided
341+ axis :: Dynamic t Orientation -> Dynamic t Constraint -> m a -> m a
342+ -- | Creates a child element in the current layout with the given size
343+ -- constraint, returning the 'Region' that the child element is allocated.
344+ region :: Dynamic t Constraint -> m (Dynamic t Region)
345+ -- | Returns the orientation of the containing 'axis'.
346+ askOrientation :: m (Dynamic t Orientation)
231-- | A monad transformer that collects layout constraints and provides a layout
348-- | A monad transformer that collects layout constraints and provides a layout
232-- solution that satisfies those constraints.
349-- solution that satisfies those constraints.
233newtype Layout t m a = Layout
350newtype Layout t m a = Layout
@@ -259,7 +376,6 @@ instance (Adjustable t m, MonadFix m, MonadHold t m) => Adjustable t (Layout t m
259 traverseDMapWithKeyWithAdjust f m e = Layout $ traverseDMapWithKeyWithAdjust (\k v -> unLayout $ f k v) m e
376 traverseDMapWithKeyWithAdjust f m e = Layout $ traverseDMapWithKeyWithAdjust (\k v -> unLayout $ f k v) m e
260 traverseDMapWithKeyWithAdjustWithMove f m e = Layout $ traverseDMapWithKeyWithAdjustWithMove (\k v -> unLayout $ f k v) m e
377 traverseDMapWithKeyWithAdjustWithMove f m e = Layout $ traverseDMapWithKeyWithAdjustWithMove (\k v -> unLayout $ f k v) m e
263instance (HasVtyWidgetCtx t m, HasDisplaySize t m, Reflex t, MonadFix m) => HasVtyWidgetCtx t (Layout t m) where
379instance (HasVtyWidgetCtx t m, HasDisplaySize t m, Reflex t, MonadFix m) => HasVtyWidgetCtx t (Layout t m) where
264 localCtx f g x = do
380 localCtx f g x = do
265 solution <- Layout ask
381 solution <- Layout ask
@@ -276,18 +392,6 @@ instance ImageWriter t m => ImageWriter t (Layout t m)
277instance (HasFocus t m, Monad m) => HasFocus t (Layout t m)
393instance (HasFocus t m, Monad m) => HasFocus t (Layout t m)
279−-- | A class of operations for creating screen layouts.
280−class Monad m => MonadLayout t m | m -> t where
281− -- | Creates a parent element in the current layout with the given size
282− -- constraint, which lays out its children according to the provided
284− axis :: Dynamic t Orientation -> Dynamic t Constraint -> m a -> m a
285− -- | Creates a child element in the current layout with the given size
286− -- constraint, returning the 'Region' that the child element is allocated.
287− region :: Dynamic t Constraint -> m (Dynamic t Region)
288− -- | Returns the orientation of the containing 'axis'.
289− askOrientation :: m (Dynamic t Orientation)
291instance (Monad m, MonadNodeId m, Reflex t, MonadFix m) => MonadLayout t (Layout t m) where
395instance (Monad m, MonadNodeId m, Reflex t, MonadFix m) => MonadLayout t (Layout t m) where
292 axis o c (Layout x) = Layout $ do
396 axis o c (Layout x) = Layout $ do
293 nodeId <- getNextNodeId
397 nodeId <- getNextNodeId
@@ -312,134 +416,6 @@ instance (MonadFix m, MonadFocus t m) => MonadFocus t (Layout t m) where
312 tellDyn w
416 tellDyn w
313 pure (a, sf)
417 pure (a, sf)
315−-- | Datatype representing constraints on a widget's size along the main axis (see 'Orientation')
316−data Constraint = Constraint_Fixed Int
317− | Constraint_Min Int
318− deriving (Show, Read, Eq, Ord)
320−-- | The main-axis orientation of a 'Layout' widget
321−data Orientation = Orientation_Column
323− deriving (Show, Read, Eq, Ord)
325−-- | Create a row-oriented 'axis'
327− :: (Reflex t, MonadNodeId m, MonadFix m, MonadLayout t m)
330−row = axis (pure Orientation_Row) (pure $ Constraint_Min 0)
332−-- | Create a column-oriented 'axis'
334− :: (Reflex t, MonadNodeId m, MonadFix m, MonadLayout t m)
337−col = axis (pure Orientation_Column) (pure $ Constraint_Min 0)
339−-- | A tile constrained to the given size. Returns the 'FocusId' of the tile,
340−-- allowing for manual focus management.
342− :: (Reflex t, MonadNodeId m, MonadFix m, HasVtyWidgetCtx t m, HasVtyInput t m, MonadFocus t m, MonadLayout t m)
346−fixed' n = tile' (Constraint_Fixed <$> n)
348−-- | A tile constrained to the given size.
350− :: (Reflex t, MonadNodeId m, MonadFix m, HasVtyWidgetCtx t m, HasVtyInput t m, MonadFocus t m, MonadLayout t m)
354−fixed n = fmap snd . fixed' n
356−-- | A tile that can grow, but will always occupy at least the given minimum
357−-- size. Returns the 'FocusId' of the tile, allowing for manual focus
360− :: (Reflex t, MonadNodeId m, MonadFix m, HasVtyWidgetCtx t m, HasVtyInput t m, MonadFocus t m, MonadLayout t m)
364−stretch' minSz = tile' (Constraint_Min <$> minSz)
366−-- | A tile that can grow, but will always occupy at least the given minimum
369− :: (Reflex t, MonadNodeId m, MonadFix m, HasVtyWidgetCtx t m, HasVtyInput t m, MonadFocus t m, MonadLayout t m)
373−stretch minSz = fmap snd . stretch' minSz
375−-- | A tile that can grow or shrink without constraint. Returns the 'FocusId'
376−-- of the tile, allowing for manual focus management.
378− :: (Reflex t, MonadNodeId m, MonadFix m, HasVtyWidgetCtx t m, HasVtyInput t m, MonadFocus t m, MonadLayout t m)
381−flex' = tile' (pure $ Constraint_Min 0)
383−-- | A tile that can grow or shrink without constraint.
385− :: (Reflex t,MonadNodeId m, MonadFix m, HasVtyWidgetCtx t m, HasVtyInput t m, MonadFocus t m, MonadLayout t m)
388−flex = fmap snd . flex'
390−-- | Produce a layout solution given a starting orientation, the overall screen
391−-- size, and a set of constraints.
395− -> LayoutForest (Constraint, Orientation)
396− -> LayoutTree (Region, Orientation)
397−solve o0 r0 (LayoutForest cs) =
398− let a = map (\(x, t@(LayoutTree (c, _) _)) -> ((x, t), c)) $ OMap.assocs cs
400− Orientation_Row -> _region_width r0
401− Orientation_Column -> _region_height r0
402− sizes = computeEdges $ computeSizes extent a
403− chunks = [ (nodeId, solve o1 r1 f)
404− | ((nodeId, LayoutTree (_, o1) f), sz) <- sizes
405− , let r1 = chunk o0 r0 sz
407− in LayoutTree (r0, o0) $ fromListLF chunks
409− computeEdges :: [(a, Int)] -> [(a, (Int, Int))]
410− computeEdges = ($ []) . fst . foldl (\(m, offset) (a, sz) ->
411− (((a, (offset, sz)) :) . m, sz + offset)) (id, 0)
414− -> [(a, Constraint)]
416− computeSizes available constraints =
417− let minTotal = sum $ ffor constraints $ \case
418− (_, Constraint_Fixed n) -> n
419− (_, Constraint_Min n) -> n
420− leftover = max 0 (available - minTotal)
421− numStretch = length $ filter (isMin . snd) constraints
422− szStretch = floor $ leftover % max numStretch 1
423− adjustment = max 0 $ available - minTotal - szStretch * numStretch
424− in snd $ mapAccumL (\adj (a, c) -> case c of
425− Constraint_Fixed n -> (adj, (a, n))
426− Constraint_Min n -> (0, (a, n + szStretch + adj))) adjustment constraints
427− isMin (Constraint_Min _) = True
430−-- | Produce a 'Region' given a starting orientation and region, and the offset
431−-- and main-axis size of the chunk.
432−chunk :: Orientation -> Region -> (Int, Int) -> Region
433−chunk o r (offset, sz) = case o of
434− Orientation_Column -> r
435− { _region_top = _region_top r + offset
436− , _region_height = sz
438− Orientation_Row -> r
439− { _region_left = _region_left r + offset
440− , _region_width = sz
443-- | Runs a 'Layout' action, using the given orientation and region to
419-- | Runs a 'Layout' action, using the given orientation and region to
444-- calculate layout solutions.
420-- calculate layout solutions.
@@ -453,54 +429,28 @@ runLayout o r (Layout x) = do
453 let solutions = solve <$> o <*> r <*> w
429 let solutions = solve <$> o <*> r <*> w
454 return result
430 return result
456−-- | Request focus be shifted backward and forward based on tab presses. <Tab>
432+-- | Initialize and run the layout monad, using all of the available screen space.
457−-- shifts focus forward and <Shift+Tab> shifts focus backward.
433+initLayout :: (HasDisplaySize t m, MonadFix m) => Layout t m a -> m a
458−tabNavigation :: (Reflex t, MonadNodeId m, HasVtyInput t m, MonadFocus t m) => m ()
434+initLayout f = do
459−tabNavigation = do
435+ dw <- displayWidth
460− fwd <- fmap (const 1) <$> key (V.KChar '\t')
436+ dh <- displayHeight
461− back <- fmap (const (-1)) <$> key V.KBackTab
437+ let r = Region 0 0 <$> dw <*> dh
462− requestFocus $ Refocus_Shift <$> leftmost [fwd, back]
438+ runLayout (pure Orientation_Column) r f
464−test :: IO ()
440+-- * The tile "window manager"
465−test = mainWidget $ do
441+--
466− inp <- input
442+-- $tiling
467− let box' w = do
443+-- Generally MonadLayout and MonadFocus are used together to build a user
468− f <- focus
444+-- interface. These functions check the available screen size and initialize
469− box (ffor (current f) $ \x -> if x then doubleBoxStyle else singleBoxStyle) $ w
445+-- the layout monad with that information, and also initialize the focus monad.
472− box' $ richText (RichTextConfig $ current $ (\x -> if x then V.withStyle V.defAttr V.underline else V.defAttr) <$> f) t
476− fixed 5 $ box' $ row $ do
490− return $ fforMaybe inp $ \case
491− V.EvKey (V.KChar 'c') [V.MCtrl] -> Just ()
494-- | Initialize a 'Layout' and 'Focus' management context, returning the produced 'FocusSet'.
447-- | Initialize a 'Layout' and 'Focus' management context, returning the produced 'FocusSet'.
495initManager
448initManager
496 :: (HasDisplaySize t m, Reflex t, MonadHold t m, MonadFix m)
449 :: (HasDisplaySize t m, Reflex t, MonadHold t m, MonadFix m)
497 => Layout t (Focus t m) a
450 => Layout t (Focus t m) a
498 -> m (a, Dynamic t FocusSet)
451 -> m (a, Dynamic t FocusSet)
499−initManager f = do
452+initManager =
500− dw <- displayWidth
453+ runFocus . initLayout
502− let r = Region 0 0 <$> dw <*> dh
503− runFocus $ runLayout (pure Orientation_Column) r f
505-- | Initialize a 'Layout' and 'Focus' management context.
455-- | Initialize a 'Layout' and 'Focus' management context.
506initManager_
456initManager_
@@ -508,3 +458,53 @@ initManager_
508 => Layout t (Focus t m) a
458 => Layout t (Focus t m) a
510initManager_ = fmap fst . initManager
460initManager_ = fmap fst . initManager
466+-- | A widget that is focusable and occupies a layout region based on the
467+-- provided constraint. Returns the 'FocusId' allowing for manual focus
470+ :: (MonadNodeId m, MonadFix m, Reflex t, HasVtyWidgetCtx t m, HasVtyInput t m, MonadFocus t m, MonadLayout t m)
471+ => Dynamic t Constraint
478+ parentFocused <- isFocused fid
479+ rec (click, result, subs) <- pane r focused $ do
480+ m <- mouseDown V.BLeft
481+ (x, sf) <- subFoci w
483+ let childFocused = liftA2 (\f s -> case f of
485+ Just f' -> OSet.member f' $ unFocusSet s) fid' subs
486+ let focused = (||) <$> parentFocused <*> childFocused
487+ requestFocus $ Refocus_Id fid <$ click
490+-- | A widget that is focusable and occupies a layout region based on the
491+-- provided constraint.
493+ :: (MonadNodeId m, MonadFix m, Reflex t, HasVtyWidgetCtx t m, HasVtyInput t m, MonadFocus t m, MonadLayout t m)
494+ => Dynamic t Constraint
497+tile c = fmap snd . tile' c
501+-- | A widget that is not focusable and occupies a layout region based on the
502+-- provided constraint.
504+ :: (Reflex t, MonadNodeId m, HasVtyWidgetCtx t m, MonadLayout t m)
505+ => Dynamic t Constraint
510+ pane r (pure False) w