{-# LANGUAGE LambdaCase #-} {-# LANGUAGE RecordWildCards #-} module System.Console.ListPrompt ( Color , ListPromptOptions(..) , simpleListPrompt -- Reexports , Default(..) ) where import Control.Monad (forM_) import Data.Default (Default (..), def) import System.Console.ANSI import System.IO (BufferMode (..), stdin) -- Internal imports import System.Console.ListPrompt.Internal import System.Console.ListPrompt.Types simpleListPrompt :: ListPromptOptions -> Choices -> IO String simpleListPrompt options choices = setup $ do dimensions <- getDimensionsIO numChoices selection <- waitForSelection dimensions 0 setSGR [] clearScreen setCursorPosition 0 0 return selection where setup = withNoBuffering stdin NoBuffering . withNoCursor . withNoEcho numChoices = length choices waitForSelection dimensions currentIdx = do clearScreen renderListOptions options dimensions choices currentIdx i <- getChar case i of '\n' -> return $ choices !! currentIdx 'j' -> waitForSelection dimensions ((currentIdx + 1) `rem` numChoices) 'k' -> waitForSelection dimensions currentIdx' where currentIdx' = if currentIdx == 0 then length choices - 1 else currentIdx - 1 _ -> waitForSelection dimensions currentIdx renderListOptions :: ListPromptOptions -> ListPromptDimensions -> Choices -> Int -- ^ The current selected item's index -> IO () renderListOptions options dimensions choices currentIdx = do clearScreen forM_ [0..2] $ drawLine options dimensions forM_ (zip [2..] choices) $ \(i, t) -> drawTextLine options dimensions i t (i-2 == currentIdx) forM_ [len + 2..len + 3] $ drawLine options dimensions where len = length choices -- | -- Draws an empty list line on a said position drawLine :: ListPromptOptions -> ListPromptDimensions -> Int -> IO () drawLine options dimensions@ListPromptDimensions{..} n = do let (_, w) = listPromptSize drawTextLine options dimensions n (replicate w ' ') False -- | -- Draws a string on a list line drawTextLine :: ListPromptOptions -> ListPromptDimensions -> Int -- ^ The list item we are currently on -> String -- ^ The text to print -> Bool -- ^ Is the currently selected item -> IO () drawTextLine ListPromptOptions{..} ListPromptDimensions{..} n str selected = do setSGR $ if selected then selectedItemSGR else normalItemSGR let (y1, x1) = targetCoordinate setCursorPosition (y1 + n) x1 let (_, w) = listPromptSize putStrLn $ " " ++ str ++ replicate (w - length str) ' '