From 39ec2358151d0d99dbeb4528efde98158031920a Mon Sep 17 00:00:00 2001 From: Jaro Date: Wed, 26 Apr 2023 23:34:10 +0200 Subject: [PATCH] Define tree for structuring paragraph content. --- balkon.cabal | 1 + .../Text/ParagraphLayout/Internal/Tree.hs | 88 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 src/Data/Text/ParagraphLayout/Internal/Tree.hs diff --git a/balkon.cabal b/balkon.cabal index 31af3d7..880889b 100644 --- a/balkon.cabal +++ b/balkon.cabal @@ -119,6 +119,7 @@ library balkon-internal Data.Text.ParagraphLayout.Internal.Span, Data.Text.ParagraphLayout.Internal.TextContainer, Data.Text.ParagraphLayout.Internal.TextOptions, + Data.Text.ParagraphLayout.Internal.Tree, Data.Text.ParagraphLayout.Internal.Zipper -- Modules used purely internally and not in any tests. diff --git a/src/Data/Text/ParagraphLayout/Internal/Tree.hs b/src/Data/Text/ParagraphLayout/Internal/Tree.hs new file mode 100644 index 0000000..2ea1d28 --- /dev/null +++ b/src/Data/Text/ParagraphLayout/Internal/Tree.hs @@ -0,0 +1,88 @@ +-- | Represents the contents of a paragraph as a tree. +-- +-- The tree is a hierarchy of boxes, with one box always present as the root. +-- Each box may contain any combination of text sequences and other boxes. +module Data.Text.ParagraphLayout.Internal.Tree + ( RootNode (..) + , InnerNode (..) + , Box (..) + , Leaf (..) + , flatten + ) +where + +import Data.Text.ParagraphLayout.Internal.BoxOptions +import Data.Text.ParagraphLayout.Internal.TextOptions + +-- | Root of the paragraph tree. +data RootNode d + + = RootBox + -- ^ The root inline box. Always present in a paragraph. + -- + -- Cannot be styled directly, but can still set options for formatting + -- text sequences directly descending from this box. + (Box d) + -- ^ Contents of the box. + +-- | Non-root node of the paragraph tree. +data InnerNode d + + = InlineBox + -- ^ An inline box, nested in another box. + d + -- ^ User-defined data associated with the box. + (Box d) + -- ^ Contents of the box. + BoxOptions + -- ^ Style options to apply to the inline box. + + | TextSequence + -- ^ A leaf node containing text. + d + -- ^ User-defined data associated with the text node. + Int + -- ^ Byte offset to the next text node or the end of the paragraph text. + +-- | A box with content and a defined format. Corresponds to a DOM element. +data Box d = Box + + [InnerNode d] + -- ^ Text nodes and other boxes contained by this box. + + TextOptions + -- ^ Style options to apply to text sequences directly contained + -- by this box. + +-- TODO: we need a way to test box equality, maybe add indexes? +type BoxPath d = [(d, BoxOptions)] + +-- | Representation of a leaf node of the tree after flattening. +data Leaf d = TextLeaf + + d + -- ^ User-defined data associated with the text node. + + Int + -- ^ Byte offset to the next text node or the end of the paragraph text. + + TextOptions + -- ^ Style options to apply to this text sequence. + + (BoxPath d) + -- ^ Inline boxes found on the path from this text sequence to the root + -- of the original tree. + +-- | Convert the tree to a flat list of its leaf nodes (text sequences). +flatten :: RootNode d -> [Leaf d] +flatten (RootBox (Box ns textOpts)) = flattenNodes [] textOpts ns + +flattenNodes :: BoxPath d -> TextOptions -> [InnerNode d] -> [Leaf d] +flattenNodes _ _ [] = [] +flattenNodes path textOpts (n : ns) = + flattenNode path textOpts n ++ flattenNodes path textOpts ns + +flattenNode :: BoxPath d -> TextOptions -> InnerNode d -> [Leaf d] +flattenNode path textOpts (TextSequence d len) = [TextLeaf d len textOpts path] +flattenNode path _ (InlineBox d (Box ns textOpts) boxOpts) = + flattenNodes ((d, boxOpts) : path) textOpts ns -- 2.30.2