-- | 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