{-# LANGUAGE TemplateHaskell #-}
module DzenDhall.AST.Render where
import DzenDhall.AST
import DzenDhall.Config
import DzenDhall.Data
import DzenDhall.Extra
import Control.Monad.Trans.Class
import Control.Monad.Trans.State
import Control.Monad.Trans.Writer
import Data.Maybe
import Control.Monad
import Data.Text (Text)
import Lens.Micro
import Lens.Micro.TH
import qualified Data.Text
data RenderState
= RenderState
{ _bgStack :: [Color]
, _fgStack :: [Color]
, _ibStack :: [IgnoreBackground]
}
data IgnoreBackground = IgnoreBackground
makeLenses ''RenderState
type Stack a = Lens' RenderState [a]
type Render = StateT RenderState (Writer Text)
runRender :: Renderable a => a -> Text
runRender a = snd $ runWriter (runStateT (render a) $ RenderState [] [] [])
class Renderable a where
render :: a -> Render ()
instance Renderable IgnoreBackground where
render IgnoreBackground = write "1"
instance Renderable Color where
render = write . \case
Color color -> color
instance Renderable Button where
render = write . \case
MouseLeft -> "1"
MouseMiddle -> "2"
MouseRight -> "3"
MouseScrollUp -> "4"
MouseScrollDown -> "5"
MouseScrollLeft -> "6"
MouseScrollRight -> "7"
instance Renderable Event where
render (Event event) = write event
instance Renderable ClickableArea where
render ca = do
render $ ca ^. caButton
write ","
write $ ca ^. caCommand
instance Renderable AbsolutePosition where
render position =
write $ showPack (position ^. apX) <> ";" <> showPack (position ^. apY)
instance Renderable Shape where
render = write . \case
I path -> "^i(" <> path <> ")"
R w h -> "^r(" <> showPack w <> "x" <> showPack h <> ")"
RO w h -> "^ro(" <> showPack w <> "x" <> showPack h <> ")"
C r -> "^c(" <> showPack r <> ")"
CO r -> "^co(" <> showPack r <> ")"
instance Renderable AST where
render EmptyAST =
pure ()
render (ASTText text) =
write text
render (ASTs a b) =
(<>) <$> render a <*> render b
render (ASTProp (FG color) ast) =
usingStackWithTag fgStack "fg" color ast
render (ASTProp (BG color) ast) =
usingStackWithTag bgStack "bg" color ast
render (ASTProp IB ast) =
usingStackWithTag ibStack "ib" IgnoreBackground ast
render (ASTProp (CA ca) ast) = do
write "^ca("
render ca
write ")"
render ast
write "^ca()"
render (ASTProp (PA position) ast) = do
write "^pa("
render position
write ")"
render ast
render (ASTProp (P position) ast) = do
write open
render ast
write close
where
(open, close) =
case position of
XY (x, y) ->
( "^p(" <> showPack x <> ";" <> showPack y <> ")"
, "^p(" <> showPack (-x) <> ";" <> showPack (-y) <> ")"
)
P_RESET_Y -> ("^p()", "")
P_LOCK_X -> ("^p(_LOCK_X)", "")
P_UNLOCK_X -> ("^p(_UNLOCK_X)", "")
P_LEFT -> ("^p(_LOCK_X)^p(_LEFT)", "^p(_UNLOCK_X)")
P_RIGHT -> ("^p(_LOCK_X)^p(_RIGHT)", "^p(_UNLOCK_X)")
P_TOP -> ("^p(_TOP)", "^p()")
P_CENTER -> ("^p(_CENTER)", "^p()")
P_BOTTOM -> ("^p(_BOTTOM)", "^p()")
render (ASTShape shape) = render shape
render (ASTPadding width padding ast) = do
write $ mkPaddingText leftPadding
render ast
write $ mkPaddingText rightPadding
where
mkPaddingText n = Data.Text.justifyRight n ' ' ""
(leftPadding, rightPadding) =
paddingWidths padding $ width - astWidth ast
write :: Text -> Render ()
write = lift . tell
usingStackWithTag :: Renderable a => Stack a -> Text -> a -> AST -> Render ()
usingStackWithTag stack tag value ast = do
push stack value
write $ "^" <> tag <> "("
render value
write ")"
render ast
void $ pop stack
renew stack tag
pop :: Stack a -> Render (Maybe a)
pop stack = do
st <- get
put $ st & stack %~ (fromMaybe [] . safeTail)
pure $ safeHead $ st ^. stack
push :: Stack a -> a -> Render ()
push stack value = modify (& stack %~ (value :))
renew :: Renderable a => Stack a -> Text -> Render ()
renew stack tag = do
mbOld <- pop stack
write $ "^" <> tag <> "("
case mbOld of
Just old -> do
render old
push stack old
Nothing -> pure ()
write ")"