summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibWeb/Layout/LineBuilder.cpp
AgeCommit message (Collapse)Author
2023-01-05LibWeb: Convert Layout::Node to new pixel unitsSam Atkins
2023-01-05LibWeb: Convert InlineLevelIterator/LineBox/LineBuilder to new px unitsSam Atkins
2023-01-05LibWeb: Convert InlineFormattingContext to new pixel unitsSam Atkins
2022-12-14LibWeb: Convert Paintable coordinates to new pixel unitsSam Atkins
This fixes a few sizing issues too. The page size is now correct in most cases! \o/ We get to remove some of the `to_type<>()` shenanigans, though it reappears in some other places.
2022-12-07LibWeb: Consider strut while calculating baseline for a lineAliaksandr Kalenik
Strut should be taken in account while computing baseline of a line. Otherwise it results in wrong alignment in boxes that has inline elements without any text. This also fixes red box in Acid 2.
2022-12-05LibWeb: Move box_baseline from LineBuilder.cpp to LayoutState.cppAliaksandr Kalenik
2022-10-14LibWeb: Fix bogus inline-block check in line box layoutAndreas Kling
When checking if a line box fragment "isn't just dumb inline content", we were checking "is replaced or inline-block". What we really need to be checking is "is replaced or inline-outside-but-not-flow-inside". So now we check that. This fixes an issue where inline-flex boxes were given incorrect extra height due to being treated as regular text for purposes of line height calculation.
2022-10-03LibWeb: Don't add half-leading twice to inline block boxesAndreas Kling
Inline-level blocks already have the half-leading applied internally, so by adding it twice, we were offsetting their baseline by the half-leading of the line. This fixes an issue where inline-blocks were vertically offset from the line they're supposed to sit on.
2022-09-29LibWeb: Fix bogus comparison when measuring if a float can fitAndreas Kling
We were using `>=` instead of `>` when checking if a float with a given width could fit in the available space. If the width was an exact match, we rejected it! Oops :^)
2022-09-29LibWeb: Don't round fragment widths while accumulating in LineBuilderAndreas Kling
By rounding the fragment widths, we sometimes inserted line breaks prematurely, even though the fragment *would* fit.
2022-09-22LibWeb: Perform horizontal inline alignment based on available spaceAndreas Kling
Previously, we were using the full containing block width as a reference for text-align values "right" and "center". This didn't take intruding floats into account.
2022-09-22LibWeb: Check both top and bottom of float position when looking for fitAndreas Kling
We have to check that there's enough space at both the top and bottom of the float's margin box, otherwise we risk overlapping existing content.
2022-09-16LibWeb: Improve inline flow around floating boxesAndreas Kling
This patch combines a number of techniques to make inline content flow more correctly around floats: - During inline layout, BFC now lets LineBuilder decide the Y coordinate when inserting a new float. LineBuilder has more information about the currently accumulated line, and can make better breaking decisions. - When inserting a float on one side, and the top of the newly inserted float is below the bottommost float on the opposite side, we now reset the opposite side back to the start of that edge. This improves breaking behavior between opposite-side floats. - After inserting a float during inline layout, we now recalculate the available space on the line, but don't adjust X offsets of already existing fragments. This is handled by update_last_line() anyway, so it was pointless busywork. - When measuring whether a line can fit at a given Y coordinate, we now consider both the top and bottom Y values of the line. This fixes an issue where the bottom part of a line would bleed over other content (since we had only checked that the top Y coordinate of that line would fit.) There are some pretty brain-dead algorithms in here that we need to make smarter, but I didn't want to complicate this any further so I've left FIXMEs about them instead.
2022-09-13LibWeb: Break lines until we have enough space between floatsAndreas Kling
Before this change, we'd always insert one line box fragment, even when a float was taking up too much space on the line, and the fragment didn't actually fit. We now perform line breaks until we have enough space between floats. This fixes many page layouts where we'd previously see small fragments of inline content outside the right edge of the containing block.
2022-08-14LibWeb: Remove unused member LineBuilder::m_layout_modeAndreas Kling
2022-07-19LibWeb: Add accessors for UsedValues::computed_{width,height}Andreas Kling
This is preparation for doing some more work when assigning to these values.
2022-07-17LibWeb: Rename LayoutState::NodeState => LayoutState::UsedValuesAndreas Kling
This object contains all the CSS "used values" as seen during the layout process, so calling it "used values" seems appropriate. :^)
2022-07-17LibWeb: Rename FormattingState to LayoutStateAndreas Kling
This seems a bit more descriptive (and also a bit shorter).
2022-07-11LibWeb: Make sure we always apply size constraints in IFCAndreas Kling
Pre-compute the effective containing block width in the IFC constructor and use that throughout.
2022-07-11LibWeb: Express intrinsic size layout via size constraintsAndreas Kling
Previously, we had three layout modes: - Normal: - Everything uses the computed values from CSS. - MinContent: - Containing blocks act as if they have 0 width. - All line breaking opportunities are taken. - MaxContent: - Containing blocks act as if they have infinite width. - Only forced line breaks are accepted. The above was based on a set of misunderstandings of CSS sizing. A major problem with the above was that *all* containing blocks behaved differently during intrinsic size layout, not just the relevant one. With this patch there are only two layout modes: - Normal: - Everything uses the computed values from CSS. - IntrinsicSizeDetermination: - One or more boxes have size constraints applied. There are two size constraints per layout box, set here: - FormattingState::NodeState::width_constraint - FormattingState::NodeState::height_constraint They are of type SizeConstraint and can be one of None, MinContent, or MaxContent. The default is None. When performing an IntrinsicSizeDetermination layout, we now assign a size constraint to the box we're trying to determine the intrinsic size of, which is then honored by using two new helpers to query the dimensions of containing blocks: - FormattingContext::containing_block_width_for(Box) - FormattingContext::containing_block_height_for(Box) If there's a relevant constraint in effect on the Box, the size of its containing block is adjusted accordingly. This is essentially an implementation of the "available space" constraints from CSS-SIZING-3. I'm sure some things will break from this, and we'll have to deal with that separately. Spec: https://drafts.csswg.org/css-sizing-3/#available
2022-03-30LibWeb: Use more precise font metrics when doing inline layoutAndreas Kling
We now position inline-level boxes based on ascent and descent metrics from the font in use. This makes our basic text layouts look a lot more like those produced by other browsers. :^) I've tried to match the terminology used by the CSS Inline Layout spec. This will regress Acid2 a little bit, and probably various other sites, but on the whole it's the direction we should be heading, so let's go.
2022-03-30LibWeb: Use the new Gfx::Painter::draw_text_run() API for drawing textAndreas Kling
This avoids a bunch of unnecessary work in Painter which not only took time, but sometimes also led to alignment issues. draw_text_run() will draw the text where we tell it, and that's it.
2022-03-26LibWeb: Make text newlines in "pre" mode emit a ForcedBreak itemAndreas Kling
Instead of emitting a Text item with the "should_force_break" flag set to true, newlines in newline-preserving text content now timply turn into ForcedBreak items. This makes the <pre> element work again.
2022-03-24LibWeb: Support CSS vertical-align values "top" and "bottom"Andreas Kling
2022-03-24LibWeb: Bring CSS line-height implementation closer to specAndreas Kling
We now distribute the line-height evenly between the space above and below inline-level boxes. This noticeably improves our baseline alignment in many cases. Note that the "vertical-align: <length>" case is quite awkward, as the extra height added by the offset baseline must count towards the line box height. There's a lot of room for improvement here, but this makes the buckets container on Acid3 show up in the right place, with the right size.
2022-03-24LibWeb: Align baseline of inline-block with non-zero top border/paddingAndreas Kling
We were not adjusting the fragment baseline for inline-blocks that had some border and/or padding size up top.
2022-03-22LibWeb: Support CSS floats in inline flowAndreas Kling
CSS floats are now emitted by the InlineLevelIterator. When this happens, IFC coordinates with the parent BFC to float the box to the side, using the current LineBuilder state for vertical placement. This makes the "instructions" text on Acid3 render as a single contiguous flow of inline content.
2022-03-19LibWeb: Rename the LayoutMode enum values and explain themAndreas Kling
The old mode names, while mechanically accurate, didn't really reflect their relationship to the CSS specifications. This patch renames them as follows: Default => Normal AllPossibleLineBreaks => MinContent OnlyRequiredLineBreaks => MaxContent There's also now an explainer comment with the LayoutMode enum about the specific implications of layout in each mode.
2022-03-18LibWeb: Rewrite CSS float implementation to use offset-from-edgeAndreas Kling
The previous implementation used relative X offsets for both left and right-side floats. This made right-side floats super awkward, since we could only determine their X position once the width of the BFC root was known, and for BFC roots with automatic width, this was not even working at all most of the time. This patch changes the way we deal with floats so that BFC keeps track of the offset-from-edge for each float. The offset is the distance from the BFC root edge (left or right, depending on float direction) to the "innermost" margin edge of the floating box. Floating box are now laid out in two passes: while going through the normal flow layout, we put floats in their *static* position (i.e the position they would have occupied if they weren't floating) and then update the Y position value to the final one. The second pass occurs later on, when the BFC root has had its width assigned by the parent context. Once we know the root width, we can set the X position value of floating boxes. (Because the X position of right-side floats is relative to the right edge of the BFC root.)
2022-03-18LibWeb: Make LineBuilder aware of the current LayoutModeAndreas Kling
This will allow us to override the available space correctly when doing intrinsic sizing.
2022-03-12LibWeb: Refactor text justification code + only justify below thresholdsin-ack
All the justification-related code is now in InlineFormattingContext::apply_justification_to_fragments and is performed after all the line boxes have been added. Text justification now only happens on the last line if the excess space including whitespace is below a certain threshold. 10% seemed reasonable since it prevents the "over-justification" of text. Note that fragments in line boxes before the last one are always justified.
2022-03-09LibWeb: Respect inline-axis margins between line box fragments :^)Andreas Kling
This makes the buckets on ACID3 space out nicely.
2022-03-03LibWeb: Remove bogus line box width adjustmentAndreas Kling
This basically reverts 95715f0c8f066b2bb43b9dbb4dec993bb627fd9f, as it was totally wrong for text-align:center and text-align:right.
2022-03-03LibWeb: Align actual baselines, not just bottomsAndreas Kling
Until now, we've been treating the bottom of every line box fragment as its baseline, and just aligning all the bottoms to the bottom of the line box. That gave decent results in many cases, but was not correct. This patch starts moving towards actual baseline calculations as specified by CSS2. Note that once layout is finished with a line box, we also store the baseline of the line box in LineBox::m_baseline. This allows us to align the real baseline of display:inline-block elements with other inline content on the same line.
2022-02-28LibWeb: Store box's containing line box fragment in FormattingStateAndreas Kling
Layout should not change any properties of a box until the moment a FormattingState is committed.
2022-02-28LibWeb: Store bottom edge location with each LineBoxAndreas Kling
Previously we were computing the bottom edge of a line box by finding the bottommost fragment on the line. That method didn't give correct results for line boxes with no fragments (which is exactly what you get when inserting a bunch of <br> elements.) To cover all situations, we now keep track of the bottommost edge in the LineBox object itself.
2022-02-28LibWeb: Use coordinate instead of WeakPtr for box->fragment connectionAndreas Kling
Using WeakPtr to remember which LineBoxFragment owns which Box was imposing some annoying constraints on the layout code. Importantly, it was forcing us to heap-allocate fragments, which makes it much harder to clone a FormattingState. This patch replaces the WeakPtr with a coordinate system instead. Fragments are referred to by their line box index + fragment index within the line box.
2022-02-26LibWeb: Very basic support for CSS vertical-align: <length>Andreas Kling
If vertical-align is a length value, we lift each line box fragment that far from the baseline of the line box. This is rather messy, and we'll have to improve factoring as we add support for more alignment types.
2022-02-26LibWeb: Add border box top/bottom metrics to line box fragmentsAndreas Kling
This will allow us to support more kinds of vertical alignment.
2022-02-25LibWeb: Fix rounding errors in calculation of final line box widthAndreas Kling
Instead of re-measuring the distance between the left and right edges of a line box, we now simply adjust the final width based on how much the rightmost fragment moved during the alignment process.
2022-02-21LibWeb: Rename FormattingState::ensure() -> get_mutable()Andreas Kling
This makes it much more obvious what the difference between get() and get_mutable() is.
2022-02-21LibWeb: Start making our layout system "transactional"Andreas Kling
This patch adds a map of Layout::Node to FormattingState::NodeState. Instead of updating layout nodes incrementally as layout progresses through the formatting contexts, all updates are now written to the corresponding NodeState instead. At the end of layout, FormattingState::commit() is called, which transfers all the values from the NodeState objects to the Node. This will soon allow us to perform completely non-destructive layouts which don't affect the tree. Note that there are many imperfections here, and still many places where we assign to the NodeState, but later read directly from the Node instead. I'm just committing at this stage to make subsequent diffs easier to understand.
2022-02-14LibWeb: Support inline-level padding and border properlyAndreas Kling
Here's roughly how this works: - InlineLevelIterator keeps a nesting stack of inline-level nodes with box model metrics. - When entering a node with box model metrics, we add them to the current "leading metrics". - When exiting a node with box model metrics, we add them to the current "trailing metrics". - Pending leading metrics are consumed by the first fragment added to the line. - Pending trailing metrics are consumed by the last fragment added to the line. Like before, the position of a line box fragment is the top left of its content box. However, fragments are placed horizontally along the line with space inserted for padding and border. InlineNode::paint() now expands the content rect as appropriate when painting background and borders. Note that margins and margin collapsing is not yet implemented. This makes the eyes on ACID2 horizontally centered. :^)
2022-02-06LibWeb: Rename Layout::Box::size() to content_size()Andreas Kling
This property represents the CSS content size, so let's reduce ambiguity by using the spec terminology. We also bring a bunch of related functions along for the ride.
2022-01-23LibWeb: Make LineBuilder assign height to empty line boxesAndreas Kling
This ensures that <br> produces empty line boxes with the line-height property as their height.
2022-01-23LibWeb: Don't do horizontal inline line layout twice for last lineAndreas Kling
After pruning empty last line boxes, we now avoid re-running the horizontal fragment positioning step, since that would be wasted work.
2022-01-23LibWeb: Align inline-level boxes to the baseline of the line boxAndreas Kling
Vertical inline alignment is still very unsophisticated, but let's at least put everything within each line on the same baseline.
2022-01-23LibWeb: Avoid creating an empty first line box in block containersAndreas Kling
2022-01-23LibWeb: Make LineBuilder respect LayoutMode::OnlyRequiredLineBreaksAndreas Kling
In this layout mode, we should only break when forced (e.g by an explicit <br> tag.) This is used when determining intrinsic sizes.)
2022-01-23LibWeb: Dimension inline-block boxes before deciding about line breaksAndreas Kling
We won't know if we need to break before the inline-block box until after we've dimensioned it.