brick-0.13: src/Brick/Focus.hs
{-# LANGUAGE DeriveFunctor #-}
-- | This module provides a type and functions for handling focus rings
-- of widgets. Note that this interface is merely provided for managing
-- the focus state for a sequence of resource names; it does not do
-- anything beyond keep track of that.
--
-- This interface is experimental.
module Brick.Focus
( FocusRing
, focusRing
, focusNext
, focusPrev
, focusGetCurrent
, focusRingCursor
, withFocusRing
)
where
import Lens.Micro ((^.))
import Data.Maybe (listToMaybe)
import Brick.Types
import Brick.Widgets.Core (Named(..))
-- | A focus ring containing a sequence of resource names to focus and a
-- currently-focused name.
data FocusRing n = FocusRingEmpty
| FocusRingNonempty ![n] !Int
deriving Functor
-- | Construct a focus ring from the list of resource names.
focusRing :: [n] -> FocusRing n
focusRing [] = FocusRingEmpty
focusRing names = FocusRingNonempty names 0
-- | Advance focus to the next widget in the ring.
focusNext :: FocusRing n -> FocusRing n
focusNext FocusRingEmpty = FocusRingEmpty
focusNext fr@(FocusRingNonempty [_] _) = fr
focusNext (FocusRingNonempty ns i) = FocusRingNonempty ns i'
where
i' = (i + 1) `mod` (length ns)
-- | Advance focus to the previous widget in the ring.
focusPrev :: FocusRing n -> FocusRing n
focusPrev FocusRingEmpty = FocusRingEmpty
focusPrev fr@(FocusRingNonempty [_] _) = fr
focusPrev (FocusRingNonempty ns i) = FocusRingNonempty ns i'
where
i' = (i + (length ns) - 1) `mod` (length ns)
-- | This function is a convenience function to look up a widget state
-- value's resource name in a focus ring and set its focus setting
-- according to the focus ring's state. This function determines whether
-- a given widget state value is the focus of the ring and passes the
-- resulting boolean to a rendering function, along with the state value
-- (a), to produce whatever comes next (b).
--
-- Focus-aware widgets have rendering functions that should be
-- usable with this combinator; see 'Brick.Widgets.List.List' and
-- 'Brick.Widgets.Edit.Edit'.
withFocusRing :: (Eq n, Named a n)
=> FocusRing n
-- ^ The focus ring to use as the source of focus state.
-> (Bool -> a -> b)
-- ^ A function that takes a value and its focus state.
-> a
-- ^ The wiget state value that we need to check for focus.
-> b
-- ^ The rest of the computation.
withFocusRing ring f a = f (focusGetCurrent ring == Just (getName a)) a
-- | Get the currently-focused resource name from the ring. If the ring
-- is emtpy, return 'Nothing'.
focusGetCurrent :: FocusRing n -> Maybe n
focusGetCurrent FocusRingEmpty = Nothing
focusGetCurrent (FocusRingNonempty ns i) = Just $ ns !! i
-- | Cursor selection convenience function for use as an
-- 'Brick.Main.appChooseCursor' value.
focusRingCursor :: (Eq n)
=> (a -> FocusRing n)
-- ^ The function used to get the focus ring out of your
-- application state.
-> a
-- ^ Your application state.
-> [CursorLocation n]
-- ^ The list of available cursor positions.
-> Maybe (CursorLocation n)
-- ^ The cursor position, if any, that matches the
-- resource name currently focused by the 'FocusRing'.
focusRingCursor getRing st ls =
listToMaybe $ filter isCurrent ls
where
isCurrent cl = cl^.cursorLocationNameL ==
(focusGetCurrent $ getRing st)