packages feed

vty-ui-0.1: src/Demo.hs

module Main where

import Data.Maybe ( fromJust )
import Control.Applicative ( (<$>) )
import Control.Monad.Trans ( liftIO )
import Control.Monad.State ( StateT, put, get, gets, evalStateT )

import Graphics.Vty.Widgets.Base
import Graphics.Vty.Widgets.List
import Graphics.Vty

titleAttr :: Attr
titleAttr = def_attr
            `with_back_color` blue
            `with_fore_color` bright_white

bodyAttr :: Attr
bodyAttr = def_attr
           `with_back_color` black
           `with_fore_color` bright_green

selAttr :: Attr
selAttr = def_attr
           `with_back_color` yellow
           `with_fore_color` black

-- Construct the user interface based on the contents of the
-- application state.
buildUi :: StateT AppState IO VBox
buildUi = do
  list <- gets theList
  msgs <- gets theMessages
  let body = fromJust $ lookup (getSelected list) msgs
      ui = list
           <--> hFill titleAttr '-' 1
           <--> text bodyAttr body
           <--> vFill bodyAttr ' '
           <--> footer
      footer = text titleAttr "- Status "
               <++> hFill titleAttr '-' 1

  return ui

-- The application state; this encapsulates what can vary based on
-- user input and what is used to construct the interface.  This is a
-- place for widgets whose state need to be stored so they can be
-- modified and used to reconstruct the interface as input is handled
data AppState = AppState { theList :: List
                         , theMessages :: [(String, String)]
                         }

-- Process events from VTY, possibly modifying the application state.
eventloop :: Vty -> StateT AppState IO ()
eventloop vty = do
  w <- buildUi
  evt <- liftIO $ do
                  pic_for_image <$> mkImage vty w >>= update vty
                  next_event vty
  case evt of
    -- If we got an up or down arrow key, modify the app state (list
    -- widget) and continue processing events.
    EvKey KUp [] -> do
                  appst <- get
                  put (appst { theList = scrollUp $ theList appst })
                  eventloop vty
    EvKey KDown [] -> do
                  appst <- get
                  put (appst { theList = scrollDown $ theList appst })
                  eventloop vty

    -- If we get 'q', quit.
    EvKey (KASCII 'q') [] -> return ()

    -- Any other key means keep looping (including terminal resize).
    _ -> eventloop vty

-- Construct the application state using the message map.
mkAppState :: [(String, String)] -> AppState
mkAppState messages =
    let list = mkList bodyAttr selAttr 3 $ map fst messages
    in AppState { theList = list
                , theMessages = messages
                }

main :: IO ()
main = do
  vty <- mkVty

  -- The data that we'll present in the interface.
  let messages = [ ("First", "the first message")
                 , ("Second", "the second message")
                 , ("Third", "the third message")
                 , ("Fourth", "the fourth message")
                 , ("Fifth", "the fifth message")
                 , ("Sixth", "the sixth message")
                 , ("Seventh", "the seventh message")
                 ]

  evalStateT (eventloop vty) $ mkAppState messages
  -- Clear the screen.
  reserve_display $ terminal vty
  shutdown vty