diff options
author | Andreas Kling <kling@serenityos.org> | 2020-05-26 21:53:10 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-05-26 22:02:27 +0200 |
commit | f01af6231329249594451810927dda9ea94bf444 (patch) | |
tree | 9b7ab13d2d57746f0e5190c6e36549003c1b055f /Libraries/LibWeb | |
parent | 4e8bcda4d11c0b6fdca9d23bbf639eed9a35580f (diff) | |
download | serenity-f01af6231329249594451810927dda9ea94bf444.zip |
LibWeb: Basic support for display:inline-block with width:auto
We now implement the somewhat fuzzy shrink-to-fit algorithm when laying
out inline-block elements with both block and inline children.
Shrink-to-fit works by doing two speculative layouts of the entire
subtree inside the current block, to compute two things:
1. Preferred minimum width: If we made a line break at every chance we
had, how wide would the widest line be?
2. Preferred width: We break only when explicitly told to (e.g "<br>")
How wide would the widest line be?
We then shrink the width of the inline-block element to an appropriate
value based on the above, taking the available width in the containing
block into consideration (sans all the box model fluff.)
To make the speculative layouts possible, plumb a LayoutMode enum
throughout the layout system since it needs to be respected in various
places.
Note that this is quite hackish and I'm sure there are smarter ways to
do a lot of this. But it does kinda work! :^)
Diffstat (limited to 'Libraries/LibWeb')
24 files changed, 182 insertions, 95 deletions
diff --git a/Libraries/LibWeb/Layout/LayoutBlock.cpp b/Libraries/LibWeb/Layout/LayoutBlock.cpp index 4d83f405be..aa525c186a 100644 --- a/Libraries/LibWeb/Layout/LayoutBlock.cpp +++ b/Libraries/LibWeb/Layout/LayoutBlock.cpp @@ -53,22 +53,27 @@ LayoutNode& LayoutBlock::inline_wrapper() return *last_child(); } -void LayoutBlock::layout() +void LayoutBlock::layout(LayoutMode line_break_policy) { compute_width(); if (!is_inline()) compute_position(); - if (children_are_inline()) - layout_inline_children(); - else - layout_block_children(); + layout_children(line_break_policy); compute_height(); } -void LayoutBlock::layout_block_children() +void LayoutBlock::layout_children(LayoutMode line_break_policy) +{ + if (children_are_inline()) + layout_inline_children(line_break_policy); + else + layout_block_children(line_break_policy); +} + +void LayoutBlock::layout_block_children(LayoutMode line_break_policy) { ASSERT(!children_are_inline()); float content_height = 0; @@ -77,19 +82,27 @@ void LayoutBlock::layout_block_children() if (child.is_inline()) return; auto& child_block = static_cast<LayoutBlock&>(child); - child_block.layout(); + child_block.layout(line_break_policy); content_height = child_block.rect().bottom() + child_block.box_model().full_margin().bottom - rect().top(); }); + if (line_break_policy != LayoutMode::Default) { + float max_width = 0; + for_each_child([&](auto& child) { + if (child.is_box()) + max_width = max(max_width, to<LayoutBox>(child).width()); + }); + rect().set_width(max_width); + } rect().set_height(content_height); } -void LayoutBlock::layout_inline_children() +void LayoutBlock::layout_inline_children(LayoutMode line_break_policy) { ASSERT(children_are_inline()); m_line_boxes.clear(); for_each_child([&](auto& child) { ASSERT(child.is_inline()); - child.split_into_lines(*this); + child.split_into_lines(*this, line_break_policy); }); for (auto& line_box : m_line_boxes) { @@ -112,6 +125,8 @@ void LayoutBlock::layout_inline_children() else if (text_align_string == "justify") text_align = CSS::ValueID::Justify; + float max_linebox_width = 0; + for (auto& line_box : m_line_boxes) { float max_height = min_line_height; for (auto& fragment : line_box.fragments()) { @@ -173,18 +188,24 @@ void LayoutBlock::layout_inline_children() if (fragment.layout_node().is_inline_block()) { auto& inline_block = const_cast<LayoutBlock&>(to<LayoutBlock>(fragment.layout_node())); inline_block.set_rect(fragment.rect()); - inline_block.layout(); + inline_block.layout(line_break_policy); } float final_line_box_width = 0; for (auto& fragment : line_box.fragments()) final_line_box_width += fragment.rect().width(); line_box.m_width = final_line_box_width; + + max_linebox_width = max(max_linebox_width, final_line_box_width); } content_height += max_height; } + if (line_break_policy != LayoutMode::Default) { + rect().set_width(max_linebox_width); + } + rect().set_height(content_height); } @@ -226,42 +247,94 @@ void LayoutBlock::compute_width() dbg() << "Total: " << total_px; #endif - // 10.3.3 Block-level, non-replaced elements in normal flow - // If 'width' is not 'auto' and 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' (plus any of 'margin-left' or 'margin-right' that are not 'auto') is larger than the width of the containing block, then any 'auto' values for 'margin-left' or 'margin-right' are, for the following rules, treated as zero. - if (width.is_auto() && total_px > containing_block.width()) { - if (margin_left.is_auto()) - margin_left = zero_value; - if (margin_right.is_auto()) - margin_right = zero_value; - } + if (!is_replaced() && !is_inline()) { + // 10.3.3 Block-level, non-replaced elements in normal flow + // If 'width' is not 'auto' and 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' (plus any of 'margin-left' or 'margin-right' that are not 'auto') is larger than the width of the containing block, then any 'auto' values for 'margin-left' or 'margin-right' are, for the following rules, treated as zero. + if (width.is_auto() && total_px > containing_block.width()) { + if (margin_left.is_auto()) + margin_left = zero_value; + if (margin_right.is_auto()) + margin_right = zero_value; + } - // 10.3.3 cont'd. - auto underflow_px = containing_block.width() - total_px; + // 10.3.3 cont'd. + auto underflow_px = containing_block.width() - total_px; + + if (width.is_auto()) { + if (margin_left.is_auto()) + margin_left = zero_value; + if (margin_right.is_auto()) + margin_right = zero_value; + if (underflow_px >= 0) { + width = Length(underflow_px, Length::Type::Absolute); + } else { + width = zero_value; + margin_right = Length(margin_right.to_px() + underflow_px, Length::Type::Absolute); + } + } else { + if (!margin_left.is_auto() && !margin_right.is_auto()) { + margin_right = Length(margin_right.to_px() + underflow_px, Length::Type::Absolute); + } else if (!margin_left.is_auto() && margin_right.is_auto()) { + margin_right = Length(underflow_px, Length::Type::Absolute); + } else if (margin_left.is_auto() && !margin_right.is_auto()) { + margin_left = Length(underflow_px, Length::Type::Absolute); + } else { // margin_left.is_auto() && margin_right.is_auto() + auto half_of_the_underflow = Length(underflow_px / 2, Length::Type::Absolute); + margin_left = half_of_the_underflow; + margin_right = half_of_the_underflow; + } + } + } else if (!is_replaced() && is_inline_block()) { + + // 10.3.9 'Inline-block', non-replaced elements in normal flow - if (width.is_auto()) { + // A computed value of 'auto' for 'margin-left' or 'margin-right' becomes a used value of '0'. if (margin_left.is_auto()) margin_left = zero_value; if (margin_right.is_auto()) margin_right = zero_value; - if (underflow_px >= 0) { - width = Length(underflow_px, Length::Type::Absolute); - } else { - width = zero_value; - margin_right = Length(margin_right.to_px() + underflow_px, Length::Type::Absolute); - } - } else { - if (!margin_left.is_auto() && !margin_right.is_auto()) { - margin_right = Length(margin_right.to_px() + underflow_px, Length::Type::Absolute); - } else if (!margin_left.is_auto() && margin_right.is_auto()) { - margin_right = Length(underflow_px, Length::Type::Absolute); - } else if (margin_left.is_auto() && !margin_right.is_auto()) { - margin_left = Length(underflow_px, Length::Type::Absolute); - } else { // margin_left.is_auto() && margin_right.is_auto() - auto half_of_the_underflow = Length(underflow_px / 2, Length::Type::Absolute); - margin_left = half_of_the_underflow; - margin_right = half_of_the_underflow; + + // If 'width' is 'auto', the used value is the shrink-to-fit width as for floating elements. + if (width.is_auto()) { + auto greatest_child_width = [&] { + float max_width = 0; + if (children_are_inline()) { + for (auto& box : line_boxes()) { + max_width = max(max_width, box.width()); + } + } else { + for_each_child([&](auto& child) { + if (child.is_box()) + max_width = max(max_width, to<LayoutBox>(child).width()); + }); + } + return max_width; + }; + + // Find the available width: in this case, this is the width of the containing + // block minus the used values of 'margin-left', 'border-left-width', 'padding-left', + // 'padding-right', 'border-right-width', 'margin-right', and the widths of any relevant scroll bars. + + float available_width = containing_block.width() + - margin_left.to_px() - border_left.to_px() - padding_left.to_px() + - padding_right.to_px() - border_right.to_px() - margin_right.to_px(); + + // Calculate the preferred width by formatting the content without breaking lines + // other than where explicit line breaks occur. + layout_children(LayoutMode::OnlyRequiredLineBreaks); + float preferred_width = greatest_child_width(); + + // Also calculate the preferred minimum width, e.g., by trying all possible line breaks. + // CSS 2.2 does not define the exact algorithm. + + layout_children(LayoutMode::AllPossibleLineBreaks); + float preferred_minimum_width = greatest_child_width(); + + // Then the shrink-to-fit width is: min(max(preferred minimum width, available width), preferred width). + width = Length(min(max(preferred_minimum_width, available_width), preferred_width), Length::Type::Absolute); } } + return width; }; @@ -428,11 +501,11 @@ LineBox& LayoutBlock::add_line_box() return m_line_boxes.last(); } -void LayoutBlock::split_into_lines(LayoutBlock& container) +void LayoutBlock::split_into_lines(LayoutBlock& container, LayoutMode line_break_policy) { ASSERT(is_inline()); - layout(); + layout(line_break_policy); auto* line_box = &container.ensure_last_line_box(); if (line_box->width() > 0 && line_box->width() + width() > container.width()) diff --git a/Libraries/LibWeb/Layout/LayoutBlock.h b/Libraries/LibWeb/Layout/LayoutBlock.h index 269eb2b6aa..597a3ac9b2 100644 --- a/Libraries/LibWeb/Layout/LayoutBlock.h +++ b/Libraries/LibWeb/Layout/LayoutBlock.h @@ -40,7 +40,7 @@ public: virtual const char* class_name() const override { return "LayoutBlock"; } - virtual void layout() override; + virtual void layout(LayoutMode = LayoutMode::Default) override; virtual void render(RenderingContext&) override; virtual LayoutNode& inline_wrapper() override; @@ -63,15 +63,17 @@ public: template<typename Callback> void for_each_fragment(Callback) const; - virtual void split_into_lines(LayoutBlock& container) override; + virtual void split_into_lines(LayoutBlock& container, LayoutMode) override; + private: virtual bool is_block() const override { return true; } NonnullRefPtr<StyleProperties> style_for_anonymous_block() const; - void layout_inline_children(); - void layout_block_children(); + void layout_children(LayoutMode); + void layout_inline_children(LayoutMode); + void layout_block_children(LayoutMode); void compute_width(); void compute_position(); diff --git a/Libraries/LibWeb/Layout/LayoutBreak.cpp b/Libraries/LibWeb/Layout/LayoutBreak.cpp index 9d0d2b768e..b4bcfec26d 100644 --- a/Libraries/LibWeb/Layout/LayoutBreak.cpp +++ b/Libraries/LibWeb/Layout/LayoutBreak.cpp @@ -39,7 +39,7 @@ LayoutBreak::~LayoutBreak() { } -void LayoutBreak::split_into_lines(LayoutBlock& block) +void LayoutBreak::split_into_lines(LayoutBlock& block, LayoutMode) { block.add_line_box(); } diff --git a/Libraries/LibWeb/Layout/LayoutBreak.h b/Libraries/LibWeb/Layout/LayoutBreak.h index 1fb28540d1..5c3dfb0986 100644 --- a/Libraries/LibWeb/Layout/LayoutBreak.h +++ b/Libraries/LibWeb/Layout/LayoutBreak.h @@ -40,7 +40,7 @@ public: private: virtual const char* class_name() const override { return "LayoutBreak"; } - virtual void split_into_lines(LayoutBlock&) override; + virtual void split_into_lines(LayoutBlock&, LayoutMode) override; }; } diff --git a/Libraries/LibWeb/Layout/LayoutCanvas.cpp b/Libraries/LibWeb/Layout/LayoutCanvas.cpp index deb316ea49..820ca07dfe 100644 --- a/Libraries/LibWeb/Layout/LayoutCanvas.cpp +++ b/Libraries/LibWeb/Layout/LayoutCanvas.cpp @@ -40,11 +40,11 @@ LayoutCanvas::~LayoutCanvas() { } -void LayoutCanvas::layout() +void LayoutCanvas::layout(LayoutMode line_break_policy) { rect().set_width(node().requested_width()); rect().set_height(node().requested_height()); - LayoutReplaced::layout(); + LayoutReplaced::layout(line_break_policy); } void LayoutCanvas::render(RenderingContext& context) diff --git a/Libraries/LibWeb/Layout/LayoutCanvas.h b/Libraries/LibWeb/Layout/LayoutCanvas.h index e2613fc780..931d36aa89 100644 --- a/Libraries/LibWeb/Layout/LayoutCanvas.h +++ b/Libraries/LibWeb/Layout/LayoutCanvas.h @@ -38,7 +38,7 @@ public: LayoutCanvas(const HTMLCanvasElement&, NonnullRefPtr<StyleProperties>); virtual ~LayoutCanvas() override; - virtual void layout() override; + virtual void layout(LayoutMode = LayoutMode::Default) override; virtual void render(RenderingContext&) override; const HTMLCanvasElement& node() const { return static_cast<const HTMLCanvasElement&>(LayoutReplaced::node()); } diff --git a/Libraries/LibWeb/Layout/LayoutDocument.cpp b/Libraries/LibWeb/Layout/LayoutDocument.cpp index d01a9787dd..7e6b77fe09 100644 --- a/Libraries/LibWeb/Layout/LayoutDocument.cpp +++ b/Libraries/LibWeb/Layout/LayoutDocument.cpp @@ -40,12 +40,12 @@ LayoutDocument::~LayoutDocument() { } -void LayoutDocument::layout() +void LayoutDocument::layout(LayoutMode line_break_policy) { ASSERT(document().frame()); rect().set_width(document().frame()->size().width()); - LayoutNode::layout(); + LayoutNode::layout(line_break_policy); ASSERT(!children_are_inline()); diff --git a/Libraries/LibWeb/Layout/LayoutDocument.h b/Libraries/LibWeb/Layout/LayoutDocument.h index 89dd0f3726..c89353b8ca 100644 --- a/Libraries/LibWeb/Layout/LayoutDocument.h +++ b/Libraries/LibWeb/Layout/LayoutDocument.h @@ -38,7 +38,7 @@ public: const Document& node() const { return static_cast<const Document&>(*LayoutNode::node()); } virtual const char* class_name() const override { return "LayoutDocument"; } - virtual void layout() override; + virtual void layout(LayoutMode = LayoutMode::Default) override; const LayoutRange& selection() const { return m_selection; } LayoutRange& selection() { return m_selection; } diff --git a/Libraries/LibWeb/Layout/LayoutImage.cpp b/Libraries/LibWeb/Layout/LayoutImage.cpp index 20a9538eff..d12246e4c9 100644 --- a/Libraries/LibWeb/Layout/LayoutImage.cpp +++ b/Libraries/LibWeb/Layout/LayoutImage.cpp @@ -40,7 +40,7 @@ LayoutImage::~LayoutImage() { } -void LayoutImage::layout() +void LayoutImage::layout(LayoutMode line_break_policy) { if (node().preferred_width() && node().preferred_height()) { rect().set_width(node().preferred_width()); @@ -57,7 +57,7 @@ void LayoutImage::layout() rect().set_height(16); } - LayoutReplaced::layout(); + LayoutReplaced::layout(line_break_policy); } void LayoutImage::render(RenderingContext& context) diff --git a/Libraries/LibWeb/Layout/LayoutImage.h b/Libraries/LibWeb/Layout/LayoutImage.h index 526d4832fc..2112bab079 100644 --- a/Libraries/LibWeb/Layout/LayoutImage.h +++ b/Libraries/LibWeb/Layout/LayoutImage.h @@ -38,7 +38,7 @@ public: LayoutImage(const HTMLImageElement&, NonnullRefPtr<StyleProperties>); virtual ~LayoutImage() override; - virtual void layout() override; + virtual void layout(LayoutMode = LayoutMode::Default) override; virtual void render(RenderingContext&) override; const HTMLImageElement& node() const { return static_cast<const HTMLImageElement&>(LayoutReplaced::node()); } diff --git a/Libraries/LibWeb/Layout/LayoutListItem.cpp b/Libraries/LibWeb/Layout/LayoutListItem.cpp index 2d14fb0ce6..c82c2cea08 100644 --- a/Libraries/LibWeb/Layout/LayoutListItem.cpp +++ b/Libraries/LibWeb/Layout/LayoutListItem.cpp @@ -38,14 +38,14 @@ LayoutListItem::~LayoutListItem() { } -void LayoutListItem::layout() +void LayoutListItem::layout(LayoutMode line_break_policy) { if (m_marker) { remove_child(*m_marker); m_marker = nullptr; } - LayoutBlock::layout(); + LayoutBlock::layout(line_break_policy); if (!m_marker) { m_marker = adopt(*new LayoutListItemMarker); diff --git a/Libraries/LibWeb/Layout/LayoutListItem.h b/Libraries/LibWeb/Layout/LayoutListItem.h index 7c3987570c..b5d4b03a24 100644 --- a/Libraries/LibWeb/Layout/LayoutListItem.h +++ b/Libraries/LibWeb/Layout/LayoutListItem.h @@ -38,7 +38,7 @@ public: LayoutListItem(const Element&, NonnullRefPtr<StyleProperties>); virtual ~LayoutListItem() override; - virtual void layout() override; + virtual void layout(LayoutMode = LayoutMode::Default) override; private: virtual const char* class_name() const override { return "LayoutListItem"; } diff --git a/Libraries/LibWeb/Layout/LayoutNode.cpp b/Libraries/LibWeb/Layout/LayoutNode.cpp index 5fd339c291..c784ac302c 100644 --- a/Libraries/LibWeb/Layout/LayoutNode.cpp +++ b/Libraries/LibWeb/Layout/LayoutNode.cpp @@ -46,10 +46,10 @@ LayoutNode::~LayoutNode() m_node->set_layout_node({}, nullptr); } -void LayoutNode::layout() +void LayoutNode::layout(LayoutMode line_break_policy) { - for_each_child([](auto& child) { - child.layout(); + for_each_child([&](auto& child) { + child.layout(line_break_policy); }); } @@ -111,11 +111,11 @@ LayoutDocument& LayoutNode::root() return *document().layout_node(); } -void LayoutNode::split_into_lines(LayoutBlock& container) +void LayoutNode::split_into_lines(LayoutBlock& container, LayoutMode line_break_policy) { for_each_child([&](auto& child) { if (child.is_inline()) { - child.split_into_lines(container); + child.split_into_lines(container, line_break_policy); } else { // FIXME: Support block children of inlines. } diff --git a/Libraries/LibWeb/Layout/LayoutNode.h b/Libraries/LibWeb/Layout/LayoutNode.h index a9d64d61f5..b9a5adbb13 100644 --- a/Libraries/LibWeb/Layout/LayoutNode.h +++ b/Libraries/LibWeb/Layout/LayoutNode.h @@ -99,7 +99,13 @@ public: bool is_inline_block() const { return is_inline() && is_block(); } - virtual void layout(); + enum class LayoutMode { + Default, + AllPossibleLineBreaks, + OnlyRequiredLineBreaks, + }; + + virtual void layout(LayoutMode); virtual void render(RenderingContext&); const LayoutBlock* containing_block() const; @@ -111,11 +117,11 @@ public: LayoutNodeWithStyle* parent(); const LayoutNodeWithStyle* parent() const; - void inserted_into(LayoutNode&) {} - void removed_from(LayoutNode&) {} - void children_changed() {} + void inserted_into(LayoutNode&) { } + void removed_from(LayoutNode&) { } + void children_changed() { } - virtual void split_into_lines(LayoutBlock& container); + virtual void split_into_lines(LayoutBlock& container, LayoutMode); bool is_visible() const { return m_visible; } void set_visible(bool visible) { m_visible = visible; } @@ -161,7 +167,7 @@ private: class LayoutNodeWithStyle : public LayoutNode { public: - virtual ~LayoutNodeWithStyle() override {} + virtual ~LayoutNodeWithStyle() override { } const StyleProperties& style() const { return m_style; } void set_style(const StyleProperties& style) { m_style = style; } diff --git a/Libraries/LibWeb/Layout/LayoutReplaced.cpp b/Libraries/LibWeb/Layout/LayoutReplaced.cpp index e98316ea73..904cf9769a 100644 --- a/Libraries/LibWeb/Layout/LayoutReplaced.cpp +++ b/Libraries/LibWeb/Layout/LayoutReplaced.cpp @@ -41,9 +41,9 @@ LayoutReplaced::~LayoutReplaced() { } -void LayoutReplaced::split_into_lines(LayoutBlock& container) +void LayoutReplaced::split_into_lines(LayoutBlock& container, LayoutMode line_break_policy) { - layout(); + layout(line_break_policy); auto* line_box = &container.ensure_last_line_box(); if (line_box->width() > 0 && line_box->width() + width() > container.width()) diff --git a/Libraries/LibWeb/Layout/LayoutReplaced.h b/Libraries/LibWeb/Layout/LayoutReplaced.h index d19c4e891a..f0446b116c 100644 --- a/Libraries/LibWeb/Layout/LayoutReplaced.h +++ b/Libraries/LibWeb/Layout/LayoutReplaced.h @@ -41,7 +41,7 @@ public: private: virtual const char* class_name() const override { return "LayoutReplaced"; } - virtual void split_into_lines(LayoutBlock& container) override; + virtual void split_into_lines(LayoutBlock& container, LayoutMode) override; }; template<> diff --git a/Libraries/LibWeb/Layout/LayoutTable.cpp b/Libraries/LibWeb/Layout/LayoutTable.cpp index 782e27e76d..8ad59649c0 100644 --- a/Libraries/LibWeb/Layout/LayoutTable.cpp +++ b/Libraries/LibWeb/Layout/LayoutTable.cpp @@ -39,10 +39,9 @@ LayoutTable::~LayoutTable() { } -void LayoutTable::layout() +void LayoutTable::layout(LayoutMode line_break_policy) { - - LayoutBlock::layout(); + LayoutBlock::layout(line_break_policy); } LayoutTableRow* LayoutTable::first_row() diff --git a/Libraries/LibWeb/Layout/LayoutTable.h b/Libraries/LibWeb/Layout/LayoutTable.h index 5166fc2a94..70b6b940ac 100644 --- a/Libraries/LibWeb/Layout/LayoutTable.h +++ b/Libraries/LibWeb/Layout/LayoutTable.h @@ -37,7 +37,7 @@ public: LayoutTable(const Element&, NonnullRefPtr<StyleProperties>); virtual ~LayoutTable() override; - virtual void layout() override; + virtual void layout(LayoutMode = LayoutMode::Default) override; LayoutTableRow* first_row(); const LayoutTableRow* first_row() const; diff --git a/Libraries/LibWeb/Layout/LayoutTableRow.cpp b/Libraries/LibWeb/Layout/LayoutTableRow.cpp index d8dc1f441c..9d0920d754 100644 --- a/Libraries/LibWeb/Layout/LayoutTableRow.cpp +++ b/Libraries/LibWeb/Layout/LayoutTableRow.cpp @@ -39,9 +39,9 @@ LayoutTableRow::~LayoutTableRow() { } -void LayoutTableRow::layout() +void LayoutTableRow::layout(LayoutMode line_break_policy) { - LayoutBox::layout(); + LayoutBox::layout(line_break_policy); } LayoutTableCell* LayoutTableRow::first_cell() diff --git a/Libraries/LibWeb/Layout/LayoutTableRow.h b/Libraries/LibWeb/Layout/LayoutTableRow.h index c77265fce8..ded261d16f 100644 --- a/Libraries/LibWeb/Layout/LayoutTableRow.h +++ b/Libraries/LibWeb/Layout/LayoutTableRow.h @@ -37,7 +37,7 @@ public: LayoutTableRow(const Element&, NonnullRefPtr<StyleProperties>); virtual ~LayoutTableRow() override; - virtual void layout() override; + virtual void layout(LayoutMode = LayoutMode::Default) override; LayoutTableCell* first_cell(); const LayoutTableCell* first_cell() const; diff --git a/Libraries/LibWeb/Layout/LayoutText.cpp b/Libraries/LibWeb/Layout/LayoutText.cpp index 406340b6db..a1d70087d3 100644 --- a/Libraries/LibWeb/Layout/LayoutText.cpp +++ b/Libraries/LibWeb/Layout/LayoutText.cpp @@ -95,7 +95,7 @@ void LayoutText::render_fragment(RenderingContext& context, const LineBoxFragmen } template<typename Callback> -void LayoutText::for_each_chunk(Callback callback, bool do_wrap_lines, bool do_wrap_breaks) const +void LayoutText::for_each_chunk(Callback callback, LayoutMode line_break_policy, bool do_wrap_lines, bool do_wrap_breaks) const { Utf8View view(m_text_for_rendering); if (view.is_empty()) @@ -103,7 +103,10 @@ void LayoutText::for_each_chunk(Callback callback, bool do_wrap_lines, bool do_w auto start_of_chunk = view.begin(); - auto commit_chunk = [&](auto it, bool has_breaking_newline) { + auto commit_chunk = [&](auto it, bool has_breaking_newline, bool must_commit = false) { + if (line_break_policy == LayoutMode::OnlyRequiredLineBreaks && !must_commit) + return; + int start = view.byte_offset_of(start_of_chunk); int length = view.byte_offset_of(it) - view.byte_offset_of(start_of_chunk); @@ -117,6 +120,9 @@ void LayoutText::for_each_chunk(Callback callback, bool do_wrap_lines, bool do_w bool last_was_space = isspace(*view.begin()); bool last_was_newline = false; for (auto it = view.begin(); it != view.end();) { + if (line_break_policy == LayoutMode::AllPossibleLineBreaks) { + commit_chunk(it, false); + } if (last_was_newline) { last_was_newline = false; commit_chunk(it, true); @@ -137,10 +143,10 @@ void LayoutText::for_each_chunk(Callback callback, bool do_wrap_lines, bool do_w if (last_was_newline) commit_chunk(view.end(), true); if (start_of_chunk != view.end()) - commit_chunk(view.end(), false); + commit_chunk(view.end(), false, true); } -void LayoutText::split_into_lines_by_rules(LayoutBlock& container, bool do_collapse, bool do_wrap_lines, bool do_wrap_breaks) +void LayoutText::split_into_lines_by_rules(LayoutBlock& container, LayoutMode line_break_policy, bool do_collapse, bool do_wrap_lines, bool do_wrap_breaks) { auto& font = style().font(); float space_width = font.glyph_width(' ') + font.glyph_spacing(); @@ -182,10 +188,11 @@ void LayoutText::split_into_lines_by_rules(LayoutBlock& container, bool do_colla }; Vector<Chunk> chunks; - for_each_chunk([&](const Utf8View& view, int start, int length, bool is_break) { - chunks.append({ Utf8View(view), start, length, is_break }); - }, - do_wrap_lines, do_wrap_breaks); + for_each_chunk( + [&](const Utf8View& view, int start, int length, bool is_break) { + chunks.append({ Utf8View(view), start, length, is_break }); + }, + line_break_policy, do_wrap_lines, do_wrap_breaks); for (size_t i = 0; i < chunks.size(); ++i) { auto& chunk = chunks[i]; @@ -229,7 +236,7 @@ void LayoutText::split_into_lines_by_rules(LayoutBlock& container, bool do_colla } } -void LayoutText::split_into_lines(LayoutBlock& container) +void LayoutText::split_into_lines(LayoutBlock& container, LayoutMode line_break_policy) { bool do_collapse = true; bool do_wrap_lines = true; @@ -254,7 +261,7 @@ void LayoutText::split_into_lines(LayoutBlock& container) do_wrap_breaks = true; } - split_into_lines_by_rules(container, do_collapse, do_wrap_lines, do_wrap_breaks); + split_into_lines_by_rules(container, line_break_policy, do_collapse, do_wrap_lines, do_wrap_breaks); } } diff --git a/Libraries/LibWeb/Layout/LayoutText.h b/Libraries/LibWeb/Layout/LayoutText.h index 0ab6373e44..bc8aac3a3e 100644 --- a/Libraries/LibWeb/Layout/LayoutText.h +++ b/Libraries/LibWeb/Layout/LayoutText.h @@ -48,15 +48,15 @@ public: void render_fragment(RenderingContext&, const LineBoxFragment&) const; - virtual void split_into_lines(LayoutBlock& container) override; + virtual void split_into_lines(LayoutBlock& container, LayoutMode) override; const StyleProperties& style() const { return parent()->style(); } private: - void split_into_lines_by_rules(LayoutBlock& container, bool do_collapse, bool do_wrap_lines, bool do_wrap_breaks); + void split_into_lines_by_rules(LayoutBlock& container, LayoutMode, bool do_collapse, bool do_wrap_lines, bool do_wrap_breaks); template<typename Callback> - void for_each_chunk(Callback, bool do_wrap_lines, bool do_wrap_breaks) const; + void for_each_chunk(Callback, LayoutMode, bool do_wrap_lines, bool do_wrap_breaks) const; String m_text_for_rendering; }; diff --git a/Libraries/LibWeb/Layout/LayoutWidget.cpp b/Libraries/LibWeb/Layout/LayoutWidget.cpp index b4f80290ea..acc02befcc 100644 --- a/Libraries/LibWeb/Layout/LayoutWidget.cpp +++ b/Libraries/LibWeb/Layout/LayoutWidget.cpp @@ -43,10 +43,10 @@ LayoutWidget::~LayoutWidget() widget().remove_from_parent(); } -void LayoutWidget::layout() +void LayoutWidget::layout(LayoutMode line_break_policy) { rect().set_size(widget().width(), widget().height()); - LayoutReplaced::layout(); + LayoutReplaced::layout(line_break_policy); widget().move_to(rect().x(), rect().y()); } diff --git a/Libraries/LibWeb/Layout/LayoutWidget.h b/Libraries/LibWeb/Layout/LayoutWidget.h index ab24c8dafc..e11c31f5cd 100644 --- a/Libraries/LibWeb/Layout/LayoutWidget.h +++ b/Libraries/LibWeb/Layout/LayoutWidget.h @@ -35,7 +35,7 @@ public: LayoutWidget(const Element&, GUI::Widget&); virtual ~LayoutWidget() override; - virtual void layout() override; + virtual void layout(LayoutMode = LayoutMode::Default) override; virtual void render(RenderingContext&) override; GUI::Widget& widget() { return m_widget; } |