From 4c63b96ae5ce1dd2ac02b5cb60a6043bf06b258b Mon Sep 17 00:00:00 2001 From: Joscha Date: Sun, 9 Feb 2020 09:27:19 +0000 Subject: [PATCH] Implement tree rendering --- package.yaml | 1 + src/Forest/Client/WidgetTree.hs | 95 +++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 src/Forest/Client/WidgetTree.hs diff --git a/package.yaml b/package.yaml index 12cf95d..06bfa4a 100644 --- a/package.yaml +++ b/package.yaml @@ -19,6 +19,7 @@ dependencies: - brick - containers - text +- transformers - vty - websockets diff --git a/src/Forest/Client/WidgetTree.hs b/src/Forest/Client/WidgetTree.hs new file mode 100644 index 0000000..a38889b --- /dev/null +++ b/src/Forest/Client/WidgetTree.hs @@ -0,0 +1,95 @@ +{-# LANGUAGE OverloadedStrings #-} + +module Forest.Client.WidgetTree + ( WidgetTree(..) + , renderWidgetTree + , IndentOptions(..) + , boxDrawingBranching + , boxDrawingLine + , asciiBranching + , asciiLine + ) where + +import Brick +import Control.Monad.Trans.Reader +import qualified Data.Text as T +import qualified Graphics.Vty as Vty + +data WidgetTree n = WidgetTree (Widget n) [WidgetTree n] + +-- This attempts to properly indent multi-line widgets, though it's kinda hacky. +-- It seems to work though, so I'm not going to complain (until the first bugs +-- appear, that is). +-- +-- The text strings passed MUST NOT be multiline strings, or this entire +-- function will break. +indentWith :: T.Text -> T.Text -> Widget n -> Widget n +indentWith firstLine otherLines wrapped = Widget + { hSize = hSize wrapped + , vSize = vSize wrapped + , render = renderWidget + } + where + maxWidth = max (T.length firstLine) (T.length otherLines) + renderWidget = do + context <- ask + result <- render $ hLimit (availWidth context - maxWidth) wrapped + let resultHeight = Vty.imageHeight $ image result + leftText = vBox $ txt firstLine : replicate (resultHeight - 1) (txt otherLines) + render $ leftText <+> wrapped + +indent :: IndentOptions -> [Widget n] -> Widget n +indent opts widgets = vBox $ reverse $ case reverse widgets of + [] -> [] + (w:ws) -> + indentWith (lastBranch opts) (afterLastBranch opts) w : + map (indentWith (inlineBranch opts) (noBranch opts)) ws + +renderWidgetTree :: IndentOptions -> WidgetTree n -> Widget n +renderWidgetTree opts (WidgetTree node children) = + node <=> indent opts (map (renderWidgetTree opts) children) + +-- | These options control how a tree is rendered. For more information on how +-- the various options are used, try rendering a tree with 'boxDrawingBranhing' +-- and inspect the results. +-- +-- Warning: The options *must* be single line strings and *must not* contain +-- newlines of any sort. +data IndentOptions = IndentOptions + { noBranch :: T.Text + , inlineBranch :: T.Text + , lastBranch :: T.Text + , afterLastBranch :: T.Text + } deriving (Show, Eq) + +boxDrawingBranching :: IndentOptions +boxDrawingBranching = IndentOptions + { noBranch = "│ " + , inlineBranch = "├╴" + , lastBranch = "└╴" + , afterLastBranch = " " + } + +boxDrawingLine :: IndentOptions +boxDrawingLine = IndentOptions + { noBranch = "│ " + , inlineBranch = "│ " + , lastBranch = "│ " + , afterLastBranch = "│ " + } + +asciiBranching :: IndentOptions +asciiBranching = IndentOptions + { noBranch = "| " + , inlineBranch = "+-" + , lastBranch = "+-" + , afterLastBranch = " " + } + +asciiLine :: IndentOptions +asciiLine = IndentOptions + { noBranch = "| " + , inlineBranch = "| " + , lastBranch = "| " + , afterLastBranch = "| " + }