packages feed

brick-tabular-list 0.2.0.0 → 0.2.0.1

raw patch · 14 files changed

+477/−466 lines, 14 filesdep ~basedep ~brickdep ~containersPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base, brick, containers, optics-core, vty

API changes (from Hackage documentation)

Files

CHANGELOG.md view
@@ -1,5 +1,9 @@ # Revision history for brick-tabular-list +## 0.2.0.1 -- 2023-02-27++* Fixed demo programs for haskell-language-server+ ## 0.2.0.0 -- 2023-02-20  * Add drawColHdrRowHdr
brick-tabular-list.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: brick-tabular-list-version: 0.2.0.0+version: 0.2.0.1  synopsis: Tabular list widgets for brick. @@ -13,7 +13,7 @@     == To get started      * Read "Brick.Widgets.TabularList.Grid" or "Brick.Widgets.TabularList.Mixed".-    * Run demo programs in demos/ directory. To learn more quickly, modify and run demo programs.+    * Run demo programs. To learn more quickly, modify and run demo programs.      == Lens support @@ -39,30 +39,19 @@     type: git     location: https://codeberg.org/amano.kenji/brick-tabular-list.git -Flag demos+Flag demo     Description: Build demonstration programs     Default: False -common all+library     build-depends:         base >=4.15.1.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-    default-language: Haskell2010--common exec-    import: all-    build-depends: brick-tabular-list-    if !flag(demos)-      buildable: False-    hs-source-dirs: demos-    ghc-options: -threaded--library-    import: all     exposed-modules:         Brick.Widgets.TabularList       , Brick.Widgets.TabularList.Grid@@ -71,30 +60,48 @@     other-modules:         Brick.Widgets.TabularList.Internal.Common       , Brick.Widgets.TabularList.Internal.Lens-    build-depends:-        generic-lens ^>=2.2.1     hs-source-dirs: src+    default-language: Haskell2010 -common mixed-tabular-list-    import: exec-    other-modules: Internal.MixedTabularList+library demo+    if !flag(demo)+      buildable: False+    build-depends:+        base+      , brick+      , brick-tabular-list+      , containers+      , optics-core+      , 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: mixed-tabular-list-    main-is:          MixedTabularList.hs+    import: exec+    main-is: MixedTabularList.hs  executable mixed-tabular-list-vi-    import: mixed-tabular-list-    main-is:          MixedTabularListVi.hs--common grid-tabular-list     import: exec-    other-modules: Internal.GridTabularList+    main-is: MixedTabularListVi.hs  executable grid-tabular-list-    import: grid-tabular-list+    import: exec     main-is: GridTabularList.hs  executable grid-tabular-list-vi-    import: grid-tabular-list+    import: exec     main-is: GridTabularListVi.hs
+ demo/Internal/GridTabularList.hs view
@@ -0,0 +1,182 @@+{-# 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 view
@@ -0,0 +1,176 @@+{-# 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
− demos/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
− demos/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
− demos/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
− demos/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
− demos/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
− demos/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
+ exec/GridTabularList.hs view
@@ -0,0 +1,21 @@+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 view
@@ -0,0 +1,25 @@+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 view
@@ -0,0 +1,14 @@+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 view
@@ -0,0 +1,18 @@+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