packages feed

brick-tabular-list-2.2.0.0: demo/GridTabularList.hs

{-# 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

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) (LstFcs 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"

rowHdr :: RowHdr Name Song
rowHdr = RowHdr {
  draw = \_ (WdthD wd) (RowHdrCtxt (Sel s)) r -> let
    attrFn = if s
      then id
      else withAttr rowHdrAttr
    in attrFn $ padRight (Pad $ if wd > 0 then 0 else 1) $ padLeft Max (str $ show r)
, width = \_ rs -> RowHdrW $ (+2) $ maximum $ map (length . show) rs
, toRH = \_ (Ix i) -> i+1
}

colHdrs :: Vector String
colHdrs = V.fromList ["Artist", "Title", "Album", "Composer", "Genre", "Time"]

colHdr :: GridColHdr Name
colHdr = GridColHdr {
  draw = \_ (WdthD wd) (GColC (Ix ci) _) -> let
    renderColH = withAttr columnHdrAttr . padRight (Pad $ if wd > 0 then 0 else 1) . padRight Max . str
    in case colHdrs V.!? ci of
      Just ch -> renderColH ch <=> hBorder
      Nothing -> emptyWidget
, height = ColHdrH 2
}

renderers :: LibraryRenderers
renderers = GridRenderers {
  cell = \_ (WdthD wd) (GrdCtxt (GRowC _ (Sel rs)) (GColC (Ix ci) (Sel 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
, colHdrRowHdr = Just $ ColHdrRowHdr $ \_ _ -> vLimit 1 (fill ' ') <=> hBorder
}

theList :: LibraryList
theList = gridTabularList TheList songs (LstItmH 1) $ S.fromList $ fmap ColW [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