{-# LANGUAGE NamedFieldPuns #-} module FortyTwo.Utils where import System.Console.ANSI (cursorUpLine, clearFromCursorToScreenEnd) import System.IO (hSetBuffering, hFlush, hSetEcho, hReady, stdin, stdout, BufferMode(..)) import Data.List (findIndex, findIndices, elemIndex, intercalate) import Control.Monad (when) import Data.Maybe (fromJust) import FortyTwo.Types(Option(..), Options) import FortyTwo.Constants (emptyString) -- | Disable the stdin stdout output buffering noBuffering :: IO() noBuffering = do hSetBuffering stdin NoBuffering hSetBuffering stdout NoBuffering -- | Enaable the stdin stdout buffering restoreBuffering :: IO() restoreBuffering = do hSetBuffering stdin LineBuffering hSetBuffering stdout LineBuffering -- | Avoid echoing the user input noEcho :: IO () noEcho = hSetEcho stdin False -- | Restore the user input echos restoreEcho :: IO () restoreEcho = hSetEcho stdin True -- | Clear terminal lines from the current cursor position clearLines :: Int -> IO() clearLines lines' = do -- move up of 1 line... cursorUpLine lines' -- and clear them clearFromCursorToScreenEnd -- | Map a collection with an index map' :: (Int -> a -> b) -> [a] -> [b] map' f = zipWith f [0..] -- | Filter a collection with index filter' :: Eq a => (Int -> a -> Bool) -> [a] -> [a] filter' f xs = [x | x <- xs, f (fromJust (elemIndex x xs)) x] -- | Get the value of any keyboard press getKey :: IO String getKey = reverse <$> getKey' emptyString where getKey' chars = do char <- getChar more <- hReady stdin (if more then getKey' else return) (char:chars) -- | Flush the output buffer flush :: IO() flush = hFlush stdout -- | Get useful informations from the options collection, like minVal, maxVal, activeIndex getOptionsMeta :: Options -> (Int, Int, Maybe Int) getOptionsMeta options = (0, length options - 1, getFocusedOptionIndex options) -- | Convert a string array to stringsToOptions :: [String] -> Options stringsToOptions options = [ Option { value = o, isFocused = False, isSelected = False } | o <- options ] -- | Give the focus to a single option in the collection focusOption :: Int -> Options -> Options focusOption focusedIndex = map' $ \ i o -> Option { value = getOptionValue o, isSelected = getOptionIsSelected o, isFocused = focusedIndex == i } -- | Toggle the isSelected flag for a single option toggleFocusedOption :: Int -> Options -> Options toggleFocusedOption focusedIndex = map' $ \ i o -> Option { value = getOptionValue o, isFocused = focusedIndex == i, isSelected = if focusedIndex == i then not $ getOptionIsSelected o else getOptionIsSelected o } -- | Print a list to comma separated toCommaSeparatedString :: [String] -> String toCommaSeparatedString = intercalate ", " -- | Get the value of any option getOptionValue :: Option -> String getOptionValue Option { value } = value -- | Get the is focused attribute of any option getOptionIsFocused :: Option -> Bool getOptionIsFocused Option { isFocused } = isFocused -- | Get the is selected attribute of any option getOptionIsSelected :: Option -> Bool getOptionIsSelected Option { isSelected } = isSelected -- | Get the index of the option selected getFocusedOptionIndex :: Options -> Maybe Int getFocusedOptionIndex = findIndex getOptionIsFocused -- | Filter the indexes of the options selected getSelecteOptionsIndexes :: Options -> [Int] getSelecteOptionsIndexes = findIndices getOptionIsSelected