module Data.Text.ParagraphLayout.Internal.ProtoLine ( ProtoLine (..) , nonEmpty , width , applyBoxes ) where import Data.Int (Int32) import Data.List.NonEmpty (NonEmpty ((:|))) import qualified Data.Text.ParagraphLayout.Internal.ApplyBoxes as Algo import Data.Text.ParagraphLayout.Internal.ProtoFragment import Data.Text.ParagraphLayout.Internal.ResolvedBox import Data.Text.ParagraphLayout.Internal.WithSpan -- | Contents of a line that have not been visually ordered or positioned yet. data ProtoLine f d = ProtoLine { protoFragments :: f (WithSpan d ProtoFragment) -- ^ Fragments on the line, in logical order. -- Intended to be either a regular list or a non-empty list. , prevOpenBoxes :: [ResolvedBox d] -- ^ Boxes whose starts are located on preceding lines. -- The spacing for their start edge will not count towards this line. , nextOpenBoxes :: [ResolvedBox d] -- ^ Boxes whose ends are located on following lines. -- The spacing for their end edge will not count towards this line. } -- | Covert a line with a regular list of fragments into `Just` a line with -- a non-empty list of fragments, or `Nothing` if there are no fragments. nonEmpty :: ProtoLine [] d -> Maybe (ProtoLine NonEmpty d) nonEmpty (ProtoLine [] _ _) = Nothing nonEmpty pl@(ProtoLine { protoFragments = (f : fs) }) = Just pl { protoFragments = f :| fs } -- | Total width of the line (content and spacing). width :: (Foldable f, Functor f) => ProtoLine f d -> Int32 width pl = totalAdvances pl + totalBoxesStart pl + totalBoxesEnd pl -- | Determine which horizontal fragments are the leftmost and which are the -- rightmost within their ancestor inline boxes. -- -- The input must be ordered from left to right. applyBoxes :: ProtoLine NonEmpty d -> NonEmpty (Algo.WithBoxes d (WithSpan d ProtoFragment)) applyBoxes pl = Algo.applyBoxes prevOpen nextOpen pfs where prevOpen = prevOpenBoxes pl nextOpen = nextOpenBoxes pl pfs = protoFragments pl -- | Total glyph advances on the line. totalAdvances :: (Foldable f, Functor f) => ProtoLine f d -> Int32 totalAdvances pl = sum $ fmap getAdvance $ protoFragments pl where getAdvance (WithSpan _ pf) = advance pf -- | Total spacing for boxes starting on the line. totalBoxesStart :: (Foldable f, Functor f) => ProtoLine f d -> Int32 totalBoxesStart pl = sum $ fmap boxStartSpacing $ boxesStart pl -- | Total spacing for boxes ending on the line. totalBoxesEnd :: (Foldable f, Functor f) => ProtoLine f d -> Int32 totalBoxesEnd pl = sum $ fmap boxEndSpacing $ boxesEnd pl -- | Boxes that start on the given line. boxesStart :: (Foldable f, Functor f) => ProtoLine f d -> [ResolvedBox d] boxesStart pl = allBoxes (protoFragments pl) `diff` prevOpenBoxes pl -- | Boxes that end on the given line. boxesEnd :: (Foldable f, Functor f) => ProtoLine f d -> [ResolvedBox d] boxesEnd pl = allBoxes (protoFragments pl) `diff` nextOpenBoxes pl