brick-tabular-list-0.1.0.0: demos/Internal/GridTabularList.hs
{-# 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 -> hCenter $ str " "
Just rh ->
let attrFn = if f
then id
else withAttr rowHdrAttr
rp = if wd > 0 then 0 else 1
in attrFn $ padRight (Pad rp) $ padLeft Max (str $ show rh)
dch lf wd (Position i f) = \case
Nothing -> hCenter $ str " "
Just ch -> let rp = if wd > 0 then 0 else 1 in
withAttr columnHdrAttr $ padRight (Pad rp) $ 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 -> hCenter $ str " "
Just (StringCell s) -> let rp = if wd > 0 then 0 else 1 in
padRight (Pad rp) $ padRight Max (str s)
Just (TimeCell time) -> let
(min, sec) = time `divMod` 60
time' = case min of
0 -> show sec
_ -> show min <> ":" <> show sec
rp = if wd > 0 then 0 else 1
in padRight (Pad rp) $ padRight Max $ str time'
, drawRowHdr = Just drh
, drawColHdr = Just dch
}
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