packages feed

brick-list-search-0.1.2.0: demos/BrickListSearch.hs

{-# LANGUAGE LambdaCase #-}
module Main where

import Brick.Widgets.List.Search
-- base
import Control.Monad (void)
-- Third party libraries
import Lens.Micro
import Data.Vector hiding ((++), map, modify)
-- brick & vty
import Brick.Main
import Brick.AttrMap
import Brick.Types
import Brick.Widgets.Core
import Brick.Util
import Brick.Widgets.Border
import Brick.Widgets.List
import Brick.Widgets.Center
import Graphics.Vty (defAttr, Event(..), Key(..), Modifier(..), black, white)

data Name = TheList deriving (Eq, Ord, Show)

data ListElem = Sep (Maybe String) | Str String deriving Show

isItem :: ListElem -> Bool
isItem = \case
  Str _ -> True
  Sep _ -> False

handleEvent :: BrickEvent Name () -> EventM Name (GenericList Name Vector ListElem) ()
handleEvent e = case e of
  VtyEvent (EvKey KEsc []) -> halt
  VtyEvent (EvKey (KChar 'q') []) -> halt
  VtyEvent (EvKey KUp []) -> do
    modify $ listSearchUp isItem
    listShowTheTop isItem
  VtyEvent (EvKey KDown []) -> do
    modify $ listSearchDown isItem
    listShowTheBottom isItem
  VtyEvent (EvKey KHome []) -> do
    modify $ listSearchFromBeginning isItem
    listShowTheTop isItem
  VtyEvent (EvKey KEnd []) -> do
    modify $ listSearchFromEnd isItem
    listShowTheBottom isItem
  VtyEvent (EvKey KPageUp []) -> do
    listSearchPageUp isItem
    listShowTheTop isItem
  VtyEvent (EvKey KPageDown []) -> do
    listSearchPageDown isItem
    listShowTheBottom isItem
  VtyEvent (EvKey (KChar 'u') [MCtrl]) -> do
    listSearchByPages isItem (-0.5)
    listShowTheTop isItem
  VtyEvent (EvKey (KChar 'd') [MCtrl]) -> do
    listSearchByPages isItem 0.5
    listShowTheBottom isItem
  VtyEvent (EvKey KUp [MCtrl]) -> do
    modify $ listSearchBy isItem (-2)
    listShowTheTop isItem
  VtyEvent (EvKey KDown [MCtrl]) -> do
    modify $ listSearchBy isItem 2
    listShowTheBottom isItem
  _ -> return ()

renderListElement :: Bool -> ListElem -> Widget Name
renderListElement lf e = case e of
  Str s -> padRight Max $ str s
  Sep Nothing -> hBorder
  Sep (Just title) -> hBorderWithLabel $ str title

drawUi :: GenericList Name Vector ListElem -> [Widget Name]
drawUi l = let msgs = [ "Press q or Esc to quit"
                      , "Press Up arrow to move up"
                      , "Press Down arrow to move down"
                      , "Press Home to move to the beginning"
                      , "Press End to move to the end"
                      , "Press PageUp to move up one page"
                      , "Press PageDown to move down one page"
                      , "Press Ctrl+u to move up half page"
                      , "Press Ctrl+d to move down half page"
                      , "Press Ctrl+Up arrow to move up two items"
                      , "Press Ctrl+Down arrow to move down two items"]
  in [vCenter $ vBox $
      hCenter (border $ vLimit 20 $ hLimit 30 $ renderList renderListElement True l) : map (hCenter . str) msgs]

listElems :: Vector ListElem
listElems = fromList $
  [Sep (Just "First separator"), Str "New account", Sep (Just "Accounts")]
  ++ map (Str . ("Account "<>) . show) [1..1000]
  ++ [Sep Nothing, Str "Show account statistics.", Sep (Just "Yet Another Separator"), Str "Last list element",
      Sep (Just "Last separator")]

theList :: GenericList Name Vector ListElem
theList = list TheList listElems 1

main :: IO ()
main = do
  let app = App {
    appDraw = drawUi
  , appChooseCursor = neverShowCursor
  , appHandleEvent = handleEvent
  -- If the first element is a separator, searching forward for an item is necessary.
  , appStartEvent = modify $ \l -> case searchListForward True isItem l of
      Nothing -> l & listSelectedL .~ Nothing
      Just idx -> l & listSelectedL ?~ idx
  , appAttrMap = const $ attrMap defAttr [(listSelectedAttr, black `on` white)]
  }
  void $ defaultMain app theList