~jaro/balkon

e810016b89d91f9c7418891a188dd1850316d026 — Jaro 1 year, 1 month ago b03b0b9
Improve documentation for pagination.
M src/Data/Text/ParagraphLayout/Internal/LinePagination.hs => src/Data/Text/ParagraphLayout/Internal/LinePagination.hs +32 -16
@@ 6,15 6,16 @@
-- to as pages within this module.
--
-- Assumptions:
--
-- * Lines are laid out from top to bottom.
-- * Each page has the same size.
--   (Preceding context may limit the space available on the given page, but it
--   is assumed that the space on every following page can be used in full.)
module Data.Text.ParagraphLayout.Internal.LinePagination
    (Line
    ,lineHeight
    ,PageContinuity(Break, Continue)
    ,bestSplit
    ,lineHeight
    ,paginateLines
    )
where


@@ 38,27 39,30 @@ class Line a where
instance Line Int32 where
    lineHeight = id

-- | Represents the best place, as determined by the `paginateLines` function,
-- to place the first line of paginated text.
-- | Represents the best place to place a chunk of paginated content.
data PageContinuity

    = Continue
    -- ^ The lines are split so that the prefix can continue on the same page
    -- as its preceding context.
    -- ^ The content is split so that a given chunk can continue
    -- on the same page as its preceding context.
    --
    -- This may be because all constraints were met, or because adding a page
    -- break would have no benefit.
    -- This may be because all constraints were met, or because
    -- adding a page break would have no benefit.

    | Break
    -- ^ The lines are split so that the prefix should begin on a new page.
    -- ^ The content is split so that a given chunk should begin
    -- on a new page.
    --
    -- This may be because the current page does not have enough space
    -- to preserve orphan/widow constrains, or because it does not have space
    -- for any lines at all.
    -- This may be because the current page does not have enough
    -- space to preserve orphan/widow constrains, or because it
    -- does not have space for any content at all.

    deriving (Eq, Show, Read, Enum, Bounded)

-- | Split a list of lines in order to fit the given pagination constraints.
-- | Split a list of lines in order to meet the given pagination constraints.
--
-- This is a high-level function that produces the best usable result,
-- even if some constraints have to be violated.
--
-- The first component of the output determines whether a page break should
-- be inserted before any of the given lines.


@@ 129,10 133,22 @@ split1 :: [a] -> ([a], [a])
split1 xs = (take 1 xs, drop 1 xs)

-- | Split a list of lines so that the prefix contains as many lines
-- as possible while the total of line heights in the prefix does
-- not exceed @h@, the first @o@ lines ("orphans") are kept together,
-- and the last @w@ lines ("widows") are kept together.
bestSplit :: Line a => Word -> Word -> Int32 -> [a] -> ([a], [a])
-- as possible while satisfying the given constraints.
--
-- This is a low-level function that makes no compromises.
bestSplit :: Line a
    => Word
    -- ^ Number of lines at the beginning ("orphans") to keep together.
    -> Word
    -- ^ Number of lines at the end ("widows") to keep together.
    -> Int32
    -- ^ Maximum total height of lines in the prefix.
    -> [a]
    -- ^ Lines to split.
    -> ([a], [a])
    -- ^ Two lists of lines that yield the original list when concatenated,
    -- where the prefix, if non-empty, matches the given orphan, widow, and
    -- maximum height constraints.
bestSplit o w h ls = NonEmpty.last $ constrainedSplits o w h ls

-- | Split a list of lines in every possible way, from shortest prefix

M src/Data/Text/ParagraphLayout/Internal/Paginable.hs => src/Data/Text/ParagraphLayout/Internal/Paginable.hs +22 -11
@@ 35,18 35,29 @@ data PageOptions = PageOptions
    }

-- | Typeclass for layouts that can be broken into pages.
--
-- The first component of the output determines whether a page break should
-- be inserted before the paragraph.
--
-- The second component of the output contains the portion of the paragraph
-- that fits on the page and satisfies the given constraints as much as
-- possible.
--
-- The third component of the output will be `Just` the remainder of the
-- paragraph that can be passed to this function again, or `Nothing` if there
-- is nothing left to put on further pages.
class Paginable pl where
    -- | Break a chunk of content from the given layout, to be placed together
    -- on a page.
    --
    -- Explanation of return values:
    --
    -- * @(`Continue`, p, `Nothing`)@
    --   means that @p@ is the entire layout and fits best on the current page.
    --
    -- * @(`Break`, p, `Nothing`)@
    --   means that @p@ is the entire layout and fits best on a new page.
    --   In other words, @p@ should be preceded by a page break.
    --
    -- * @(`Continue`, p, `Just` rest)@
    --   means that @p@ is a part of the layout that fits best on the current
    --   page, and @rest@ should be passed to this function again.
    --   In other words, @p@ should be followed by a page break.
    --
    -- * @(`Break`, p, `Just` rest)@
    --   means that @p@ is a part of the layout that fits best on a new page,
    --   and @rest@ should be passed to this function again.
    --   In other words, @p@ should be surrounded by page breaks
    --   on both sides.
    paginate :: PageOptions -> pl -> (PageContinuity, pl, Maybe pl)

instance Line a => Paginable [a] where