packages feed

vty-ui-1.0: src/ListDemo.hs

{-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-unused-do-bind #-}
module Main where

import System.Exit ( exitSuccess )
import Graphics.Vty
import Graphics.Vty.Widgets.All

data AppElements =
    AppElements { theList :: Widget (List String FormattedText)
                , theBody :: Widget FormattedText
                , theFooter1 :: Widget FormattedText
                , theFooter2 :: Widget FormattedText
                , theEdit :: Widget Edit
                , theListLimit :: Widget (VLimit (List String FormattedText))
                , uis :: Collection
                }

-- Visual attributes.
titleAttr = bright_white `on` blue
editAttr = white `on` black
focAttr = black `on` green
boxAttr = bright_yellow `on` black
bodyAttr = bright_green `on` black
selAttr = black `on` yellow
hlAttr1 = red `on` black
hlAttr2 = yellow `on` black

uiCore appst w = do
  (hBorder >>= withBorderAttribute titleAttr)
      <--> w
      <--> (hBorder >>= withBorderAttribute titleAttr)
      <--> (return $ theEdit appst)
      <--> ((return $ theFooter1 appst)
            <++> (return $ theFooter2 appst)
            <++> (hBorder >>= withBorderAttribute titleAttr))

buildUi1 appst = do
  uiCore appst (return $ theList appst)

buildUi2 appst =
    uiCore appst ((return $ theListLimit appst)
                  <--> (hBorder >>= withBorderAttribute titleAttr)
                  <--> (return $ theBody appst)
                  <--> (vFill ' '))

-- Construct the application state using the message map.
mkAppElements :: IO AppElements
mkAppElements = do
  lw <- newStringList selAttr []
  b <- textWidget wrap ""
  f1 <- plainText "" >>= withNormalAttribute titleAttr
  f2 <- plainText "[]" >>= withNormalAttribute titleAttr
  e <- editWidget
  ll <- vLimit 5 lw

  c <- newCollection

  return $ AppElements { theList = lw
                       , theBody = b
                       , theFooter1 = f1
                       , theFooter2 = f2
                       , theEdit = e
                       , theListLimit = ll
                       , uis = c
                       }

updateBody :: AppElements -> Int -> IO ()
updateBody st i = do
  let msg = "This is the text for list entry " ++ (show $ i + 1)
  setText (theBody st) msg

updateFooterNums :: AppElements -> Widget (List a b) -> IO ()
updateFooterNums st w = do
  result <- getSelected w
  sz <- getListSize w
  let msg = case result of
              Nothing -> "--/--"
              Just (i, _) ->
                  "-" ++ (show $ i + 1) ++ "/" ++
                          (show sz) ++ "-"
  setText (theFooter1 st) msg

updateFooterText :: AppElements -> Widget Edit -> String -> IO ()
updateFooterText st _ t = setText (theFooter2 st) ("[" ++ t ++ "]")

main :: IO ()
main = do
  st <- mkAppElements

  ui1 <- buildUi1 st
  ui2 <- buildUi2 st

  fg1 <- newFocusGroup
  fg2 <- newFocusGroup

  showMainUI <- addToCollection (uis st) ui1 fg1
  showMessageUI <- addToCollection (uis st) ui2 fg2

  listCtx1 <- addToFocusGroup fg1 (theList st)
  addToFocusGroup fg1 (theEdit st)

  listCtx2 <- addToFocusGroup fg2 (theList st)
  addToFocusGroup fg2 (theEdit st)

  -- These event handlers will fire regardless of the input event
  -- context.
  (theEdit st) `onChange` (updateFooterText st (theEdit st))
  (theEdit st) `onActivate` \e -> do
         addToList (theList st) =<< getEditText e
         setEditText e ""

  let doBodyUpdate (SelectionOn i _ _) = updateBody st i
      doBodyUpdate SelectionOff = return ()

  (theList st) `onSelectionChange` doBodyUpdate
  (theList st) `onSelectionChange` \_ -> updateFooterNums st $ theList st
  (theList st) `onItemAdded` \_ -> updateFooterNums st $ theList st
  (theList st) `onItemRemoved` \_ -> updateFooterNums st $ theList st

  (theList st) `onKeyPressed` \_ k _ -> do
         case k of
           (KASCII 'q') -> exitSuccess
           KDel -> do
                  result <- getSelected (theList st)
                  case result of
                    Nothing -> return ()
                    Just (i, _) -> removeFromList (theList st) i >> return ()
                  return True
           _ -> return False

  -- These event handlers will only fire when the UI is in the
  -- appropriate mode, depending on the state of the Widget
  -- Collection.
  listCtx1 `onKeyPressed` \_ k _ -> do
            case k of
              KEnter -> do
                     r <- getSelected (theList st)
                     case r of
                       Nothing -> return True
                       Just _ -> showMessageUI >> return True
              _ -> return False

  listCtx2 `onKeyPressed` \_ k _ -> do
         case k of
           KASCII 'c' -> showMainUI >> return True
           KASCII '+' -> do
                  addToVLimit (theListLimit st) 1
                  return True
           KASCII '-' -> do
                  addToVLimit (theListLimit st) (-1)
                  return True
           _ -> return False

  setEditText (theEdit st) "edit me"

  -- We need to call these handlers manually because while they will
  -- be called automatically as items are added to the list in the
  -- future, the items currently in the list didn't call these because
  -- they weren't registered at the time the items were added.  And
  -- that was impossible because the list was created and populated
  -- before we even got a reference to it, so we couldn't have set up
  -- event handlers.
  updateFooterNums st (theList st)

  -- Enter the event loop.
  runUi (uis st) $ defaultContext { normalAttr = bodyAttr
                                  , focusAttr = focAttr
                                  }