module Data.Text.ParagraphLayout.Internal.ParagraphLine ( ParagraphLine , cutLines , mergeLines ) where import Data.Int (Int32) import qualified Data.List.NonEmpty as NonEmpty import Data.Text.ParagraphLayout.Internal.Fragment import Data.Text.ParagraphLayout.Internal.LinePagination import Data.Text.ParagraphLayout.Internal.Plain.ParagraphLayout import Data.Text.ParagraphLayout.Internal.Rect -- | Represents one line of a `ParagraphLayout`. newtype ParagraphLine d = ParagraphLine (ParagraphLayout d) instance Line (ParagraphLine d) where lineHeight (ParagraphLine pl) = height $ paragraphRect pl -- | Split the given `ParagraphLayout` into individual lines. cutLines :: ParagraphLayout d -> [ParagraphLine d] cutLines pl = map (\ n -> cutLine n pl) (lineNumbers pl) -- | Reduce the given `ParagraphLayout` to fragments with the given line number. cutLine :: Int -> ParagraphLayout d -> ParagraphLine d cutLine n pl = ParagraphLine $ trimTop $ limitFragments n pl -- | Add a constant to each fragment's `y_origin` so that their maximum is zero. trimTop :: ParagraphLayout d -> ParagraphLayout d trimTop pl = shiftFragments (-top) pl where top = maximum $ map (y_origin . fragmentRect) $ paragraphFragments pl lineNumbers :: ParagraphLayout d -> [Int] lineNumbers pl = dedupe $ map fragmentLine $ paragraphFragments pl -- | Remove duplicates from a sorted list. dedupe :: Eq a => [a] -> [a] dedupe xs = map NonEmpty.head $ NonEmpty.group xs -- | Combine the given `ParagraphLine`s into a `ParagraphLayout` by merging -- their fragments. mergeLines :: [ParagraphLine d] -> ParagraphLayout d mergeLines lls = foldl mergeLine emptyParagraphLayout lls mergeLine :: ParagraphLayout d -> ParagraphLine d -> ParagraphLayout d mergeLine pl (ParagraphLine nextLine) = pl' where -- Quadratic time complexity. TODO: Consider optimising. pl' = appendFragments pl $ shiftFragments y nextLine y = y_terminus $ paragraphRect pl -- | Add @dy@ to each fragment's `y_origin`. shiftFragments :: Int32 -> ParagraphLayout d -> ParagraphLayout d shiftFragments dy = mapFragments (shiftFragment dy) shiftFragment :: Int32 -> Fragment d -> Fragment d shiftFragment dy f = f' where f' = f { fragmentRect = r' } r' = r { y_origin = y_origin r + dy } r = fragmentRect f -- | Keep only fragments with the given line number. limitFragments :: Int -> ParagraphLayout d -> ParagraphLayout d limitFragments n = filterFragments ((== n) . fragmentLine)