-- | Core functionality of the package. Import from either "System.Console.Ansigraph" or -- "System.Console.Ansigraph.Core". module System.Console.Ansigraph.Internal.Core where import System.Console.ANSI import System.IO (hFlush, stdout) import Control.Monad.IO.Class (MonadIO, liftIO) -- for GHC <= 7.8 import Control.Applicative ---- Basics ---- -- | ANSI colors are characterized by a 'Color' and a 'ColorIntensity'. data AnsiColor = AnsiColor { intensity :: ColorIntensity , color :: Color } deriving Show -- | Record that holds graphing options. data GraphSettings = GraphSettings { -- | Foreground color for real number component. realColor :: AnsiColor -- | Foreground color for imaginary number component. , imagColor :: AnsiColor -- | Foreground color for negative real values. For matrix graphs only. , realNegColor :: AnsiColor -- | Foreground color for negative imaginary values. For matrix graphs only. , imagNegColor :: AnsiColor -- | Background color for real number component. , realBG :: AnsiColor -- | Background color for imaginary number component. , imagBG :: AnsiColor -- | Framerate in FPS. , framerate :: Int } -- | 'Vivid' 'Blue' – used as the default real foreground color. blue = AnsiColor Vivid Blue -- | 'Vivid' 'Magenta' – used as the default foreground color for imaginary -- graph component. pink = AnsiColor Vivid Magenta -- | 'Vivid' 'White' – used as the default graph background color -- for both real and imaginary graph components. white = AnsiColor Vivid White -- | 'Vivid' 'Red' – used as the default foreground color for negative real component. red = AnsiColor Vivid Red -- | 'Dull' 'Green' – used as the default foreground color for negative imaginary component. green = AnsiColor Dull Green -- | 'Dull' 'Black'. black = AnsiColor Dull Blue -- | 'Vivid' 'Yellow'. yellow = AnsiColor Vivid Yellow -- | 'Vivid' 'Magenta'. magenta = AnsiColor Vivid Magenta -- | 'Vivid' 'Cyan'. cyan = AnsiColor Vivid Cyan -- | Default graph settings. graphDefaults = GraphSettings blue pink red green white white 15 -- | Holds two 'Maybe' 'AnsiColor's representing foreground and background colors for display via ANSI. -- 'Nothing' means use the default terminal color. data Coloring = Coloring { foreground :: Maybe AnsiColor, background :: Maybe AnsiColor } deriving Show -- | A 'Coloring' representing default terminal colors, i.e. two 'Nothing's. noColoring = Coloring Nothing Nothing -- | Helper constructor function for 'Coloring' that takes straight 'AnsiColor's without 'Maybe'. mkColoring :: AnsiColor -> AnsiColor -> Coloring mkColoring c1 c2 = Coloring (Just c1) (Just c2) -- | Projection retrieving foreground and background colors -- for real number graphs in the form of a 'Coloring'. realColors :: GraphSettings -> Coloring realColors s = mkColoring (realColor s) (realBG s) -- | Projection retrieving foreground and background colors -- for imaginary component of complex number graphs in the form of a 'Coloring'. imagColors :: GraphSettings -> Coloring imagColors s = mkColoring (imagColor s) (imagBG s) -- | Retrieves a pair of 'Coloring's for real and imaginary graph components respectively. colorSets :: GraphSettings -> (Coloring,Coloring) colorSets s = (realColors s, imagColors s) -- | Swaps foreground and background colors within a 'Coloring'. invert :: Coloring -> Coloring invert (Coloring fg bg) = Coloring bg fg -- | Easily create a 'Coloring' by specifying the background 'AnsiColor' and no custom foreground. fromBG :: AnsiColor -> Coloring fromBG c = Coloring Nothing (Just c) -- | Easily create a 'Coloring' by specifying the foreground 'AnsiColor' and no custom background. fromFG :: AnsiColor -> Coloring fromFG c = Coloring (Just c) Nothing -- | The SGR command corresponding to a particular 'ConsoleLayer' and 'AnsiColor'. interpAnsiColor :: ConsoleLayer -> AnsiColor -> SGR interpAnsiColor l (AnsiColor i c) = SetColor l i c -- | Produce a (possibly empty) list of 'SGR' commands from a 'ConsoleLayer' and 'AnsiColor'. -- An empty 'SGR' list is equivalent to 'Reset'. sgrList :: ConsoleLayer -> Maybe AnsiColor -> [SGR] sgrList l = fmap (interpAnsiColor l) . maybe [] pure -- | Set the given 'AnsiColor' on the given 'ConsoleLayer'. setColor :: MonadIO m => ConsoleLayer -> AnsiColor -> m () setColor l c = liftIO $ setSGR [interpAnsiColor l c] -- | Apply both foreground and background color contained in a 'Coloring'. applyColoring :: MonadIO m => Coloring -> m () applyColoring (Coloring fg bg) = liftIO $ do setSGR [Reset] setSGR $ sgrList Foreground fg ++ sgrList Background bg -- | Clear any SGR settings and then flush stdout. clear :: MonadIO m => m () clear = liftIO $ setSGR [Reset] >> hFlush stdout putStrLn', putStr' :: MonadIO m => String -> m () putStrLn' = liftIO . putStrLn putStr' = liftIO . putStr -- | Clear any SGR settings, flush stdout and print a new line. clearLn :: MonadIO m => m () clearLn = clear >> putStrLn' "" -- | Use a particular ANSI 'Coloring' to print a string at the terminal (without a new line), -- then clear all ANSI SGR codes and flush stdout. colorStr :: MonadIO m => Coloring -> String -> m () colorStr c s = do applyColoring c putStr' s clear -- | Use a particular ANSI 'Coloring' to print a string at the terminal, -- then clear all ANSI SGR codes, flush stdout and print a new line. colorStrLn :: MonadIO m => Coloring -> String -> m () colorStrLn c s = do applyColoring c putStr' s clearLn -- | Like 'colorStr' but prints bold text. boldStr :: MonadIO m => Coloring -> String -> m () boldStr c s = do applyColoring c liftIO $ setSGR [SetConsoleIntensity BoldIntensity] putStr' s clear -- | Like 'colorStrLn' but prints bold text. boldStrLn :: MonadIO m => Coloring -> String -> m () boldStrLn c s = boldStr c s >> putStrLn' ""