~jaro/balkon

a2d91928eca26673b19d166f2af3ee8da527df76 — Jaro 11 months ago 85a32d4
Add start/end paragraph alignment.

In the future, when the paragraph direction is not explicitly set,
start/end options will allow alignment to change line by line,
whereas left/right alignment will be constant.
M lib/Data/Text/ParagraphLayout/Rich.hs => lib/Data/Text/ParagraphLayout/Rich.hs +7 -1
@@ 5,7 5,13 @@ module Data.Text.ParagraphLayout.Rich
    -- * Input paragraph
    ( Paragraph (Paragraph)
    , constructParagraph
    , ParagraphAlignment (AlignLeft, AlignRight, AlignCentreH)
    , ParagraphAlignment
        ( AlignStart
        , AlignEnd
        , AlignLeft
        , AlignRight
        , AlignCentreH
        )
    , BoxSpacing (BoxSpacingLeftRight)
    , LineHeight (Absolute, Normal)
    , ParagraphOptions

M src/Data/Text/ParagraphLayout/Internal/Layout.hs => src/Data/Text/ParagraphLayout/Internal/Layout.hs +25 -5
@@ 121,7 121,7 @@ positionLineH dir align maxWidth originY (num, pl) = (nextY, frags)
        wpfs = PL.applyBoxes pl
        originX = paragraphOriginX + if lineWidth > maxWidth
            then overflowingLineOffset dir (lineWidth - maxWidth)
            else fittingLineOffset align (maxWidth - lineWidth)
            else fittingLineOffset align dir (maxWidth - lineWidth)
        lineWidth = PL.width pl

-- | Inline offset of the first fragment on a line that overflows.


@@ 133,10 133,30 @@ overflowingLineOffset DirRTL excess = -excess
overflowingLineOffset DirBTT excess = -excess

-- | Inline offset of the first fragment on a line with extra blank space.
fittingLineOffset :: ParagraphAlignment -> Int32 -> Int32
fittingLineOffset AlignLeft _ = 0
fittingLineOffset AlignRight slack = slack
fittingLineOffset AlignCentreH slack = slack `div` 2
fittingLineOffset :: ParagraphAlignment -> Direction -> Int32 -> Int32
fittingLineOffset AlignLeft _ = leftAlignOffset
fittingLineOffset AlignRight _ = rightAlignOffset
fittingLineOffset AlignCentreH _ = centreAlignOffset
fittingLineOffset AlignStart DirLTR = leftAlignOffset
fittingLineOffset AlignEnd DirLTR = rightAlignOffset
fittingLineOffset AlignStart DirRTL = rightAlignOffset
fittingLineOffset AlignEnd DirRTL = leftAlignOffset
-- For completeness, treat vertical directions as horizontal directions
-- rotated 90° clockwise, thus left becomes top and right becomes bottom.
-- TODO: Verify this when vertical text is implemented.
fittingLineOffset AlignStart DirTTB = leftAlignOffset
fittingLineOffset AlignEnd DirTTB = rightAlignOffset
fittingLineOffset AlignStart DirBTT = rightAlignOffset
fittingLineOffset AlignEnd DirBTT = leftAlignOffset

leftAlignOffset :: Int32 -> Int32
leftAlignOffset _ = 0

rightAlignOffset :: Int32 -> Int32
rightAlignOffset slack = slack

centreAlignOffset :: Int32 -> Int32
centreAlignOffset slack = slack `div` 2

-- | Position the given horizontal fragment on a line,
-- using @originY@ as its top edge and @originX@ as its left edge,

M src/Data/Text/ParagraphLayout/Internal/ParagraphAlignment.hs => src/Data/Text/ParagraphLayout/Internal/ParagraphAlignment.hs +9 -1
@@ 10,7 10,15 @@ where
-- This has no effect on lines with overflowing content.
data ParagraphAlignment

    = AlignLeft
    = AlignStart
    -- ^ Behave like `AlignLeft` when the base direction of the paragraph
    -- is LTR, and like `AlignRight` when the base direction is RTL.

    | AlignEnd
    -- ^ Behave like `AlignLeft` when the base direction of the paragraph
    -- is RTL, and like `AlignRight` when the base direction is LTR.

    | AlignLeft
    -- ^ Stack fragments to the left edge of each line,
    -- leaving free space on the right side.
    --

M src/Data/Text/ParagraphLayout/Internal/ParagraphOptions.hs => src/Data/Text/ParagraphLayout/Internal/ParagraphOptions.hs +8 -1
@@ 15,6 15,10 @@ import Data.Text.ParagraphLayout.Internal.ParagraphAlignment
-- This record type is likely to be extended in the future.
-- Use `defaultParagraphOptions` and update it with specific record selectors
-- instead of constructing `ParagraphOptions` directly.
--
-- In order to get CSS defaults, use:
--
-- > defaultParagraphOptions { paragraphAlignment = AlignStart }
data ParagraphOptions = ParagraphOptions

    { paragraphFont :: Font


@@ 38,7 42,10 @@ data ParagraphOptions = ParagraphOptions
    }
    deriving (Eq)

-- | `ParagraphOptions` with default values.
-- | `ParagraphOptions` with backwards-compatible values.
--
-- Note that this sets `paragraphAlignment` to `AlignLeft`,
-- whereas the CSS default should be `AlignStart`.
defaultParagraphOptions :: ParagraphOptions
defaultParagraphOptions = ParagraphOptions
    { paragraphFont = emptyFont

M src/Data/Text/ParagraphLayout/Internal/Rich.hs => src/Data/Text/ParagraphLayout/Internal/Rich.hs +4 -0
@@ 33,6 33,10 @@ layoutRich p = paragraphLayout $ map unwrap frags
            Just xs -> layoutAndAlignLines dir align maxWidth xs
            Nothing -> []
        wrappedRuns = spansToRunsWrapped spans
        -- TODO: To support @unicode-bidi: plaintext@ as in CSS, allow ignoring
        --       the text direction of the root box, and instead use the BiDi
        --       rules P2 and P3 to determine the base directionality, which
        --       may differ between lines.
        dir = textDirection rootTextOpts
        align = paragraphAlignment opts
        maxWidth = paragraphMaxWidth opts