brick-tabular-list 0.2.0.1 → 1.0.0.0
raw patch · 19 files changed
+687/−788 lines, 19 filesdep +vectordep ~basedep ~brickdep ~containers
Dependencies added: vector
Dependency ranges changed: base, brick, containers, generic-lens, microlens, optics-core, vty
Files
- CHANGELOG.md +9/−0
- brick-tabular-list.cabal +21/−31
- demo/GridTabularList.hs +182/−0
- demo/Internal/GridTabularList.hs +0/−182
- demo/Internal/MixedTabularList.hs +0/−176
- demo/MixedTabularList.hs +184/−0
- exec/GridTabularList.hs +0/−21
- exec/GridTabularListVi.hs +0/−25
- exec/MixedTabularList.hs +0/−14
- exec/MixedTabularListVi.hs +0/−18
- grid-tabular-list-01.png binary
- grid-tabular-list-02.png binary
- grid-tabular-list-03.png binary
- mixed-tabular-list.png binary
- src/Brick/Widgets/TabularList.hs +35/−38
- src/Brick/Widgets/TabularList/Grid.hs +120/−134
- src/Brick/Widgets/TabularList/Internal/Common.hs +15/−17
- src/Brick/Widgets/TabularList/Mixed.hs +75/−101
- src/Brick/Widgets/TabularList/Types.hs +46/−31
CHANGELOG.md view
@@ -1,5 +1,14 @@ # Revision history for brick-tabular-list +## 1.0.0.0 -- 2023-03-10++* Substantially simplified and improved API. It is far less likely to make mistakes with the new API.+* Substantially improved documentation+* Removed unneccesary demo programs+* Inserted a separator below column headers in demo programs+* Specify 120 characters per line as maximum line length+* Added `NoFieldSelectors` to modules+ ## 0.2.0.1 -- 2023-02-27 * Fixed demo programs for haskell-language-server
brick-tabular-list.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: brick-tabular-list-version: 0.2.0.1+version: 1.0.0.0 synopsis: Tabular list widgets for brick. @@ -10,6 +10,12 @@ * Grid tabular list * Mixed tabular list + A tabular list consists of cells(row columns), column headers, and row headers. Column headers and row headers are+ optional.++ It can handle a very large data set if you delete invisible rows from memory and fetch visible rows from a database+ (file). For example, SQLite database file can handle a large spreadsheet.+ == To get started * Read "Brick.Widgets.TabularList.Grid" or "Brick.Widgets.TabularList.Mixed".@@ -21,6 +27,10 @@ For zoom, you have to use van Laarhoven lens because brick supports zoom through microlens. + == For Contributors++ This library tries not to exceed 120 characters per line.+ homepage: https://codeberg.org/amano.kenji/brick-tabular-list bug-reports: https://codeberg.org/amano.kenji/brick-tabular-list/issues @@ -45,13 +55,13 @@ library build-depends:- base >=4.15.1.0 && <5+ base >=4.16.4.0 && <5 , brick >=1.5 && <1.7- , containers ^>=0.6.4- , generic-lens ^>=2.2.1- , microlens ^>=0.4.13- , optics-core ^>=0.4.1- , vty ^>=5.38+ , containers >=0.6.4 && <0.7+ , generic-lens >=2.2.1 && <2.3+ , microlens >=0.4.13 && <0.5+ , optics-core >=0.4.1 && <0.5+ , vty >=5.38 && <5.39 exposed-modules: Brick.Widgets.TabularList , Brick.Widgets.TabularList.Grid@@ -63,7 +73,7 @@ hs-source-dirs: src default-language: Haskell2010 -library demo+common demo if !flag(demo) buildable: False build-depends:@@ -72,36 +82,16 @@ , brick-tabular-list , containers , optics-core+ , vector >=0.12.3.1 && <0.14 , vty- exposed-modules:- Internal.MixedTabularList- , Internal.GridTabularList hs-source-dirs: demo- default-language: Haskell2010--common exec- if !flag(demo)- buildable: False- build-depends:- base- , brick-tabular-list- , demo- hs-source-dirs: exec ghc-options: -threaded default-language: Haskell2010 executable mixed-tabular-list- import: exec+ import: demo main-is: MixedTabularList.hs -executable mixed-tabular-list-vi- import: exec- main-is: MixedTabularListVi.hs- executable grid-tabular-list- import: exec+ import: demo main-is: GridTabularList.hs--executable grid-tabular-list-vi- import: exec- main-is: GridTabularListVi.hs
+ demo/GridTabularList.hs view
@@ -0,0 +1,182 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE NoFieldSelectors #-}+module Main where++import Brick.Widgets.TabularList.Grid+-- base+import GHC.Generics (Generic)+import Control.Monad (void)+-- Third party libraries+import Optics.Core+import Data.Sequence (Seq)+import qualified Data.Sequence as S+import qualified Data.Vector as V+import Data.Vector (Vector)+-- brick+import Brick.Main+import Brick.AttrMap+import Brick.Types+import Brick.Widgets.Core+import Brick.Widgets.Center+import Brick.Widgets.Border+import Brick.Util+import Brick.Widgets.List+import Brick.Widgets.Border.Style+import Graphics.Vty (defAttr, Event(..), Key(..), Modifier(..), black, white, blue, red)++data Song = Song {+ artist :: String+, title :: String+, album :: String+, composer :: String+, genre :: String+, time :: Int+} deriving (Generic)++data Name = TheList deriving (Eq, Ord, Show)++data AppState = AppState {+ libList :: LibraryList+, libRenderers :: LibraryRenderers+, listWidth :: Int+} deriving Generic++type LibraryList = GridTabularList Name Song+type LibraryRenderers = GridRenderers Name Song Int++songs :: Seq Song+songs = S.fromList $+ map (\n -> Song {+ artist = "Artist " <> show n+ , title = "Title " <> show n+ , album = "Album " <> show n+ , composer = "Composer " <> show n+ , genre = "Genre " <> show n+ , time = n+ })+ [1..1000]++handleLibEvent :: BrickEvent Name () -> EventM Name AppState ()+handleLibEvent e = do+ s <- get+ let r = s ^. #libRenderers+ case e of+ VtyEvent (EvKey KEsc []) -> halt+ VtyEvent (EvKey (KChar 'q') []) -> halt+ VtyEvent (EvKey (KChar 'r') []) -> zoom (#libRenderers . #rowHdr) $ modify $ \case+ Nothing -> Just rowHdr+ Just _ -> Nothing+ VtyEvent (EvKey (KChar 'c') []) -> zoom (#libRenderers . #colHdr) $ modify $ \case+ Nothing -> Just colHdr+ Just _ -> Nothing+ VtyEvent (EvKey (KChar '-') []) -> zoom #listWidth $ modify $ max 1 . subtract 1+ VtyEvent (EvKey (KChar '=') []) -> zoom #listWidth $ modify (+1)+ VtyEvent e -> zoom #libList $ do+ handleGridListEvent r e+ handleGridListEventVi r e+ _ -> return ()++drawUi :: AppState -> [Widget Name]+drawUi s = let+ msgs = [ "Press Up arrow or k to go up one item"+ , "Press Down arrow or j to go down one item"+ , "Press PageUp or Ctrl+f to go up one page"+ , "Press PageDown or Ctrl+b to go down one page"+ , "Press Home or g to go to the beginning"+ , "Press End or G to go to the end"+ , "Press Ctrl+u to go up half page"+ , "Press Ctrl+d to go down half page"+ , " "+ , "Press Left arrow or h to go left by one column"+ , "Press Right arrow or l to go right by one column"+ , "Press Ctrl+Home or H to go to the first column"+ , "Press Ctrl+End or L to go to the last column"+ , "Press Ctrl+PageUp or Alt+h or Backspace to go left by one page of columns"+ , "Press Ctrl+PageDown or Alt+l to go right by one page of columns" ]+ theList = renderGridTabularList (s ^. #libRenderers) True (s ^. #libList)+ in [vCenter $ vBox $ hCenter (padLeftRight 2 $ joinBorders $ border $ hLimit (s ^. #listWidth) $ vLimit 15 theList)+ : hCenter (str "Press Esc or q to exit" )+ : hCenter (str "Press c to toggle column headers")+ : hCenter (str "Press r to toggle row headaers")+ : hCenter (str "Press - to shorten the list and = to widen the list")+ : hCenter (str " ")+ : map (hCenter . str) msgs]++columnHdrAttr :: AttrName+columnHdrAttr = attrName "columnHeader"+rowHdrAttr :: AttrName+rowHdrAttr = attrName "rowHeader"+colSelectedAttr :: AttrName+colSelectedAttr = attrName "selectedColumn"++colHdrs :: Vector String+colHdrs = V.fromList ["Artist", "Title", "Album", "Composer", "Genre", "Time"]++rowHdr :: RowHdr Name Song Int+rowHdr = RowHdr {+ draw = \_ wd f rh -> let+ attrFn = if f+ then id+ else withAttr rowHdrAttr+ in attrFn $ padRight (Pad $ if wd > 0 then 0 else 1) $ padLeft Max (str $ show rh)+, width = \_ rh -> (+2) $ maximum $ map (length . show) rh+, toRowHdr = \_ i -> i+1+}++colHdr :: GridColHdr Name+colHdr = GridColHdr {+ draw = \_ wd (FlatContext i _) -> let+ renderColH = withAttr columnHdrAttr . padRight (Pad $ if wd > 0 then 0 else 1) . padRight Max . str+ in case colHdrs V.!? i of+ Just ch -> renderColH ch <=> hBorder+ Nothing -> emptyWidget+, height = 2+}++renderers :: LibraryRenderers+renderers = GridRenderers {+ drawCell = \_ wd (GridContext (FlatContext _ rs) (FlatContext ci cs)) s -> let+ attrFn = if rs && cs+ then withAttr colSelectedAttr+ else id+ renderCell s = padRight (Pad $ if wd > 0 then 0 else 1) (padRight Max $ str s)+ in attrFn $ case ci of+ 0 -> renderCell $ s ^. #artist+ 1 -> renderCell $ s ^. #title+ 2 -> renderCell $ s ^. #album+ 3 -> renderCell $ s ^. #composer+ 4 -> renderCell $ s ^. #genre+ 5 -> let+ (min, sec) = (s ^. #time) `divMod` 60+ in renderCell $ case min of+ 0 -> show sec+ _ -> show min <> ":" <> show sec+ _ -> fill ' '+, rowHdr = Just rowHdr+, colHdr = Just colHdr+, drawColHdrRowHdr = Just $ \_ _ -> vLimit 1 (fill ' ') <=> hBorder+}++theList :: LibraryList+theList = gridTabularList TheList songs 1 $ S.fromList [12, 11, 11, 14, 11, 7]++main :: IO ()+main = do+ let appState = AppState {+ libList = theList+ , libRenderers = renderers+ , listWidth = 39+ }+ app = App {+ appDraw = drawUi+ , appChooseCursor = neverShowCursor+ , appHandleEvent = handleLibEvent+ , appStartEvent = return ()+ , appAttrMap = const $ attrMap defAttr [ (colSelectedAttr, black `on` white)+ , (columnHdrAttr, fg blue)+ , (rowHdrAttr, fg red)]+ }+ void $ defaultMain app appState
− demo/Internal/GridTabularList.hs
@@ -1,182 +0,0 @@-{-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE OverloadedLabels #-}-{-# LANGUAGE FlexibleContexts #-}-module Internal.GridTabularList (- runMain-, theList-)-where--import Brick.Widgets.TabularList.Grid hiding (sizes, contents)--- base-import GHC.Generics (Generic)-import Control.Monad (void)--- Third party libraries-import Optics.Core-import Data.Sequence (Seq(..))-import qualified Data.Sequence as S--- brick-import Brick.Main-import Brick.AttrMap-import Brick.Types-import Brick.Widgets.Core-import Brick.Widgets.Center-import Brick.Widgets.Border-import Brick.Util-import Brick.Widgets.List-import Brick.Widgets.Border.Style-import Graphics.Vty (defAttr, Event(..), Key(..), Modifier(..), black, white, blue, red)--data Song = Song {- artist :: String-, title :: String-, album :: String-, composer :: String-, genre :: String-, time :: Int-} deriving Generic--data SongCell = StringCell String | TimeCell Int deriving Generic--data Name = TheList deriving (Eq, Ord, Show)--type LibraryList = GridTabularList Name Song SongCell Int String-type LibraryRenderers = GridRenderers Name Song SongCell Int String-type LibraryContents = GridContents Name Song SongCell Int String-type LibraryEventHandler = LibraryRenderers -> Event -> EventM Name LibraryList ()--songs :: Seq Song-songs = S.fromList $- map (\n -> Song ("Artist " <> show n) ("Title " <> show n)- ("Album " <> show n) ("Composer " <> show n) ("Genre " <> show n)- n)- [1..1000]--sizes :: GridSizes Int-sizes = GridSizes {- row = S.fromList [12, 11, 11, 14, 11, 7]-, rowHdr = Just $ VisibleRowHeaders $ \_ rowHs -> (+2) $ maximum $ map (length . show) rowHs-, colHdr = Just 1-}--contents :: LibraryContents-contents = let- getCell s 0 = Just $ StringCell $ s ^. #artist- getCell s 1 = Just $ StringCell $ s ^. #title- getCell s 2 = Just $ StringCell $ s ^. #album- getCell s 3 = Just $ StringCell $ s ^. #composer- getCell s 4 = Just $ StringCell $ s ^. #genre- getCell s 5 = Just $ TimeCell $ s ^. #time- getCell _ _ = Nothing- getColumnHeader 0 = Just "Artist"- getColumnHeader 1 = Just "Title"- getColumnHeader 2 = Just "Album"- getColumnHeader 3 = Just "Composer"- getColumnHeader 4 = Just "Genre"- getColumnHeader 5 = Just "Time"- getColumnHeader _ = Nothing- getRowHeader _ n = Just (n+1)- in GridContents {- cell = getCell- , rowHdr = Just getRowHeader- , colHdr = Just getColumnHeader- }--data AppState = AppState {- libList :: LibraryList-, libRenderers :: LibraryRenderers-, listWidth :: Int-} deriving Generic--handleLibEvent :: LibraryEventHandler -> BrickEvent Name () -> EventM Name AppState ()-handleLibEvent eh e = do- s <- get- case e of- VtyEvent (EvKey KEsc []) -> halt- VtyEvent (EvKey (KChar 'q') []) -> halt- VtyEvent (EvKey (KChar 'c') []) -> zoom (#libRenderers . #drawColHdr) $ modify $ \case- Nothing -> Just dch- Just _ -> Nothing- VtyEvent (EvKey (KChar 'r') []) -> zoom (#libRenderers . #drawRowHdr) $ modify $ \case- Nothing -> Just drh- Just _ -> Nothing- VtyEvent (EvKey (KChar '-') []) -> zoom #listWidth $ modify $ max 1 . subtract 1- VtyEvent (EvKey (KChar '=') []) -> zoom #listWidth $ modify (+1)- VtyEvent e -> zoom #libList $ eh (s ^. #libRenderers) e- _ -> return ()--drawUi :: [String] -> AppState -> [Widget Name]-drawUi msgs s = let- theList = renderGridTabularList (s ^. #libRenderers) True (s ^. #libList)- in [vCenter $ vBox $ hCenter (padLeftRight 2 $ border $ hLimit (s ^. #listWidth) $ vLimit 15 theList)- : hCenter (str "Press Esc or q to exit" )- : hCenter (str "Press c to toggle column headers")- : hCenter (str "Press r to toggle row headaers")- : hCenter (str "Press - to shorten the list and = to widen the list")- : hCenter (str " ")- : map (hCenter . str) msgs]--columnHdrAttr :: AttrName-columnHdrAttr = attrName "columnHeader"-rowHdrAttr :: AttrName-rowHdrAttr = attrName "rowHeader"-colSelectedAttr :: AttrName-colSelectedAttr = attrName "selectedColumn"--getApp :: [String] -> LibraryEventHandler -> App AppState () Name-getApp msgs eh = App {- appDraw = drawUi msgs-, appChooseCursor = neverShowCursor-, appHandleEvent = handleLibEvent eh-, appStartEvent = return ()-, appAttrMap = const $ attrMap defAttr [ (colSelectedAttr, black `on` white)- , (columnHdrAttr, fg blue)- , (rowHdrAttr, fg red)]-}--drh lf wd (Position i f) row = \case- Nothing -> fill ' '- Just rh ->- let attrFn = if f- then id- else withAttr rowHdrAttr- in attrFn $ padRight (Pad $ if wd > 0 then 0 else 1) $ padLeft Max (str $ show rh)--dch lf wd (Position i f) = \case- Nothing -> fill ' '- Just ch -> withAttr columnHdrAttr $ padRight (Pad $ if wd > 0 then 0 else 1) $ padRight Max (str ch)--renderers :: LibraryRenderers-renderers = GridRenderers {- drawCell = \lf wd gc song mc -> let- attrFn = if gc ^. #row % #selected && gc ^. #col % #selected - then withAttr colSelectedAttr- else id- in attrFn $ case mc of- Nothing -> fill ' '- Just (StringCell s) -> padRight (Pad $ if wd > 0 then 0 else 1) $ padRight Max (str s)- Just (TimeCell time) -> let- (min, sec) = time `divMod` 60- time' = case min of- 0 -> show sec- _ -> show min <> ":" <> show sec- in padRight (Pad $ if wd > 0 then 0 else 1) $ padRight Max $ str time'-, drawRowHdr = Just drh-, drawColHdr = Just dch--- This is the same as Nothing.-, drawColHdrRowHdr = Just $ \_ _ -> fill ' '-}--theList :: LibraryList-theList = gridTabularList TheList songs 1 sizes contents--runMain :: [String] -> LibraryEventHandler -> IO ()-runMain msgs eh = do- let appState = AppState {- libList = theList- , libRenderers = renderers- , listWidth = 39- }- void $ defaultMain (getApp msgs eh) appState
− demo/Internal/MixedTabularList.hs
@@ -1,176 +0,0 @@-{-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE OverloadedLabels #-}-module Internal.MixedTabularList (- runMain-)-where--import Brick.Widgets.TabularList.Mixed hiding (sizes, contents)--- base-import GHC.Generics (Generic)-import Control.Monad (void)--- Third party libraries-import Optics.Core-import Data.Sequence (Seq(..))-import qualified Data.Sequence as S--- brick-import Brick.Main-import Brick.AttrMap-import Brick.Types-import Brick.Widgets.Core-import Brick.Widgets.Center-import Brick.Widgets.Border-import Brick.Util-import Brick.Widgets.List-import Brick.Widgets.Border.Style-import Graphics.Vty (defAttr, Event(..), Key(..), Modifier(..), black, white, blue, red)--data Song = Song {- artist :: String-, title :: String-, album :: String-, time :: Int-} deriving Generic--data LibraryEntry = LibFolder String | LibSong Song deriving Generic--data LibCell = StringCell String | FolderCell String | TimeCell Int deriving Generic--data Name = TheList deriving (Eq, Ord, Show)--type LibraryList = MixedTabularList Name LibraryEntry LibCell Int String-type LibraryRenderers = MixedRenderers Name LibraryEntry LibCell Int String-type LibraryContents = MixedContents LibraryEntry LibCell Int String-type LibraryEventHandler = Event -> EventM Name LibraryList ()--libraryEntries :: Seq LibraryEntry-libraryEntries = let- folders = [LibFolder "[.]", LibFolder "[..]"]- songs = map (\n -> LibSong (Song ("Artist " <> show n) ("Title " <> show n) ("Album " <> show n) n)) [1..1000]- in S.fromList $ folders ++ songs--contents :: LibraryContents-contents = let- getCell (LibFolder s) 0 = Just $ FolderCell $ "Folder: " <> s- getCell (LibFolder _) _ = Nothing- getCell (LibSong s) 0 = Just $ StringCell $ s ^. #artist- getCell (LibSong s) 1 = Just $ StringCell $ s ^. #title- getCell (LibSong s) 2 = Just $ StringCell $ s ^. #album- getCell (LibSong s) 3 = Just $ TimeCell $ s ^. #time- getCell (LibSong _) _ = Nothing- getColumnHeader 0 = Just "Artist"- getColumnHeader 1 = Just "Title"- getColumnHeader 2 = Just "Album"- getColumnHeader 3 = Just "Time"- getColumnHeader _ = Nothing- getRowHeader _ n = Just (n+1)- in MixedContents {- cell = getCell- , rowHdr = Just getRowHeader- , colHdr = Just getColumnHeader- }--data AppState = AppState {- libList :: LibraryList-, libRenderers :: LibraryRenderers-, listWidth :: Int-} deriving Generic--handleLibEvent :: LibraryEventHandler -> BrickEvent Name () -> EventM Name AppState ()-handleLibEvent eh e = case e of- VtyEvent (EvKey KEsc []) -> halt- VtyEvent (EvKey (KChar 'q') []) -> halt- VtyEvent (EvKey (KChar 'c') []) -> zoom (#libRenderers . #drawColHdr) $ modify $ \case- Nothing -> Just dch- Just _ -> Nothing- VtyEvent (EvKey (KChar 'r') []) -> zoom (#libRenderers . #drawRowHdr) $ modify $ \case- Nothing -> Just drh- Just _ -> Nothing- VtyEvent (EvKey (KChar '-') []) -> zoom #listWidth $ modify $ max 1 . subtract 1- VtyEvent (EvKey (KChar '=') []) -> zoom #listWidth $ modify (+1)- VtyEvent e -> zoom #libList $ eh e- _ -> return ()- where abc = []--drawUi :: [String] -> AppState -> [Widget Name]-drawUi msgs s = let- theList = renderMixedTabularList (s ^. #libRenderers) True (s ^. #libList)- in [vCenter $ vBox $ hCenter (border $ vLimit 15 $ hLimit (s ^. #listWidth) theList)- : hCenter (str "Press Esc or q to exit" )- : hCenter (str "Press c to toggle column headers")- : hCenter (str "Press r to toggle row headaers")- : hCenter (str "Press - to shorten the list and = to widen the list")- : hCenter (str " ")- : map (hCenter . str) msgs ]--columnHdrAttr :: AttrName-columnHdrAttr = attrName "columnHeader"-rowHdrAttr :: AttrName-rowHdrAttr = attrName "rowHeader"--getApp :: [String] -> LibraryEventHandler -> App AppState () Name-getApp msgs eh = App {- appDraw = drawUi msgs-, appChooseCursor = neverShowCursor-, appHandleEvent = handleLibEvent eh-, appStartEvent = return ()-, appAttrMap = const $ attrMap defAttr [ (listSelectedAttr, black `on` white)- , (columnHdrAttr, fg blue)- , (rowHdrAttr, fg red)]-}--dch lf ci = \case- Nothing -> hCenter $ str " "- Just ch -> withAttr columnHdrAttr $ padRight Max (str ch) <+> str " "--drh lf wd (Position i f) row = \case- Nothing -> hCenter $ str " "- Just rh ->- let attrFn = if f- then id- else withAttr rowHdrAttr- in attrFn $ padRight (Pad $ if wd > 0 then 0 else 1) $ padLeft Max (str $ show rh)--renderers :: LibraryRenderers-renderers = MixedRenderers {- drawCell = \lf (Position i f) row mc -> case mc of- Nothing -> hCenter $ str " "- Just (StringCell s) -> padRight Max (str s) <+> str " "- Just (FolderCell s) -> padRight Max $ str s- Just (TimeCell time) -> let- (min, sec) = time `divMod` 60- time' = case min of- 0 -> show sec- _ -> show min <> ":" <> show sec- in padRight Max (str time') <+> str " "-, drawRowHdr = Just drh-, drawColHdr = Just dch--- This is the same as Nothing.-, drawColHdrRowHdr = Just $ \_ _ -> fill ' '-}--runMain :: [String] -> LibraryEventHandler -> IO ()-runMain msgs eh = do- let sizes = MixedSizes {- rowHdr = Just $ VisibleRowHeaders $ \_ rowHs -> (+2) $ maximum $ map (length . show) rowHs- , colSizes = AvailWidth $ \aW -> let- artist = max 7 $ (aW * 30) `div` 100- title = max 6 $ (aW * 30) `div` 100- time = 7- album = aW - artist - title - time- songWidths = [artist, title, album, time]- in ColSizes {- rowKind = \case- LibFolder _ -> [aW]- LibSong _ -> songWidths- , colHdr = Just (songWidths, 1)- }- }- appState = AppState {- libList = mixedTabularList TheList libraryEntries 1 sizes contents- , libRenderers = renderers- , listWidth = 80- }- void $ defaultMain (getApp msgs eh) appState
+ demo/MixedTabularList.hs view
@@ -0,0 +1,184 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE NoFieldSelectors #-}+module Main where++import Brick.Widgets.TabularList.Mixed+-- base+import GHC.Generics (Generic)+import Control.Monad (void)+-- Third party libraries+import Optics.Core+import Data.Sequence (Seq)+import qualified Data.Sequence as S+import Data.Vector (Vector)+import qualified Data.Vector as V+-- brick+import Brick.Main+import Brick.AttrMap+import Brick.Types+import Brick.Widgets.Core+import Brick.Widgets.Center+import Brick.Widgets.Border+import Brick.Util+import Brick.Widgets.List+import Brick.Widgets.Border.Style+import Graphics.Vty (defAttr, Event(..), Key(..), Modifier(..), black, white, blue, red)++data Song = Song {+ artist :: String+, title :: String+, album :: String+, time :: Int+} deriving Generic++data LibraryEntry = LibFolder String | LibSong Song deriving Generic++data Name = TheList deriving (Eq, Ord, Show)++data Widths = Widths {+ song :: [Width]+, folder :: [Width]+} deriving Generic++data AppState = AppState {+ libList :: LibraryList+, libRenderers :: LibraryRenderers+, listWidth :: Int+} deriving Generic++type LibraryList = MixedTabularList Name LibraryEntry Widths+type LibraryRenderers = MixedRenderers Name LibraryEntry Widths Int++libraryEntries :: Seq LibraryEntry+libraryEntries = let+ folders = [LibFolder "[.]", LibFolder "[..]"]+ songs = map (\n -> LibSong $ Song {+ artist = "Artist " <> show n+ , title = "Title " <> show n+ , album = "Album " <> show n+ , time = n+ })+ [1..998]+ in S.fromList $ folders ++ songs++handleLibEvent :: BrickEvent Name () -> EventM Name AppState ()+handleLibEvent e = case e of+ VtyEvent (EvKey KEsc []) -> halt+ VtyEvent (EvKey (KChar 'q') []) -> halt+ VtyEvent (EvKey (KChar 'r') []) -> zoom (#libRenderers . #rowHdr) $ modify $ \case+ Nothing -> Just rowHdr+ Just _ -> Nothing+ VtyEvent (EvKey (KChar 'c') []) -> zoom (#libRenderers . #colHdr) $ modify $ \case+ Nothing -> Just colHdr+ Just _ -> Nothing+ VtyEvent (EvKey (KChar '-') []) -> zoom #listWidth $ modify $ max 1 . subtract 1+ VtyEvent (EvKey (KChar '=') []) -> zoom #listWidth $ modify (+1)+ VtyEvent e -> zoom #libList $ do+ handleMixedListEvent e+ handleMixedListEventVi e+ _ -> return ()++drawUi :: AppState -> [Widget Name]+drawUi s = let+ theList = renderMixedTabularList (s ^. #libRenderers) True (s ^. #libList)+ msgs = [ "Press Up arrow or k to go up one item"+ , "Press Down arrow or j to go down one item"+ , "Press PageUp or Ctrl+b to go up one page"+ , "Press PageDown or Ctrl+f to go down one page"+ , "Press Home or g to go to the beginning"+ , "Press End or G to go to the end"+ , "Press Ctrl+u to go up half page"+ , "Press Ctrl+d to go down half page" ]+ in [vCenter $ vBox $ hCenter (joinBorders $ border $ vLimit 15 $ hLimit (s ^. #listWidth) theList)+ : hCenter (str "Press Esc or q to exit" )+ : hCenter (str "Press c to toggle column headers")+ : hCenter (str "Press r to toggle row headaers")+ : hCenter (str "Press - to shorten the list and = to widen the list")+ : hCenter (str " ")+ : map (hCenter . str) msgs ]++columnHdrAttr :: AttrName+columnHdrAttr = attrName "columnHeader"+rowHdrAttr :: AttrName+rowHdrAttr = attrName "rowHeader"++dc :: ListFocused -> MixedContext -> LibraryEntry -> Widget n+dc lf (MixedContext (FlatContext _ f) ci) e = let+ renderPlainCell s = padRight Max (str s) <+> str " "+ in case e of+ LibFolder f -> case ci of+ 0 -> padRight Max $ str $ "Folder: " <> f+ _ -> emptyWidget+ LibSong (Song {artist, title, album, time}) -> case ci of+ 0 -> renderPlainCell artist+ 1 -> renderPlainCell title+ 2 -> renderPlainCell album+ 3 -> renderPlainCell $ let+ (min, sec) = time `divMod` 60+ in case min of+ 0 -> show sec+ _ -> show min <> ":" <> show sec+ _ -> emptyWidget++colHdrs :: Vector String+colHdrs = V.fromList ["Artist", "Title", "Album", "Time"]++cWprk :: AvailWidth -> [LibraryEntry] -> Widths+cWprk aW _ = let+ artist = max 7 $ (aW * 30) `div` 100+ title = max 6 $ (aW * 30) `div` 100+ time = 7+ album = aW - artist - title - time+ in Widths { song = [artist, title, album, time], folder = [aW] }++wpr :: Widths -> LibraryEntry -> [Width]+wpr (Widths {song, folder}) = \case+ LibSong _ -> song+ LibFolder _ -> folder++rowHdr :: RowHdr Name LibraryEntry Int+rowHdr = RowHdr {+ draw = \lf wd f rh -> let+ attrFn = if f+ then id+ else withAttr rowHdrAttr+ in attrFn $ padRight (Pad $ if wd > 0 then 0 else 1) $ padLeft Max (str $ show rh)+, width = \_ rh -> (+2) $ maximum $ map (length . show) rh+, toRowHdr = \_ i -> i+1+}++colHdr :: MixedColHdr Name Widths+colHdr = MixedColHdr {+ draw = \_ ci -> case colHdrs V.!? ci of+ Just ch -> withAttr columnHdrAttr (padRight Max (str ch) <+> str " ") <=> hBorder+ Nothing -> emptyWidget+, widths = \Widths {song} -> song+, height = 2+}++main :: IO ()+main = do+ let appState = AppState {+ libList = mixedTabularList TheList libraryEntries 1 cWprk wpr+ , libRenderers = MixedRenderers {+ drawCell = dc+ , rowHdr = Just rowHdr+ , colHdr = Just colHdr+ , drawColHdrRowHdr = Just $ \_ _ -> vLimit 1 (fill ' ') <=> hBorder+ }+ , listWidth = 80+ }+ app = App {+ appDraw = drawUi+ , appChooseCursor = neverShowCursor+ , appHandleEvent = handleLibEvent+ , appStartEvent = return ()+ , appAttrMap = const $ attrMap defAttr [ (listSelectedAttr, black `on` white)+ , (columnHdrAttr, fg blue)+ , (rowHdrAttr, fg red)]+ }+ void $ defaultMain app appState
− exec/GridTabularList.hs
@@ -1,21 +0,0 @@-module Main where--import Internal.GridTabularList-import Brick.Widgets.TabularList.Grid--main :: IO ()-main = do- let msgs = [ "Press Up arrow to go up one item"- , "Press Down arrow to go down one item"- , "Press PageUp to go up one page"- , "Press PageDown to go down one page"- , "Press Home to go to the beginning"- , "Press End to go to the end" - , " "- , "Press Left arrow to go left by one column"- , "Press Right arrow to go right by one column"- , "Press Ctrl+Home to go to the first column"- , "Press Ctrl+End to go to the last column"- , "Press Ctrl+PageUp to go left one page of columns"- , "Press Ctrl+PageDown to go right one page of columns" ]- runMain msgs handleGridListEvent
− exec/GridTabularListVi.hs
@@ -1,25 +0,0 @@-module Main where--import Internal.GridTabularList-import Brick.Widgets.TabularList.Grid--main :: IO ()-main = do- let msgs = [ "Press Up arrow or k to go up one item"- , "Press Down arrow or j to go down one item"- , "Press PageUp or Ctrl+f to go up one page"- , "Press PageDown or Ctrl+b to go down one page"- , "Press Home or g to go to the beginning"- , "Press End or G to go to the end" - , "Press Ctrl+u to go up half page"- , "Press Ctrl+d to go down half page"- , " "- , "Press Left arrow or h to go left by one column"- , "Press Right arrow or l to go right by one column"- , "Press Ctrl+Home or H to go to the first column"- , "Press Ctrl+End or L to go to the last column"- , "Press Ctrl+PageUp or Alt+h or Backspace to go left by one page of columns"- , "Press Ctrl+PageDown or Alt+l to go right by one page of columns" ]- runMain msgs $ \r e -> do- handleGridListEvent r e- handleGridListEventVi r e
− exec/MixedTabularList.hs
@@ -1,14 +0,0 @@-module Main where--import Internal.MixedTabularList-import Brick.Widgets.TabularList.Mixed--main :: IO ()-main = do- let msgs = [ "Press Up arrow to go up one item"- , "Press Down arrow to go down one item"- , "Press PageUp to go up one page"- , "Press PageDown to go down one page"- , "Press Home to go to the beginning"- , "Press End to go to the end" ]- runMain msgs handleMixedListEvent
− exec/MixedTabularListVi.hs
@@ -1,18 +0,0 @@-module Main where--import Internal.MixedTabularList-import Brick.Widgets.TabularList.Mixed--main :: IO ()-main = do- let msgs = [ "Press Up arrow or k to go up one item"- , "Press Down arrow or j to go down one item"- , "Press PageUp or Ctrl+b to go up one page"- , "Press PageDown or Ctrl+f to go down one page"- , "Press Home or g to go to the beginning"- , "Press End or G to go to the end"- , "Press Ctrl+u to go up half page"- , "Press Ctrl+d to go down half page" ]- runMain msgs $ \e -> do- handleMixedListEvent e- handleMixedListEventVi e
grid-tabular-list-01.png view
binary file changed (30139 → 29263 bytes)
grid-tabular-list-02.png view
binary file changed (30635 → 30032 bytes)
grid-tabular-list-03.png view
binary file changed (25044 → 24491 bytes)
mixed-tabular-list.png view
binary file changed (30007 → 27746 bytes)
src/Brick/Widgets/TabularList.hs view
@@ -4,61 +4,58 @@ import Brick.Widgets.TabularList.Types import Brick.Widgets.Center import Brick.Widgets.List+import Brick.Widgets.Core+import Data.Void --- * #SharedTraitsOfTabularListWidgets# Shared Traits of Tabular List Widgets------ $SharedTraitsOfTabularListWidgets--- A tabular list consists of cells, column headers, and row headers. Column headers and row headers are optional.--- 'WidthDeficit' is calculated for renderers of list elements that are shrunk to the available width.+-- * Type Variables #TypeVariables# ----- Widths for list elements are either fixed or calculated. Heights are always fixed.+-- $TypeVariables+-- * @__n__@ is the type of the resource name for the list. This is not for column headers because column headers are+-- above the list. Read [brick user guide](https://github.com/jtdaugherty/brick/blob/master/docs/guide.rst) for more+-- details.+-- * @__e__@ is the type of list elements which are also called list rows in tabular list widgets.+-- * @__w__@ is the type that contains widths per row kind. mixed-tabular-list demo program shows how to utilize it.+-- * @__r__@ is the type for row headers. If you don't want to show row headers, set this to @__()__@ or 'Void'.++-- * Rendering #Rendering# --+-- $Rendering -- Because tabular list widgets build on top of 'GenericList', attributes for 'GenericList' apply to tabular list -- widgets if the attributes are defined in your brick application. ----- It can handle a very large data set if you delete invisible rows from memory and fetch visible rows from a database--- (file). For example, SQLite database file can handle a large spreadsheet.---- * #Rendering# Rendering------ $Rendering--- A rendering function for cells or row headers is given a space with+-- A rendering function for row columns or row headers is given a space with ----- > setAvailableSize (width, listItemHeight)+-- @+-- 'setAvailableSize' (width, listItemHeight)+-- @ ----- A rendering function for column headers and column header row header is given a space with+-- A rendering function for column headers or column header row header is given a space with ----- > setAvailableSize (width, columnHeaderHeight)+-- @+-- 'setAvailableSize' (width, columnHeaderHeight)+-- @ -- -- If the given height for rendering functions is not claimed, the list will look broken. -- -- The given width for rendering functions must be claimed by the returned widget with brick's padding functions or -- 'hCenter'. Otherwise, the list will look broken. ----- The following examples show how a rendering function can claim the available width.------ > padRight (Pad $ if widthDeficit > 0 then 0 else 1) $ padLeft Max content+-- 'WidthDeficit' is calculated for renderers of tabular list components that are shrunk to the available width. ----- > padRight Max content <+> str " "+-- The following examples show how a rendering function can claim the available width. ----- > padLeft (Pad $ if widthDeficit > 0 then 0 else 1) $ hCenter content+-- @+-- 'padRight' ('Pad' $ if widthDeficit > 0 then 0 else 1) $ 'padLeft' 'Max' content+-- @ ----- In the examples above, I used padding with one character at the left or the right to introduce gaps between columns.--- If 'WidthDeficit' is positive, you may want to remove padding because the element that is being rendered is not--- followed or preceded by other columns.+-- @+-- 'padRight' 'Max' content '<+>' 'str' " "+-- @ ----- If row headers and column headers are drawn and the renderer for column header row header(drawColHdrRowHdr) doesn't--- exist, then column header row header is filled with empty space. The renderer for column header row header merely--- allows you to customize column header row header.---- * #ListTypeVariables# List Type Variables+-- @+-- 'padLeft' ('Pad' $ if widthDeficit > 0 then 0 else 1) $ 'hCenter' content+-- @ ----- $ListTypeVariables--- * @__n__@ is the type of the resource name for the list. This is not for column headers because column headers are--- above the list. Read [brick user guide](https://github.com/jtdaugherty/brick/blob/master/docs/guide.rst) for more--- details.--- * @__row__@ is the type of each row.--- * @__cell__@ is the type of columns of each row. If you want to differentiate different columns, @__cell__@ should--- have a data constructor for each type of column.--- * @__rowH__@ is the type of row header.--- * @__colH__@ is the type of column header.+-- In the examples above, I used padding with one space character at the left or the right to introduce gaps between+-- columns. If 'WidthDeficit' is positive, you may want to remove padding because a column is not followed or preceded+-- by other columns.
src/Brick/Widgets/TabularList/Grid.hs view
@@ -1,18 +1,14 @@ {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE OverloadedLabels #-}-{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE LambdaCase #-}-{-# LANGUAGE NoMonomorphismRestriction #-}-{-# LANGUAGE TupleSections #-}+{-# LANGUAGE NoFieldSelectors #-}+{-# LANGUAGE NamedFieldPuns #-} -- | Grid tabular list is a uniform grid that supports cell-by-cell navigation. -----   ------ Read [Shared Traits of Tabular List Widgets]("Brick.Widgets.TabularList#g:SharedTraitsOfTabularListWidgets")--- before reading further.+--    ----- Because this list is designed to show an arbitrary number of columns, horizontal scrolling is supported through+-- Because this list is designed to show arbitrary numbers of columns, horizontal scrolling is supported through -- cell-by-cell navigation. -- -- Grid tabular list tries to show the current column in the center. If it can't show the current column in the center,@@ -21,16 +17,16 @@ -- It should be fast enough to handle a large spreadsheet. It is also suitable for an interface to a database table. module Brick.Widgets.TabularList.Grid ( -- * Data types- GridContents(..)-, GridContext(..)+ GridContext(..)+, GridColHdr(..) , GridRenderers(..)-, GridSizes(..)+, GridWidths , GridTabularList(..) -- * List construction , gridTabularList -- * Rendering , renderGridTabularList--- * Column navigation+-- * Column navigation #ColumnNavigation# , gridMoveLeft , gridMoveRight , gridMoveTo@@ -53,7 +49,7 @@ import Data.Foldable (toList) import Control.Monad (unless) -- Third party libraries-import Optics.Core hiding (Empty)+import Optics.Core ( (&), (%), (%~), (.~), (^.) ) import qualified Data.Sequence as S import Data.Sequence (Seq(..)) import Data.Generics.Labels@@ -61,62 +57,59 @@ import qualified Brick.Widgets.List as L import Brick.Types import Brick.Widgets.Core-import Brick.Widgets.Center import Graphics.Vty (Event(..), Key(..), Modifier(..)) import Brick.Main (lookupViewport) --- | Functions for getting contents of grid tabular list elements.--- See [List Type Variables]("Brick.Widgets.TabularList#g:ListTypeVariables").-data GridContents n row cell rowH colH = GridContents {- cell :: row -> ColumnIndex -> Maybe cell-, rowHdr :: Maybe (row -> RowIndex -> Maybe rowH)-, colHdr :: Maybe (ColumnIndex -> Maybe colH)-} deriving Generic---- | Context information for grid cells+-- | Context for grid cells data GridContext = GridContext {- row :: Position -- ^ Position among rows-, col :: Position -- ^ Position among columns+ row :: FlatContext -- ^ Row context+, col :: FlatContext -- ^ Column context } deriving (Show, Generic) --- | Rendering functions for elements of grid tabular list. See+-- | Grid column header ----- * [List Type Variables]("Brick.Widgets.TabularList#g:ListTypeVariables")+-- * [Type Variables]("Brick.Widgets.TabularList#g:TypeVariables") -- * [Rendering]("Brick.Widgets.TabularList#g:Rendering")-data GridRenderers n row cell rowH colH = GridRenderers {- drawCell :: ListFocused -> WidthDeficit -> GridContext -> row -> Maybe cell -> Widget n-, drawRowHdr :: Maybe (ListFocused -> WidthDeficit -> Position -> row -> Maybe rowH -> Widget n)-, drawColHdr :: Maybe (ListFocused -> WidthDeficit -> Position -> Maybe colH -> Widget n)-, drawColHdrRowHdr :: Maybe (ListFocused -> WidthDeficit -> Widget n)+data GridColHdr n = GridColHdr {+ draw :: ListFocused -> WidthDeficit -> FlatContext -> Widget n+, height :: Height -- ^ Height for column headers and column header row header } deriving Generic --- | Sizes for elements of grid tabular list.--- See [List Type Variables]("Brick.Widgets.TabularList#g:ListTypeVariables").-data GridSizes rowH = GridSizes {- row :: Seq Width -- ^ Widths for column headers and row columns-, rowHdr :: Maybe (RowHeaderWidth rowH) -- ^ Width for row headers-, colHdr :: Maybe Height -- ^ Height for column headers+-- | Rendering functions for components of grid tabular list+--+-- * [Type Variables]("Brick.Widgets.TabularList#g:TypeVariables")+-- * [Rendering]("Brick.Widgets.TabularList#g:Rendering")+data GridRenderers n e r = GridRenderers {+ drawCell :: ListFocused -> WidthDeficit -> GridContext -> e -> Widget n+, rowHdr :: Maybe (RowHdr n e r)+, colHdr :: Maybe (GridColHdr n)+, drawColHdrRowHdr :: DrawColHdrRowHdr n } deriving Generic --- | See [List Type Variables]("Brick.Widgets.TabularList#g:ListTypeVariables").-data GridTabularList n row cell rowH colH = GridTabularList {- list :: L.GenericList n Seq row -- ^ The underlying primitive list that comes from brick.-, sizes :: GridSizes rowH-, contents :: GridContents n row cell rowH colH-, currentColumn :: ColumnIndex+-- | Widths for column headers and row columns.+type GridWidths = Seq Width++-- | * [Type Variables]("Brick.Widgets.TabularList#g:TypeVariables")+data GridTabularList n e = GridTabularList {+ -- | The underlying primitive list that comes from brick+ list :: L.GenericList n Seq e+, widths :: GridWidths+ -- | Manipulating this field directly is unsafe. Use+ -- [column navigation]("Brick.Widgets.TabularList.Grid#g:ColumnNavigation") functions to manipulate this. If you still+ -- want to manipulate this directly, create a function that manipulates it, and test the function properly.+, currentColumn :: Index } deriving Generic -- | Create a grid tabular list-gridTabularList :: n -- ^ The list name (must be unique)- -> Seq row -- ^ The initial list rows+gridTabularList+ :: n -- ^ The list name (must be unique)+ -> Seq e -- ^ The initial list elements -> ListItemHeight- -> GridSizes rowH- -> GridContents n row cell rowH colH- -> GridTabularList n row cell rowH colH-gridTabularList n rows h sizes contents = GridTabularList {+ -> GridWidths+ -> GridTabularList n e+gridTabularList n rows h widths = GridTabularList { list = L.list n rows h-, sizes = sizes-, contents = contents+, widths = widths , currentColumn = 0 } @@ -154,9 +147,9 @@ -- | Calculate visible columns with the width available for columns. It tries to show the current column in the center. -- If it can't show the current column in the center, the first column is shown at the left corner, or the last column -- is shown at the right corner.-visibleColumns :: GridTabularList n row cell rowH colH -> AvailWidth -> VisibleColumns+visibleColumns :: GridTabularList n e -> AvailWidth -> VisibleColumns visibleColumns l aW = let curCol = l ^. #currentColumn in- case S.splitAt curCol (l ^. #sizes % #row) of+ case S.splitAt curCol (l ^. #widths) of -- If current column is outside the boundary of row columns, return `NoColumn`. (_, Empty) -> NoColumn (left, cW :<| right) -> if aW <= 0@@ -165,7 +158,7 @@ -- If the available width is less than the current column's width, else if cW >= aW then CurrentColumn- -- Otherwise, calculate the leftmost visible column for the current column shown in the center.+ -- Otherwise, else let -- The amount of space to the left of the current column shown in the center. lW = (aW - cW) `div` 2@@ -206,17 +199,19 @@ else AnchoredRight { left = idx, offset = accW+w-aW, totalWidth = accW+w } -- If there aren't enough columns to fill the available width with the last column at the right corner, return -- 'AnchoredLeft' with the last column as the rightmost visible column.- leftForRight Empty _ _ = AnchoredLeft $ length (l ^. #sizes % #row) - 1+ leftForRight Empty _ _ = AnchoredLeft $ length (l ^. #widths) - 1+ -- Calculate the leftmost visible column for the current column shown in the center. in leftForMiddle left (curCol-1) 0 -renderColumns :: GridTabularList n row cell rowH colH+renderColumns+ :: GridTabularList n e -> VisibleColumns- -> (WidthDeficit -> ColumnIndex -> Width -> Widget n)+ -> (WidthDeficit -> Index -> Width -> Widget n)+ -> Height -> Widget n-renderColumns l vCs dC = Widget Greedy Fixed $ do+renderColumns l vCs dC h = Widget Greedy Fixed $ do c <- getContext- let cWs = l ^. #sizes % #row- iH = l ^. #list % #listItemHeight+ let cWs = l ^. #widths curCol = l ^. #currentColumn aW = c^^.availWidthL render $ case vCs of@@ -225,98 +220,93 @@ Nothing -> error $ "Current column, " <> show curCol <> " is outside the boundary of column widths." Just cW -> dC (max 0 $ cW - aW) curCol aW AnchoredLeft right -> hBox $ zipWith (dC 0) [0..] $ toList $ S.take (right+1) cWs- MiddleColumns {..} -> cropLeftBy offset $ setAvailableSize (totalWidth, iH) $+ MiddleColumns {left, right, offset, totalWidth} -> cropLeftBy offset $ setAvailableSize (totalWidth, h) $ hBox $ zipWith (dC 0) [left..] $ toList $ S.take (right-left+1) $ S.drop left cWs- AnchoredRight {..} -> cropLeftBy offset $ setAvailableSize (totalWidth, iH) $+ AnchoredRight {left, offset, totalWidth} -> cropLeftBy offset $ setAvailableSize (totalWidth, h) $ hBox $ zipWith (dC 0) [left..] $ toList $ S.drop left cWs -- | Render grid tabular list renderGridTabularList :: (Ord n, Show n)- => GridRenderers n row cell rowH colH -- ^ Renderers+ => GridRenderers n e r -> ListFocused- -> GridTabularList n row cell rowH colH -- ^ The list+ -> GridTabularList n e -- ^ The list -> Widget n renderGridTabularList r lf l = Widget Greedy Greedy $ do c <- getContext- let drawCell = r ^. #drawCell- cell = l ^. #contents % #cell- list = l ^. #list- curCol = l ^. #currentColumn- aW = c^^.availWidthL+ let aW = c^^.availWidthL aH = c^^.availHeightL- iH = list ^. #listItemHeight- wSet = setAvailableSize . (, iH)- colHdrRow vCs rhw' rhwd = case (l ^. #contents % #colHdr, l ^. #sizes % #colHdr, r ^. #drawColHdr) of- (Nothing, _, _) -> emptyWidget- (_, Nothing, _) -> emptyWidget- (_, _, Nothing) -> emptyWidget- (Just colH, Just colHdrH, Just dch) -> let- drawCol wd c w = setAvailableSize (w, colHdrH) $ dch lf wd (Position c (c == curCol)) $ colH c+ GridRenderers {drawCell} = r+ GridTabularList {list=l', currentColumn=curCol} = l+ iH = l' ^. #listItemHeight+ colHdrRow vCs rhw' rhwd = case r ^. #colHdr of+ Nothing -> emptyWidget+ Just (GridColHdr {draw, height}) -> let+ drawCol wd c w = setAvailableSize (w, height) $ draw lf wd (FlatContext c (c == curCol)) chrw = case r ^. #drawColHdrRowHdr of Nothing -> fill ' ' Just dchrw -> dchrw lf rhwd- in setAvailableSize (rhw', colHdrH) chrw <+> renderColumns l vCs drawCol+ in setAvailableSize (rhw', height) chrw <+> renderColumns l vCs drawCol height renderRow vCs i f r = let- drawCol wd c w = let gc = GridContext (Position i f) (Position c $ c == curCol)- in wSet w $ drawCell lf wd gc r $ cell r c- in renderColumns l vCs drawCol+ drawCol wd c w = let gc = GridContext (FlatContext i f) (FlatContext c $ c == curCol)+ in setAvailableSize (w, iH) $ drawCell lf wd gc r+ in renderColumns l vCs drawCol iH renderList = let vCs = visibleColumns l aW in- render $ colHdrRow vCs 0 0 <=> L.renderListWithIndex (renderRow vCs) lf list- renderHdrList rh rhw drh = let+ render $ colHdrRow vCs 0 0 <=> L.renderListWithIndex (renderRow vCs) lf l'+ renderHdrList (RowHdr {draw, width, toRowHdr}) = let+ rhw = width aW $ zipWithVisibleRowsAndIndexes l' aH toRowHdr rhw' = min rhw aW rhwd = max 0 $ rhw - aW vCs = visibleColumns l $ aW - rhw'- renderHdrRow i f row = wSet rhw' (drh lf rhwd (Position i f) row $ rh row i) <+> renderRow vCs i f row- in render $ colHdrRow vCs rhw' rhwd <=> L.renderListWithIndex renderHdrRow lf list- case (l ^. #contents % #rowHdr, l ^. #sizes % #rowHdr, r ^. #drawRowHdr) of- (Nothing, _, _) -> renderList- (_, Nothing, _) -> renderList- (_, _, Nothing) -> renderList- (Just rh, Just (FixedRowHeader w), Just drh) -> renderHdrList rh w drh- (Just rh, Just (AvailRowHeader w), Just drh) -> renderHdrList rh (w aW) drh- (Just rh, Just (VisibleRowHeaders w), Just drh) -> renderHdrList rh (w aW $ visibleRowHdrs list aH rh) drh+ renderHdrRow i f r = setAvailableSize (rhw', iH) (draw lf rhwd f $ toRowHdr r i) <+> renderRow vCs i f r+ in render $ colHdrRow vCs rhw' rhwd <=> L.renderListWithIndex renderHdrRow lf l'+ maybe renderList renderHdrList $ r ^. #rowHdr -- | Move to the left by one column.-gridMoveLeft :: GridTabularList n row cell rowH colH -- ^ The list- -> GridTabularList n row cell rowH colH+gridMoveLeft+ :: GridTabularList n e -- ^ The list+ -> GridTabularList n e gridMoveLeft gl = if null $ gl ^. #list % #listSelected then gl else gl & #currentColumn %~ max 0 . subtract 1 -- | Move to the right by one column.-gridMoveRight :: GridTabularList n row cell rowH colH -- ^ The list- -> GridTabularList n row cell rowH colH+gridMoveRight+ :: GridTabularList n e -- ^ The list+ -> GridTabularList n e gridMoveRight gl = if null $ gl ^. #list % #listSelected then gl- else gl & #currentColumn %~ min (length (gl ^. #sizes % #row) - 1) . (+1)+ else gl & #currentColumn %~ min (length (gl ^. #widths) - 1) . (+1) -- | Move to the given column index-gridMoveTo :: ColumnIndex- -> GridTabularList n row cell rowH colH -- ^ The list- -> GridTabularList n row cell rowH colH+gridMoveTo+ :: Index+ -> GridTabularList n e -- ^ The list+ -> GridTabularList n e gridMoveTo n gl = if null $ gl ^. #list % #listSelected then gl- else gl & #currentColumn .~ (max 0 . min (length (gl ^. #sizes % #row) - 1)) n+ else gl & #currentColumn .~ (max 0 . min (length (gl ^. #widths) - 1)) n -- | Move to the first column.-gridMoveToBeginning :: GridTabularList n row cell rowH colH -- ^ The list- -> GridTabularList n row cell rowH colH+gridMoveToBeginning+ :: GridTabularList n e -- ^ The list+ -> GridTabularList n e gridMoveToBeginning gl = if null $ gl ^. #list % #listSelected then gl else gl & #currentColumn .~ 0 -- | Move to the last column.-gridMoveToEnd :: GridTabularList n row cell rowH colH -- ^ The list- -> GridTabularList n row cell rowH colH +gridMoveToEnd+ :: GridTabularList n e -- ^ The list+ -> GridTabularList n e gridMoveToEnd gl = if null $ gl ^. #list % #listSelected then gl- else gl & #currentColumn .~ length (gl ^. #sizes % #row) - 1+ else gl & #currentColumn .~ length (gl ^. #widths) - 1 -- | 'GridRenderers' are needed because if row header renderer doesn't exist, width calculation is affected. gridMovePage :: Ord n- => GridRenderers n row cell rowH colH- -> (VisibleColumns -> EventM n (GridTabularList n row cell rowH colH) ())- -> EventM n (GridTabularList n row cell rowH colH) ()+ => GridRenderers n e r+ -> (VisibleColumns -> EventM n (GridTabularList n e) ())+ -> EventM n (GridTabularList n e) () gridMovePage r f = do l <- get unless (null $ l ^. #list % #listSelected) $ do@@ -325,39 +315,35 @@ Nothing -> return () Just vp -> let (aW, aH) = vp ^. #_vpSize- rhw = case (l ^. #contents % #rowHdr, l ^. #sizes % #rowHdr, r ^. #drawRowHdr) of- (Nothing, _, _) -> 0- (_, Nothing, _) -> 0- (_, _, Nothing) -> 0- (_, Just (FixedRowHeader w), _) -> w- (_, Just (AvailRowHeader w), _) -> w aW- (Just rowH, Just (VisibleRowHeaders w), _) -> w aW $ visibleRowHdrs (l ^. #list) aH rowH+ rhw = case r ^. #rowHdr of+ Nothing -> 0+ Just (RowHdr {width, toRowHdr}) -> width aW $ zipWithVisibleRowsAndIndexes (l ^. #list) aH toRowHdr in f $ visibleColumns l $ aW - rhw -- | Move to the previous page of columns. ----- 'GridRenderers' are needed because if row header renderer doesn't exist, width calculation is affected.+-- 'GridRenderers' are needed because if row header doesn't exist, width calculation is affected. gridMovePageUp :: Ord n- => GridRenderers n row cell rowH colH -- ^ Renderers- -> EventM n (GridTabularList n row cell rowH colH) ()+ => GridRenderers n e r -- ^ Renderers+ -> EventM n (GridTabularList n e) () gridMovePageUp r = gridMovePage r $ \case NoColumn -> return () CurrentColumn -> modify gridMoveLeft- AnchoredLeft {} -> modify gridMoveToBeginning- MiddleColumns {..} -> modify $ gridMoveTo left- AnchoredRight {..} -> modify $ gridMoveTo left+ AnchoredLeft _ -> modify gridMoveToBeginning+ MiddleColumns {left} -> modify $ gridMoveTo left+ AnchoredRight {left} -> modify $ gridMoveTo left -- | Move to the next page of columns. ----- 'GridRenderers' are needed because if row header renderer doesn't exist, width calculation is affected.+-- 'GridRenderers' are needed because if row header doesn't exist, width calculation is affected. gridMovePageDown :: Ord n- => GridRenderers n row cell rowH colH -- ^ Renderers- -> EventM n (GridTabularList n row cell rowH colH) ()+ => GridRenderers n e r -- ^ Renderers+ -> EventM n (GridTabularList n e) () gridMovePageDown r = gridMovePage r $ \case NoColumn -> return () CurrentColumn -> modify gridMoveRight- AnchoredLeft {..} -> modify $ gridMoveTo right- MiddleColumns {..} -> modify $ gridMoveTo right+ AnchoredLeft {right} -> modify $ gridMoveTo right+ MiddleColumns {right} -> modify $ gridMoveTo right AnchoredRight {} -> modify gridMoveToEnd -- | Handle events for grid tabular list with navigation keys.@@ -371,10 +357,10 @@ -- * Move to the previous page of columns (Ctrl+PageUp) -- * Move to the next page of columns (Ctrl+PageDown) ----- 'GridRenderers' are needed because if row header renderer doesn't exist, width calculation is affected.+-- 'GridRenderers' are needed because if row header doesn't exist, width calculation is affected. handleGridListEvent :: Ord n- => GridRenderers n row cell rowH colH -- ^ Renderers- -> Event -> EventM n (GridTabularList n row cell rowH colH) ()+ => GridRenderers n e r -- ^ Renderers+ -> Event -> EventM n (GridTabularList n e) () handleGridListEvent r e = case e of EvKey KLeft [] -> modify gridMoveLeft EvKey KRight [] -> modify gridMoveRight@@ -395,10 +381,10 @@ -- * Move to the previous page of columns (Alt+h) -- * Move to the next page of columns (Alt+l) ----- 'GridRenderers' are needed because if row header renderer doesn't exist, width calculation is affected.+-- 'GridRenderers' are needed because if row header doesn't exist, width calculation is affected. handleGridListEventVi :: Ord n- => GridRenderers n row cell rowH colH -- ^ Renderers- -> Event -> EventM n (GridTabularList n row cell rowH colH) ()+ => GridRenderers n e r -- ^ Renderers+ -> Event -> EventM n (GridTabularList n e) () handleGridListEventVi r e = case e of EvKey (KChar 'h') [] -> modify gridMoveLeft EvKey (KChar 'l') [] -> modify gridMoveRight
src/Brick/Widgets/TabularList/Internal/Common.hs view
@@ -1,7 +1,8 @@ module Brick.Widgets.TabularList.Internal.Common (- visibleRows-, visibleRowHdrs-, visibleRowsAndRowHdrs+ AvailHeight+, visibleRowsWithStart+, visibleRows+, zipWithVisibleRowsAndIndexes ) where import Brick.Widgets.TabularList.Types@@ -14,9 +15,11 @@ -- Brick import Brick.Widgets.List -type Start = Int+-- | Height available for 'GenericList' type AvailHeight = Int-visibleRowsWithStart :: GenericList n Seq row -> AvailHeight -> ([row], Start)++-- | Return visible rows and the index of the first visible row, given 'GenericList' and height available for list.+visibleRowsWithStart :: GenericList n Seq row -> AvailHeight -> ([row], Index) visibleRowsWithStart l aH = let idx = fromMaybe 0 (l^.listSelectedL) numPerHeight = case aH `divMod` (l^.listItemHeightL) of@@ -27,16 +30,11 @@ rows = toList $ slice start length $ l^.listElementsL in (rows, start) --- | Return visible rows, given 'GenericList' and height available for list elements-visibleRows :: GenericList n Seq row -> AvailHeight -> [row]-visibleRows l aH = visibleRowsWithStart l aH ^. _1---- | Return visible row headers, given 'GenericList' and height available for list elements-visibleRowHdrs :: GenericList n Seq row -> AvailHeight -> (row -> RowIndex -> Maybe rowH) -> [rowH]-visibleRowHdrs l aH rowH = visibleRowsAndRowHdrs l aH rowH ^. _2+-- | Return visible rows, given 'GenericList' and height available for list.+visibleRows :: GenericList n Seq e -> AvailHeight -> [e]+visibleRows l aH = fst $ visibleRowsWithStart l aH --- | Return visible rows and visible row headers, given 'GenericList', available height, and row header function.-visibleRowsAndRowHdrs :: GenericList n Seq row -> AvailHeight -> (row -> RowIndex -> Maybe rowH) -> ([row], [rowH])-visibleRowsAndRowHdrs l aH rowH = let- (rows, start) = visibleRowsWithStart l aH- in (rows, catMaybes $ zipWith rowH rows [start..])+-- | Zip visible rows and their row indexes with a function.+zipWithVisibleRowsAndIndexes :: GenericList n Seq e -> AvailHeight -> (e -> Index -> c) -> [c]+zipWithVisibleRowsAndIndexes l aH f = let (es, s) = visibleRowsWithStart l aH+ in zipWith f es [s..]
src/Brick/Widgets/TabularList/Mixed.hs view
@@ -1,15 +1,11 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE OverloadedLabels #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE BlockArguments #-}-{-# LANGUAGE TupleSections #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE NoFieldSelectors #-} -- | Mixed tabular list is a list with different kinds of rows. ----- ------ Read [Shared Traits of Tabular List Widgets]("Brick.Widgets.TabularList#g:SharedTraitsOfTabularListWidgets")--- before reading further.+--  -- -- Each row belongs to a row kind which is usually a data constructor of row data type. Because there can be more than -- one data constructor in row data type, this list is called mixed tabular list. Each row kind can have a different@@ -20,11 +16,11 @@ -- Because this list is designed to show every column in the available space, horizontal scrolling is not supported. module Brick.Widgets.TabularList.Mixed ( -- * Data types- MixedContents(..)+ MixedContext(..)+, MixedColHdr(..) , MixedRenderers(..)-, ColSizes(..)-, CalcColSizes(..)-, MixedSizes(..)+, CalcWidthsPerRowKind+, WidthsPerRow , MixedTabularList(..) -- * List construction , mixedTabularList@@ -46,140 +42,118 @@ -- Third party libraries import Data.Map.Strict (Map) import qualified Data.Map.Strict as Map-import Optics.Core+import Optics.Core ( (^.) ) import Data.Generics.Labels import Data.Sequence (Seq) -- Brick & Vty import qualified Brick.Widgets.List as L import Brick.Types import Brick.Widgets.Core-import Brick.Widgets.Center import Graphics.Vty.Input.Events (Event) --- | Functions for getting contents of mixed tabular list elements.--- See [List Type Variables]("Brick.Widgets.TabularList#g:ListTypeVariables").-data MixedContents row cell rowH colH = MixedContents {- cell :: row -> ColumnIndex -> Maybe cell -- ^ Function for obtaining a column of a row-, rowHdr :: Maybe (row -> RowIndex -> Maybe rowH) -- ^ Function for getting row header-, colHdr :: Maybe (ColumnIndex -> Maybe colH) -- ^ Function for getting column header-} deriving Generic+-- | Context for mixed columns+data MixedContext = MixedContext {+ row :: FlatContext -- ^ Row context+, col :: Index -- ^ Index of column+} --- | Rendering functions for elements of mixed tabular list. See+-- | Mixed column header ----- * [List Type Variables]("Brick.Widgets.TabularList#g:ListTypeVariables")+-- * [Type Variables]("Brick.Widgets.TabularList#g:TypeVariables") -- * [Rendering]("Brick.Widgets.TabularList#g:Rendering")-data MixedRenderers n row cell rowH colH = MixedRenderers {- drawCell :: ListFocused -> Position -> row -> Maybe cell -> Widget n-, drawRowHdr :: Maybe (ListFocused -> WidthDeficit -> Position -> row -> Maybe rowH -> Widget n)-, drawColHdr :: Maybe (ListFocused -> ColumnIndex -> Maybe colH -> Widget n)-, drawColHdrRowHdr :: Maybe (ListFocused -> WidthDeficit -> Widget n)+data MixedColHdr n w = MixedColHdr {+ draw :: ListFocused -> Index -> Widget n+ -- | A function for getting widths for column headers from the type that contains widths per row kind+, widths :: w -> [Width]+, height:: Height -- ^ Height for column headers and column header row header } deriving Generic --- | Column sizes calculated after row header width is calculated.--- See [List Type Variables]("Brick.Widgets.TabularList#g:ListTypeVariables").-data ColSizes row = ColSizes {- -- | Widths for each row kind. Use pattern matching to detect the kind of each row. Usually, a row kind is a data- -- constructor of row. The returned widths should be pre-calculated widths. If widths are calculated in this function,- -- width calculation will be repeated at every row. If the function is given pre-calculated widths, width calculation- -- is done only once.- rowKind :: row -> [Width]- -- | Widths and height for column headers. Widths and height don't have to be pre-calculated because column headers are- -- only drawn once. Because they are drawn once, any calculation done in this field is also done only once.-, colHdr :: Maybe ([Width], Height)+-- | Rendering functions for components of mixed tabular list+--+-- * [Type Variables]("Brick.Widgets.TabularList#g:TypeVariables")+-- * [Rendering]("Brick.Widgets.TabularList#g:Rendering")+data MixedRenderers n e w r = MixedRenderers {+ drawCell :: ListFocused -> MixedContext -> e -> Widget n+, rowHdr :: Maybe (RowHdr n e r)+, colHdr :: Maybe (MixedColHdr n w)+, drawColHdrRowHdr :: DrawColHdrRowHdr n } deriving Generic --- | Function for calculating column sizes.--- See [List Type Variables]("Brick.Widgets.TabularList#g:ListTypeVariables").-data CalcColSizes row =- -- | Calculate sizes with the width available after row header width is calculated.- AvailWidth (AvailWidth -> ColSizes row)- -- | Calculate sizes with visible rows and the width available after row header width is calculated.- | VisibleRows ([row] -> AvailWidth -> ColSizes row) deriving Generic+-- | Calculate widths per row kind from visible list rows and the width available after row header.+--+-- * [Type Variables]("Brick.Widgets.TabularList#g:TypeVariables")+type CalcWidthsPerRowKind e w = AvailWidth -> [e] -> w --- | Sizes for elements of mixed tabular list.--- See [List Type Variables]("Brick.Widgets.TabularList#g:ListTypeVariables").-data MixedSizes row rowH = MixedSizes {- rowHdr :: Maybe (RowHeaderWidth rowH) -- ^ Width for row headers-, colSizes :: CalcColSizes row -- ^ Function for calculating column sizes after row header width is calculated-} deriving Generic+-- | It is a function to get widths for each row. Use pattern matching to detect the kind of each row. Usually, a row+-- kind is a data constructor of row data type.+--+-- * [Type Variables]("Brick.Widgets.TabularList#g:TypeVariables")+type WidthsPerRow e w = w -> e -> [Width] --- | See [List Type Variables]("Brick.Widgets.TabularList#g:ListTypeVariables").-data MixedTabularList n row cell rowH colH = MixedTabularList {- list :: L.GenericList n Seq row -- ^ The underlying primitive list that comes from brick-, sizes :: MixedSizes row rowH-, contents :: MixedContents row cell rowH colH+-- | * [Type Variables]("Brick.Widgets.TabularList#g:TypeVariables")+data MixedTabularList n e w = MixedTabularList {+ list :: L.GenericList n Seq e -- ^ The underlying list that comes from brick+, calcWidthsPerRowKind :: CalcWidthsPerRowKind e w+, widthsPerRow :: WidthsPerRow e w } deriving Generic -- | Create a mixed tabular list. mixedTabularList :: n -- ^ The name of the list. It must be unique.- -> Seq row -- ^ The initial list rows+ -> Seq e -- ^ The initial list elements -> ListItemHeight- -> MixedSizes row rowH- -> MixedContents row cell rowH colH- -> MixedTabularList n row cell rowH colH-mixedTabularList n rows h sizes contents = MixedTabularList {+ -> CalcWidthsPerRowKind e w+ -> WidthsPerRow e w+ -> MixedTabularList n e w+mixedTabularList n rows h cWprk wpr = MixedTabularList { list = L.list n rows h-, sizes = sizes-, contents = contents+, calcWidthsPerRowKind = cWprk+, widthsPerRow = wpr } --- | Render mixed tabular list+-- | Render mixed tabular list. renderMixedTabularList :: (Show n, Ord n)- => MixedRenderers n row cell rowH colH -- ^ Renderers+ => MixedRenderers n e w r -- ^ Renderers -> ListFocused- -> MixedTabularList n row cell rowH colH -- ^ The list+ -> MixedTabularList n e w -- ^ The list -> Widget n renderMixedTabularList r lf l = Widget Greedy Greedy $ do c <- getContext- let drawCell = r ^. #drawCell- cell = l ^. #contents % #cell- aW = c^^.availWidthL+ let aW = c^^.availWidthL aH = c^^.availHeightL- iH = l ^. #list % #listItemHeight- wSet = setAvailableSize . (, iH)- colHdrRow sizes rhw' rhwd = case (l ^. #contents % #colHdr, sizes, r ^. #drawColHdr) of- (Nothing, _, _) -> emptyWidget- (_, Nothing, _) -> emptyWidget- (_, _, Nothing) -> emptyWidget- (Just colH, Just (colWs, colHdrH), Just dch) -> let- drawCol ci w = setAvailableSize (w, colHdrH) $ dch lf ci $ colH ci+ MixedRenderers {drawCell} = r+ MixedTabularList {list=l', calcWidthsPerRowKind=cWprk, widthsPerRow=wpr} = l+ iH = l' ^^. L.listItemHeightL+ colHdrRow wprk rhw' rhwd = case r ^. #colHdr of+ Nothing -> emptyWidget+ Just (MixedColHdr {draw, widths, height}) -> let+ drawCol ci w = setAvailableSize (w, height) $ draw lf ci chrw = case r ^. #drawColHdrRowHdr of Nothing -> fill ' ' Just dchrw -> dchrw lf rhwd- in setAvailableSize (rhw', colHdrH) chrw <+> hBox (zipWith drawCol [0..] colWs)+ in setAvailableSize (rhw', height) chrw <+> hBox (zipWith drawCol [0..] $ widths wprk) renderRow wprk i f row = let- drawColumn ci w = wSet w $ drawCell lf (Position i f) row $ cell row ci- in hBox $ zipWith drawColumn [0..] $ wprk row- sizesAfterRowHdr rhw rows = case l ^. #sizes % #colSizes of- AvailWidth wf -> wf $ aW - rhw- VisibleRows wf -> case rows of- Nothing -> wf (visibleRows (l ^. #list) aH) $ aW - rhw- Just rows -> wf rows $ aW - rhw- renderList = let ColSizes {..} = sizesAfterRowHdr 0 Nothing- in render $ colHdrRow colHdr 0 0 <=> L.renderListWithIndex (renderRow rowKind) lf (l ^. #list)- renderHdrList rh rhw drh rows = let+ drawColumn ci w = setAvailableSize (w, iH) $ drawCell lf (MixedContext (FlatContext i f) ci) row+ in hBox $ zipWith drawColumn [0..] $ wpr wprk row+ renderList = let wprk = cWprk aW $ visibleRows l' aH+ in render $ colHdrRow wprk 0 0 <=> L.renderListWithIndex (renderRow wprk) lf l'+ renderHdrList (RowHdr {draw, width, toRowHdr}) = let+ (es, s) = visibleRowsWithStart l' aH+ rhw = width aW $ zipWith toRowHdr es [s..] rhw' = min rhw aW rhwd = max 0 $ rhw - aW- ColSizes {..} = sizesAfterRowHdr rhw' rows- renderHdrRow i f r = wSet rhw' (drh lf rhwd (Position i f) r $ rh r i) <+> renderRow rowKind i f r- in render $ colHdrRow colHdr rhw' rhwd <=> L.renderListWithIndex renderHdrRow lf (l ^. #list)- case (l ^. #contents % #rowHdr, l ^. #sizes % #rowHdr, r ^. #drawRowHdr) of- (Nothing, _, _) -> renderList- (_, Nothing, _) -> renderList- (_, _, Nothing) -> renderList- (Just rh, Just (FixedRowHeader w), Just drh) -> renderHdrList rh w drh Nothing- (Just rh, Just (AvailRowHeader w), Just drh) -> renderHdrList rh (w aW) drh Nothing- (Just rh, Just (VisibleRowHeaders w), Just drh) -> let (rs, rHs) = visibleRowsAndRowHdrs (l ^. #list) aH rh- in renderHdrList rh (w aW rHs) drh (Just rs)+ wprk = cWprk (aW - rhw') es+ renderHdrRow i f r = setAvailableSize (rhw', iH) (draw lf rhwd f $ toRowHdr r i) <+> renderRow wprk i f r+ in render $ colHdrRow wprk rhw' rhwd <=> L.renderListWithIndex renderHdrRow lf l'+ maybe renderList renderHdrList $ r ^. #rowHdr -- | Handle events for mixed tabular list with navigation keys. This just calls 'L.handleListEvent'. handleMixedListEvent :: Ord n => Event -- ^ Event- -> EventM n (MixedTabularList n row cell rowH colH) ()+ -> EventM n (MixedTabularList n e w) () handleMixedListEvent e = zoom #list (L.handleListEvent e) -- | Handle events for mixed tabular list with vim keys. This just calls 'L.handleListEventVi'. handleMixedListEventVi :: Ord n => Event -- ^ Event- -> EventM n (MixedTabularList n row cell rowH colH) ()+ -> EventM n (MixedTabularList n e w) () handleMixedListEventVi e = zoom #list (L.handleListEventVi (\_ -> return ()) e)
src/Brick/Widgets/TabularList/Types.hs view
@@ -1,63 +1,78 @@ {-# LANGUAGE DeriveGeneric #-}--- | Types shared by tabular list widgets+{-# LANGUAGE NoFieldSelectors #-}+-- | Types shared by tabular list widgets. -- -- You don't have to import this module because modules for tabular list widgets re-export this module. module Brick.Widgets.TabularList.Types (- ColumnIndex-, RowIndex-, Index+ Index , Width , Height , AvailWidth , ListItemHeight-, Position(..)+, FlatContext(..) , WidthDeficit , ListFocused-, RowHeaderWidth(..)+, Selected+, RowHdr(..)+, DrawColHdrRowHdr ) where -- base import GHC.Generics (Generic)---- Types used by tabular list widgets+-- brick+import Brick.Types --- | Index of column among columns-type ColumnIndex = Int--- | Index of row among rows-type RowIndex = Int--- | Index of element among elements+-- | Index of a tabular list component among the same kind of components type Index = Int--- | Width of a list element++-- | Width of a tabular list component type Width = Int--- | Height of a list element++-- | Height of a tabular list component type Height = Int+ -- | Available width type AvailWidth = Int--- | The fixed height for row headers and cells.++-- | The fixed height for row headers and row columns. ----- If the height of row headers or cells is not this height, then the list will look broken.+-- If the height of row headers or row columns is not this height, then the list will look broken. type ListItemHeight = Int--- | Linear position for tabular list elements.-data Position = Position {- index :: Index -- ^ Index of item-, selected :: Bool -- ^ Whether or not the item is selected++-- | Context for one dimensional tabular list components+data FlatContext = FlatContext {+ index :: Index -- ^ Index of a component+, selected :: Bool -- ^ Whether a component is selected } deriving (Show, Generic)+ -- | > widthDeficit = max 0 $ desiredWidth - availableWidth ----- It is positive when an element is shrunk to the available width.+-- It is positive when a column is shrunk to the available width. -- -- If it is positive, you may want to remove paddings in your content because it is not followed or preceded by -- other columns. type WidthDeficit = Int+ -- | Whether the list is focused in an application type ListFocused = Bool --- | Row header width information-data RowHeaderWidth rowH =- -- | A fixed width for row header.- FixedRowHeader Width- -- | Calculate row header width with the width available for each row of a list- | AvailRowHeader (AvailWidth -> Width)- -- | Calculate row header width with visible row headers and the width available for each row of a list.- | VisibleRowHeaders (AvailWidth -> [rowH] -> Width) - deriving Generic+-- | Whether a tabular list component is selected+type Selected = Bool++-- | Row header+--+-- * [Type Variables]("Brick.Widgets.TabularList#g:TypeVariables")+-- * [Rendering]("Brick.Widgets.TabularList#g:Rendering")+data RowHdr n e r = RowHdr {+ draw :: ListFocused -> WidthDeficit -> Selected -> r -> Widget n+ -- | Calculate row header width from visible row headers and the width available for a list row.+, width :: AvailWidth -> [r] -> Width+ -- | Get a row header from a list row and row index.+, toRowHdr :: e -> Index -> r+} deriving Generic++-- | The renderer for column header row header.+--+-- If row headers and column headers exist and 'DrawColHdrRowHdr' is 'Nothing', then column header row header is+-- filled with empty space. 'DrawColHdrRowHdr' merely allows you to customize column header row header.+type DrawColHdrRowHdr n = Maybe (ListFocused -> WidthDeficit -> Widget n)