packages feed

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 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. ----- ![ ](grid-tabular-list-01.png) ![ ](grid-tabular-list-02.png) ![ ](grid-tabular-list-03.png)------ Read [Shared Traits of Tabular List Widgets]("Brick.Widgets.TabularList#g:SharedTraitsOfTabularListWidgets")--- before reading further.+-- ![demo-01](grid-tabular-list-01.png) ![demo-02](grid-tabular-list-02.png) ![demo-03](grid-tabular-list-03.png) ----- 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. ----- ![ ](mixed-tabular-list.png)------ Read [Shared Traits of Tabular List Widgets]("Brick.Widgets.TabularList#g:SharedTraitsOfTabularListWidgets")--- before reading further.+-- ![demo](mixed-tabular-list.png) -- -- 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)