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 +4/−0
- brick-tabular-list.cabal +37/−30
- demo/Internal/GridTabularList.hs +182/−0
- demo/Internal/MixedTabularList.hs +176/−0
- demos/GridTabularList.hs +0/−21
- demos/GridTabularListVi.hs +0/−25
- demos/Internal/GridTabularList.hs +0/−182
- demos/Internal/MixedTabularList.hs +0/−176
- demos/MixedTabularList.hs +0/−14
- demos/MixedTabularListVi.hs +0/−18
- exec/GridTabularList.hs +21/−0
- exec/GridTabularListVi.hs +25/−0
- exec/MixedTabularList.hs +14/−0
- exec/MixedTabularListVi.hs +18/−0
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