LibWeb: Create "empty" line box fragments for inline elements

In order for inline elements (e.g <span>) to contribute padding etc.
to line boxes, we now create special "leading" and "trailing" fragments
for Layout::InlineNode and size them according to the horizontal
padding values.

The height of these fragments is taken from the tallest fragment on the
line. (Perhaps we should stop having per-fragment heights and just keep
a single height per line box, but that's a separate issue.)

In order to make things look nice, we now also adjust the height of all
fragments on a line so that nobody is shorter than the CSS line-height.
This commit is contained in:
Andreas Kling 2020-12-03 19:21:33 +01:00
parent 311e1039b5
commit d59ec3ab85
Notes: sideshowbarker 2024-07-19 01:04:21 +09:00
7 changed files with 56 additions and 4 deletions

View file

@ -112,6 +112,12 @@ void InlineFormattingContext::run(LayoutMode layout_mode)
for (size_t i = 0; i < line_box.fragments().size(); ++i) {
auto& fragment = line_box.fragments()[i];
if (fragment.type() == LineBoxFragment::Type::Leading || fragment.type() == LineBoxFragment::Type::Trailing) {
fragment.set_height(max_height);
} else {
fragment.set_height(max(min_line_height, fragment.height()));
}
// Vertically align everyone's bottom to the line.
// FIXME: Support other kinds of vertical alignment.
fragment.set_offset({ roundf(x_offset + fragment.offset().x()), content_height + (max_height - fragment.height()) - (line_spacing / 2) });

View file

@ -24,7 +24,9 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibGfx/Painter.h>
#include <LibWeb/DOM/Element.h>
#include <LibWeb/Layout/BlockBox.h>
#include <LibWeb/Layout/InlineNode.h>
namespace Web::Layout {
@ -39,4 +41,30 @@ InlineNode::~InlineNode()
{
}
void InlineNode::split_into_lines(BlockBox& containing_block, LayoutMode layout_mode)
{
if (!style().padding().left.is_undefined_or_auto()) {
float padding_left = style().padding().left.resolved(CSS::Length::make_px(0), *this, containing_block.width()).to_px(*this);
containing_block.ensure_last_line_box().add_fragment(*this, 0, 0, padding_left, 0, LineBoxFragment::Type::Leading);
}
Node::split_into_lines(containing_block, layout_mode);
if (!style().padding().right.is_undefined_or_auto()) {
float padding_right = style().padding().right.resolved(CSS::Length::make_px(0), *this, containing_block.width()).to_px(*this);
containing_block.ensure_last_line_box().add_fragment(*this, 0, 0, padding_right, 0, LineBoxFragment::Type::Trailing);
}
}
void InlineNode::paint_fragment(PaintContext& context, const LineBoxFragment& fragment, PaintPhase phase) const
{
auto& painter = context.painter();
if (phase == PaintPhase::Background) {
auto background_color = specified_style().property(CSS::PropertyID::BackgroundColor);
if (background_color.has_value() && background_color.value()->is_color())
painter.fill_rect(enclosing_int_rect(fragment.absolute_rect()), background_color.value()->to_color(document()));
}
}
}

View file

@ -36,6 +36,10 @@ public:
virtual ~InlineNode() override;
virtual const char* class_name() const override { return "InlineNode"; }
void paint_fragment(PaintContext&, const LineBoxFragment&, PaintPhase) const;
virtual void split_into_lines(BlockBox& containing_block, LayoutMode) override;
private:
virtual bool is_inline_node() const final { return true; }
};

View file

@ -33,7 +33,7 @@
namespace Web::Layout {
void LineBox::add_fragment(const Node& layout_node, int start, int length, int width, int height)
void LineBox::add_fragment(const Node& layout_node, int start, int length, int width, int height, LineBoxFragment::Type fragment_type)
{
bool text_align_is_justify = layout_node.style().text_align() == CSS::TextAlign::Justify;
if (!text_align_is_justify && !m_fragments.is_empty() && &m_fragments.last().layout_node() == &layout_node) {
@ -42,7 +42,7 @@ void LineBox::add_fragment(const Node& layout_node, int start, int length, int w
m_fragments.last().m_length = (start - m_fragments.last().m_start) + length;
m_fragments.last().set_width(m_fragments.last().width() + width);
} else {
m_fragments.append(make<LineBoxFragment>(layout_node, start, length, Gfx::FloatPoint(m_width, 0.0f), Gfx::FloatSize(width, height)));
m_fragments.append(make<LineBoxFragment>(layout_node, start, length, Gfx::FloatPoint(m_width, 0.0f), Gfx::FloatSize(width, height), fragment_type));
}
m_width += width;

View file

@ -38,7 +38,7 @@ public:
float width() const { return m_width; }
void add_fragment(const Node& layout_node, int start, int length, int width, int height);
void add_fragment(const Node& layout_node, int start, int length, int width, int height, LineBoxFragment::Type = LineBoxFragment::Type::Normal);
const NonnullOwnPtrVector<LineBoxFragment>& fragments() const { return m_fragments; }
NonnullOwnPtrVector<LineBoxFragment>& fragments() { return m_fragments; }

View file

@ -29,6 +29,7 @@
#include <LibWeb/Layout/InitialContainingBlockBox.h>
#include <LibWeb/Layout/LineBoxFragment.h>
#include <LibWeb/Layout/TextNode.h>
#include <LibWeb/Layout/InlineNode.h>
#include <LibWeb/Painting/PaintContext.h>
#include <ctype.h>
@ -41,6 +42,9 @@ void LineBoxFragment::paint(PaintContext& context, PaintPhase phase)
return;
}
if (is<InlineNode>(layout_node()))
downcast<InlineNode>(layout_node()).paint_fragment(context, *this, phase);
if (is<TextNode>(layout_node()))
downcast<TextNode>(layout_node()).paint_fragment(context, *this, phase);
}

View file

@ -37,12 +37,19 @@ class LineBoxFragment : public Weakable<LineBoxFragment> {
friend class LineBox;
public:
LineBoxFragment(const Node& layout_node, int start, int length, const Gfx::FloatPoint& offset, const Gfx::FloatSize& size)
enum class Type {
Normal,
Leading,
Trailing,
};
LineBoxFragment(const Node& layout_node, int start, int length, const Gfx::FloatPoint& offset, const Gfx::FloatSize& size, Type type)
: m_layout_node(layout_node)
, m_start(start)
, m_length(length)
, m_offset(offset)
, m_size(size)
, m_type(type)
{
}
@ -50,12 +57,14 @@ public:
int start() const { return m_start; }
int length() const { return m_length; }
const Gfx::FloatRect absolute_rect() const;
Type type() const { return m_type; }
const Gfx::FloatPoint& offset() const { return m_offset; }
void set_offset(const Gfx::FloatPoint& offset) { m_offset = offset; }
const Gfx::FloatSize& size() const { return m_size; }
void set_width(float width) { m_size.set_width(width); }
void set_height(float height) { m_size.set_height(height); }
float width() const { return m_size.width(); }
float height() const { return m_size.height(); }
@ -77,6 +86,7 @@ private:
int m_length { 0 };
Gfx::FloatPoint m_offset;
Gfx::FloatSize m_size;
Type m_type { Type::Normal };
};
}