vty-ui-1.5: 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
, theFooter :: Widget FormattedText
, theListLimit :: Widget (VLimit (List String FormattedText))
, uis :: Collection
}
titleAttr = bright_white `on` blue
focAttr = black `on` green
bodyAttr = white `on` black
selAttr = black `on` yellow
keyAttr = fgColor magenta
message1 :: String
message1 = "This demonstration shows how list widgets behave. \n\
\See the keystrokes below to try the demo."
message2 :: [(String, Attr)]
message2 = [ ("- Press ", def_attr), ("q", keyAttr), (" to quit\n", def_attr)
, ("- Press ", def_attr), ("+", keyAttr)
, (" / ", def_attr), ("a", keyAttr)
, (" to add a list item\n", def_attr)
, ("- Press ", def_attr), ("-", keyAttr)
, (" / ", def_attr), ("d", keyAttr)
, (" to remove the selected list item\n", def_attr)
, ("- Press ", def_attr)
, ("up", keyAttr), (" / ", def_attr)
, ("down", keyAttr), (" / ", def_attr)
, ("page up", keyAttr), (" / ", def_attr)
, ("page down", keyAttr)
, (" to navigate the list\n", def_attr)
]
buildUi appst = do
msg1 <- plainText message1
setTextFormatter msg1 wrap
msg2 <- plainTextWithAttrs message2
setTextFormatter msg2 wrap
mainUi <- (hBorder >>= withBorderAttribute titleAttr)
<--> (return $ theList appst)
<--> ((return $ theFooter appst)
<++> (hBorder >>= withBorderAttribute titleAttr))
centered =<< hLimit 55 =<<
((return msg1)
<--> ((vLimit 25 mainUi >>= bordered)
<--> return msg2 >>= withBoxSpacing 1)
>>= withBoxSpacing 1)
-- Construct the application statea using the message map.
mkAppElements :: IO AppElements
mkAppElements = do
lw <- newStringList selAttr []
b <- textWidget wrap ""
ft <- plainText "" >>= withNormalAttribute titleAttr
ll <- vLimit 5 lw
c <- newCollection
return $ AppElements { theList = lw
, theBody = b
, theFooter = ft
, 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 -> "0/0"
Just (i, _) -> (show $ i + 1) ++ "/" ++ (show sz)
setText (theFooter st) msg
main :: IO ()
main = do
st <- mkAppElements
ui <- buildUi st
fg <- newFocusGroup
_ <- addToCollection (uis st) ui fg
_ <- addToFocusGroup fg (theList st)
(theList st) `onSelectionChange` \_ -> updateFooterNums st $ theList st
(theList st) `onItemAdded` \_ -> updateFooterNums st $ theList st
(theList st) `onItemRemoved` \_ -> updateFooterNums st $ theList st
let removeCurrentItem = do
result <- getSelected (theList st)
case result of
Nothing -> return ()
Just (i, _) -> removeFromList (theList st) i >> return ()
addNewItem = do
addToList (theList st) "unused" =<< plainText "a list item"
(theList st) `onKeyPressed` \_ k _ -> do
case k of
(KASCII 'q') -> exitSuccess
(KASCII '-') -> removeCurrentItem >> return True
(KASCII 'd') -> removeCurrentItem >> return True
(KASCII '+') -> addNewItem >> return True
(KASCII 'a') -> addNewItem >> return True
_ -> return False
-- 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
}