{-# LANGUAGE OverloadedStrings #-} module Main where import Control.Monad (void) import qualified Graphics.Vty as V import qualified Brick.Types as T import Brick.Types (Widget) import Lens.Micro ((&), (.~), (^.)) import qualified Brick.Main as M import qualified Brick.Widgets.Border as B import Brick.Widgets.Core (str , hLimit) import Brick.AttrMap import Brick.Util (fg) drawUi :: Int -> [Widget ()] drawUi idx = [ui idx] ui :: Int -> Widget () ui i = B.borderWithLabel (str "Instruction") $ hLimit 45 $ instructionDisplay values i instructionDisplay :: [String] -> Int -> Widget () instructionDisplay insns i = T.Widget T.Fixed T.Fixed $ do let insnWidth = 15 ctx <- T.getContext curAttr <- T.lookupAttrName curInstructionAttr -- Build a Vty image of all the instructions we want to show. -- In practice we'd choose just enough to be visible in the -- space allowed. Pad each one on the left and right so they -- all take up the same amount of width. Then join them all -- horizontally and translate the final image so that the middle -- of the current instruction is centered in the container (by -- looking at the context width). -- -- We also add a dummy entry at the end ("") so that the -- algorithm puts enough space at the end of the image to avoid -- collapsing the parent container (try removing that to see -- what I mean). let img = V.translate (((ctx^.T.availWidthL - insnWidth) `div` 2) + (-1) * (insnWidth * i)) 0 $ V.horizCat $ centerInstruction <$> zip [0..] (insns ++ [""]) -- Center and draw an instruction. -- -- Center it by using th insnWidth to decide how much space -- to add to either side. Then choose its attribute based on -- whether its positional index is the current one. centerInstruction (idx, s) = let attr = if idx == i then curAttr else ctx^.T.attrL in V.string attr $ centerString insnWidth s return $ T.emptyResult & T.imageL .~ img centerString :: Int -> String -> String centerString w s = let l = (w - (length s)) `div` 2 r = w - (length s + l) in replicate l ' ' ++ s ++ replicate r ' ' values :: [String] values = [ "DropFrame" , "[begin]" , "Copy 0" , "Skip 0" , "MoveFrame" , "DropFrame" , "[begin]" , "Copy 0" , "Skip 0" , "MoveFrame" ] appEvent :: Int -> T.BrickEvent () e -> T.EventM () (T.Next Int) appEvent idx (T.VtyEvent (V.EvKey V.KRight [])) = M.continue $ min (length values - 1) (idx + 1) appEvent idx (T.VtyEvent (V.EvKey V.KLeft [])) = M.continue $ max 0 (idx - 1) appEvent idx (T.VtyEvent (V.EvKey V.KEsc [])) = M.halt idx appEvent idx _ = M.continue idx curInstructionAttr :: AttrName curInstructionAttr = "currentInstruction" app :: M.App Int e () app = M.App { M.appDraw = drawUi , M.appStartEvent = return , M.appHandleEvent = appEvent , M.appAttrMap = const $ attrMap V.defAttr [(curInstructionAttr, fg V.cyan)] , M.appChooseCursor = M.neverShowCursor } main :: IO () main = void $ M.defaultMain app 0