brick 0.25 → 0.26
raw patch · 4 files changed
+149/−2 lines, 4 filesdep ~microlensnew-component:exe:brick-list-vi-demoPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: microlens
API changes (from Hackage documentation)
+ Brick.Widgets.List: handleListEventVi :: (Ord n) => (Event -> List n e -> EventM n (List n e)) -> Event -> List n e -> EventM n (List n e)
Files
- CHANGELOG.md +11/−0
- brick.cabal +15/−1
- programs/ListViDemo.hs +90/−0
- src/Brick/Widgets/List.hs +33/−1
CHANGELOG.md view
@@ -2,6 +2,17 @@ Brick changelog --------------- +0.26+----++API changes:+ * Added Brick.Widgets.List.handleListEventVi to add support for+ vi-style movements to lists (thanks Richard Alex Hofer)++Other changes:+ * Added ListViDemo.hs to demonstrate the Vi-style handler for lists+ (thanks Richard Alex Hofer)+ 0.25 ----
brick.cabal view
@@ -1,5 +1,5 @@ name: brick-version: 0.25+version: 0.26 synopsis: A declarative terminal user interface library description: Write terminal applications painlessly with 'brick'! You write an@@ -268,6 +268,20 @@ ghc-options: -threaded -Wall -fno-warn-unused-do-bind -O3 default-language: Haskell2010 main-is: ListDemo.hs+ build-depends: base <= 5,+ brick,+ vty >= 5.15,+ text,+ microlens >= 0.3.0.0,+ vector++executable brick-list-vi-demo+ if !flag(demos)+ Buildable: False+ hs-source-dirs: programs+ ghc-options: -threaded -Wall -fno-warn-unused-do-bind -O3+ default-language: Haskell2010+ main-is: ListViDemo.hs build-depends: base <= 5, brick, vty >= 5.15,
+ programs/ListViDemo.hs view
@@ -0,0 +1,90 @@+{-# LANGUAGE OverloadedStrings #-}+module Main where++import Control.Monad (void)+import Data.Maybe (fromMaybe)+import Data.Monoid+import qualified Graphics.Vty as V+import Lens.Micro ((^.))++import qualified Brick.AttrMap as A+import qualified Brick.Main as M+import Brick.Types (Widget)+import qualified Brick.Types as T+import Brick.Util (fg, on)+import qualified Brick.Widgets.Border as B+import qualified Brick.Widgets.Center as C+import Brick.Widgets.Core (hLimit, str, vBox, vLimit, withAttr, (<+>))+import qualified Brick.Widgets.List as L+import qualified Data.Vector as Vec++drawUI :: (Show a) => L.List () a -> [Widget ()]+drawUI l = [ui]+ where+ label = str "Item " <+> cur <+> str " of " <+> total+ cur = case l^.(L.listSelectedL) of+ Nothing -> str "-"+ Just i -> str (show (i + 1))+ total = str $ show $ Vec.length $ l^.(L.listElementsL)+ box = B.borderWithLabel label $+ hLimit 25 $+ vLimit 15 $+ L.renderList listDrawElement True l+ ui = C.vCenter $ vBox [ C.hCenter box+ , str " "+ , C.hCenter $ str "Press +/- to add/remove list elements."+ , C.hCenter $ str "Press Esc to exit."+ ]++appEvent :: L.List () Char -> T.BrickEvent () e -> T.EventM () (T.Next (L.List () Char))+appEvent l (T.VtyEvent e) =+ case e of+ V.EvKey (V.KChar '+') [] ->+ let el = nextElement (L.listElements l)+ pos = Vec.length $ l^.(L.listElementsL)+ in M.continue $ L.listInsert pos el l++ V.EvKey (V.KChar '-') [] ->+ case l^.(L.listSelectedL) of+ Nothing -> M.continue l+ Just i -> M.continue $ L.listRemove i l++ V.EvKey V.KEsc [] -> M.halt l++ ev -> M.continue =<< (L.handleListEventVi L.handleListEvent) ev l+ where+ nextElement :: Vec.Vector Char -> Char+ nextElement v = fromMaybe '?' $ Vec.find (flip Vec.notElem v) (Vec.fromList ['a' .. 'z'])+appEvent l _ = M.continue l++listDrawElement :: (Show a) => Bool -> a -> Widget ()+listDrawElement sel a =+ let selStr s = if sel+ then withAttr customAttr (str $ "<" <> s <> ">")+ else str s+ in C.hCenter $ str "Item " <+> (selStr $ show a)++initialState :: L.List () Char+initialState = L.list () (Vec.fromList ['a','b','c']) 1++customAttr :: A.AttrName+customAttr = L.listSelectedAttr <> "custom"++theMap :: A.AttrMap+theMap = A.attrMap V.defAttr+ [ (L.listAttr, V.white `on` V.blue)+ , (L.listSelectedAttr, V.blue `on` V.white)+ , (customAttr, fg V.cyan)+ ]++theApp :: M.App (L.List () Char) e ()+theApp =+ M.App { M.appDraw = drawUI+ , M.appChooseCursor = M.showFirstCursor+ , M.appHandleEvent = appEvent+ , M.appStartEvent = return+ , M.appAttrMap = const theMap+ }++main :: IO ()+main = void $ M.defaultMain theApp initialState
src/Brick/Widgets/List.hs view
@@ -19,6 +19,7 @@ -- * Handling events , handleListEvent+ , handleListEventVi -- * Lenses , listElementsL@@ -58,7 +59,7 @@ import Lens.Micro ((^.), (&), (.~), (%~), _2) import Data.Maybe (fromMaybe) import Data.Monoid ((<>))-import Graphics.Vty (Event(..), Key(..))+import Graphics.Vty (Event(..), Key(..), Modifier(..)) import qualified Data.Vector as V import Brick.Types@@ -97,6 +98,37 @@ EvKey KPageDown [] -> listMovePageDown theList EvKey KPageUp [] -> listMovePageUp theList _ -> return theList++-- | Enable list movement with the vi keys with a fallback if none match.+-- Use (handleListEventVi handleListEvent) in place of handleListEvent to add+-- the vi keys bindings to the standard ones.+-- Movements handled:+-- * Up (k)+-- * Down (j)+-- * Page Up (Ctrl-b)+-- * Page Down (Ctrl-f)+-- * Half Page Up (Ctrl-u)+-- * Half Page Down (Ctrl-d)+-- * Top (g)+-- * Bottom (G)+handleListEventVi :: (Ord n)+ => (Event -> List n e -> EventM n (List n e))+ -- ^ Fallback event handler to use if none of the vi keys+ -- match.+ -> Event+ -> List n e+ -> EventM n (List n e)+handleListEventVi fallback e theList =+ case e of+ EvKey (KChar 'k') [] -> return $ listMoveUp theList+ EvKey (KChar 'j') [] -> return $ listMoveDown theList+ EvKey (KChar 'g') [] -> return $ listMoveTo 0 theList+ EvKey (KChar 'G') [] -> return $ listMoveTo (V.length $ listElements theList) theList+ EvKey (KChar 'f') [MCtrl] -> listMovePageDown theList+ EvKey (KChar 'b') [MCtrl] -> listMovePageUp theList+ EvKey (KChar 'd') [MCtrl] -> listMoveByPages 0.5 theList+ EvKey (KChar 'u') [MCtrl] -> listMoveByPages (-0.5) theList+ _ -> fallback e theList -- | The top-level attribute used for the entire list. listAttr :: AttrName