mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-01 13:49:16 +00:00
LibWeb: Layout inline elements respective of writing-mode
Use the `writing-mode` property to determine what values should be used for computing each element's rect on the screen. If it is a vertical mode, swap the inline and block, lengths and offsets. This only lays out whole inline formatting contexts vertically, and does not currently support mixing the two orientations in a single context.
This commit is contained in:
parent
ede9012723
commit
80e7e6dd7d
Notes:
github-actions[bot]
2024-11-03 16:03:04 +00:00
Author: https://github.com/BenJilks
Commit: 80e7e6dd7d
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2135
Reviewed-by: https://github.com/kalenikaliaksandr ✅
9 changed files with 65 additions and 12 deletions
11
Tests/LibWeb/Layout/expected/writing-modes-vertical-rl.txt
Normal file
11
Tests/LibWeb/Layout/expected/writing-modes-vertical-rl.txt
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
|
||||||
|
BlockContainer <html> at (0,0) content-size 800x186.265625 [BFC] children: not-inline
|
||||||
|
BlockContainer <body> at (8,8) content-size 784x170.265625 children: inline
|
||||||
|
frag 0 from TextNode start: 0, length: 24, rect: [775,8 17x170.265625] baseline: 13.296875
|
||||||
|
"Well, hello friends! :-)"
|
||||||
|
TextNode <#text>
|
||||||
|
|
||||||
|
ViewportPaintable (Viewport<#document>) [0,0 800x600]
|
||||||
|
PaintableWithLines (BlockContainer<HTML>) [0,0 800x186.265625]
|
||||||
|
PaintableWithLines (BlockContainer<BODY>) [8,8 784x170.265625]
|
||||||
|
TextPaintable (TextNode<#text>)
|
6
Tests/LibWeb/Layout/input/writing-modes-vertical-rl.html
Normal file
6
Tests/LibWeb/Layout/input/writing-modes-vertical-rl.html
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
writing-mode: vertical-rl;
|
||||||
|
}
|
||||||
|
</style>Well, hello friends! :-)
|
|
@ -250,9 +250,10 @@ void InlineFormattingContext::generate_line_boxes()
|
||||||
line_boxes.clear_with_capacity();
|
line_boxes.clear_with_capacity();
|
||||||
|
|
||||||
auto direction = m_context_box->computed_values().direction();
|
auto direction = m_context_box->computed_values().direction();
|
||||||
|
auto writing_mode = m_context_box->computed_values().writing_mode();
|
||||||
|
|
||||||
InlineLevelIterator iterator(*this, m_state, containing_block(), m_containing_block_used_values, m_layout_mode);
|
InlineLevelIterator iterator(*this, m_state, containing_block(), m_containing_block_used_values, m_layout_mode);
|
||||||
LineBuilder line_builder(*this, m_state, m_containing_block_used_values, direction);
|
LineBuilder line_builder(*this, m_state, m_containing_block_used_values, direction, writing_mode);
|
||||||
|
|
||||||
// NOTE: When we ignore collapsible whitespace chunks at the start of a line,
|
// NOTE: When we ignore collapsible whitespace chunks at the start of a line,
|
||||||
// we have to remember how much start margin that chunk had in the inline
|
// we have to remember how much start margin that chunk had in the inline
|
||||||
|
|
|
@ -16,6 +16,27 @@
|
||||||
|
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
|
CSSPixels LineBox::width() const
|
||||||
|
{
|
||||||
|
if (m_writing_mode != CSS::WritingMode::HorizontalTb)
|
||||||
|
return m_block_length;
|
||||||
|
return m_inline_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSSPixels LineBox::height() const
|
||||||
|
{
|
||||||
|
if (m_writing_mode != CSS::WritingMode::HorizontalTb)
|
||||||
|
return m_inline_length;
|
||||||
|
return m_block_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSSPixels LineBox::bottom() const
|
||||||
|
{
|
||||||
|
if (m_writing_mode != CSS::WritingMode::HorizontalTb)
|
||||||
|
return m_inline_length;
|
||||||
|
return m_bottom;
|
||||||
|
}
|
||||||
|
|
||||||
void LineBox::add_fragment(Node const& layout_node, int start, int length, CSSPixels leading_size, CSSPixels trailing_size, CSSPixels leading_margin, CSSPixels trailing_margin, CSSPixels content_width, CSSPixels content_height, CSSPixels border_box_top, CSSPixels border_box_bottom, RefPtr<Gfx::GlyphRun> glyph_run)
|
void LineBox::add_fragment(Node const& layout_node, int start, int length, CSSPixels leading_size, CSSPixels trailing_size, CSSPixels leading_margin, CSSPixels trailing_margin, CSSPixels content_width, CSSPixels content_height, CSSPixels border_box_top, CSSPixels border_box_bottom, RefPtr<Gfx::GlyphRun> glyph_run)
|
||||||
{
|
{
|
||||||
bool text_align_is_justify = layout_node.computed_values().text_align() == CSS::TextAlign::Justify;
|
bool text_align_is_justify = layout_node.computed_values().text_align() == CSS::TextAlign::Justify;
|
||||||
|
@ -27,7 +48,7 @@ void LineBox::add_fragment(Node const& layout_node, int start, int length, CSSPi
|
||||||
} else {
|
} else {
|
||||||
CSSPixels inline_offset = leading_margin + leading_size + m_inline_length;
|
CSSPixels inline_offset = leading_margin + leading_size + m_inline_length;
|
||||||
CSSPixels block_offset = 0;
|
CSSPixels block_offset = 0;
|
||||||
m_fragments.append(LineBoxFragment { layout_node, start, length, inline_offset, block_offset, content_width, content_height, border_box_top, m_direction, move(glyph_run) });
|
m_fragments.append(LineBoxFragment { layout_node, start, length, inline_offset, block_offset, content_width, content_height, border_box_top, m_direction, m_writing_mode, move(glyph_run) });
|
||||||
}
|
}
|
||||||
m_inline_length += leading_margin + leading_size + content_width + trailing_size + trailing_margin;
|
m_inline_length += leading_margin + leading_size + content_width + trailing_size + trailing_margin;
|
||||||
m_block_length = max(m_block_length, content_height + border_box_top + border_box_bottom);
|
m_block_length = max(m_block_length, content_height + border_box_top + border_box_bottom);
|
||||||
|
|
|
@ -13,14 +13,15 @@ namespace Web::Layout {
|
||||||
|
|
||||||
class LineBox {
|
class LineBox {
|
||||||
public:
|
public:
|
||||||
LineBox(CSS::Direction direction)
|
LineBox(CSS::Direction direction, CSS::WritingMode writing_mode)
|
||||||
: m_direction(direction)
|
: m_direction(direction)
|
||||||
|
, m_writing_mode(writing_mode)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
CSSPixels width() const { return m_inline_length; }
|
CSSPixels width() const;
|
||||||
CSSPixels height() const { return m_block_length; }
|
CSSPixels height() const;
|
||||||
CSSPixels bottom() const { return m_bottom; }
|
CSSPixels bottom() const;
|
||||||
|
|
||||||
CSSPixels inline_length() const { return m_inline_length; }
|
CSSPixels inline_length() const { return m_inline_length; }
|
||||||
CSSPixels block_length() const { return m_block_length; }
|
CSSPixels block_length() const { return m_block_length; }
|
||||||
|
@ -49,6 +50,7 @@ private:
|
||||||
CSSPixels m_bottom { 0 };
|
CSSPixels m_bottom { 0 };
|
||||||
CSSPixels m_baseline { 0 };
|
CSSPixels m_baseline { 0 };
|
||||||
CSS::Direction m_direction { CSS::Direction::Ltr };
|
CSS::Direction m_direction { CSS::Direction::Ltr };
|
||||||
|
CSS::WritingMode m_writing_mode { CSS::WritingMode::HorizontalTb };
|
||||||
|
|
||||||
// The amount of available width that was originally available when creating this line box. Used for text justification.
|
// The amount of available width that was originally available when creating this line box. Used for text justification.
|
||||||
AvailableSize m_original_available_width { AvailableSize::make_indefinite() };
|
AvailableSize m_original_available_width { AvailableSize::make_indefinite() };
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
LineBoxFragment::LineBoxFragment(Node const& layout_node, int start, int length, CSSPixels inline_offset, CSSPixels block_offset, CSSPixels inline_length, CSSPixels block_length, CSSPixels border_box_top, CSS::Direction direction, RefPtr<Gfx::GlyphRun> glyph_run)
|
LineBoxFragment::LineBoxFragment(Node const& layout_node, int start, int length, CSSPixels inline_offset, CSSPixels block_offset, CSSPixels inline_length, CSSPixels block_length, CSSPixels border_box_top, CSS::Direction direction, CSS::WritingMode writing_mode, RefPtr<Gfx::GlyphRun> glyph_run)
|
||||||
: m_layout_node(layout_node)
|
: m_layout_node(layout_node)
|
||||||
, m_start(start)
|
, m_start(start)
|
||||||
, m_length(length)
|
, m_length(length)
|
||||||
|
@ -23,6 +23,7 @@ LineBoxFragment::LineBoxFragment(Node const& layout_node, int start, int length,
|
||||||
, m_block_length(block_length)
|
, m_block_length(block_length)
|
||||||
, m_border_box_top(border_box_top)
|
, m_border_box_top(border_box_top)
|
||||||
, m_direction(direction)
|
, m_direction(direction)
|
||||||
|
, m_writing_mode(writing_mode)
|
||||||
, m_glyph_run(move(glyph_run))
|
, m_glyph_run(move(glyph_run))
|
||||||
{
|
{
|
||||||
if (m_glyph_run) {
|
if (m_glyph_run) {
|
||||||
|
@ -34,11 +35,15 @@ LineBoxFragment::LineBoxFragment(Node const& layout_node, int start, int length,
|
||||||
|
|
||||||
CSSPixelPoint LineBoxFragment::offset() const
|
CSSPixelPoint LineBoxFragment::offset() const
|
||||||
{
|
{
|
||||||
|
if (m_writing_mode != CSS::WritingMode::HorizontalTb)
|
||||||
|
return { m_block_offset, m_inline_offset };
|
||||||
return { m_inline_offset, m_block_offset };
|
return { m_inline_offset, m_block_offset };
|
||||||
}
|
}
|
||||||
|
|
||||||
CSSPixelSize LineBoxFragment::size() const
|
CSSPixelSize LineBoxFragment::size() const
|
||||||
{
|
{
|
||||||
|
if (m_writing_mode != CSS::WritingMode::HorizontalTb)
|
||||||
|
return { m_block_length, m_inline_length };
|
||||||
return { m_inline_length, m_block_length };
|
return { m_inline_length, m_block_length };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ class LineBoxFragment {
|
||||||
friend class LineBox;
|
friend class LineBox;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LineBoxFragment(Node const& layout_node, int start, int length, CSSPixels inline_offset, CSSPixels block_offset, CSSPixels inline_length, CSSPixels block_length, CSSPixels border_box_top, CSS::Direction, RefPtr<Gfx::GlyphRun>);
|
LineBoxFragment(Node const& layout_node, int start, int length, CSSPixels inline_offset, CSSPixels block_offset, CSSPixels inline_length, CSSPixels block_length, CSSPixels border_box_top, CSS::Direction, CSS::WritingMode, RefPtr<Gfx::GlyphRun>);
|
||||||
|
|
||||||
Node const& layout_node() const { return m_layout_node; }
|
Node const& layout_node() const { return m_layout_node; }
|
||||||
int start() const { return m_start; }
|
int start() const { return m_start; }
|
||||||
|
@ -70,6 +70,7 @@ private:
|
||||||
CSSPixels m_border_box_top { 0 };
|
CSSPixels m_border_box_top { 0 };
|
||||||
CSSPixels m_baseline { 0 };
|
CSSPixels m_baseline { 0 };
|
||||||
CSS::Direction m_direction { CSS::Direction::Ltr };
|
CSS::Direction m_direction { CSS::Direction::Ltr };
|
||||||
|
CSS::WritingMode m_writing_mode { CSS::WritingMode::HorizontalTb };
|
||||||
|
|
||||||
RefPtr<Gfx::GlyphRun> m_glyph_run;
|
RefPtr<Gfx::GlyphRun> m_glyph_run;
|
||||||
float m_insert_position { 0 };
|
float m_insert_position { 0 };
|
||||||
|
|
|
@ -10,11 +10,12 @@
|
||||||
|
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
LineBuilder::LineBuilder(InlineFormattingContext& context, LayoutState& layout_state, LayoutState::UsedValues& containing_block_used_values, CSS::Direction direction)
|
LineBuilder::LineBuilder(InlineFormattingContext& context, LayoutState& layout_state, LayoutState::UsedValues& containing_block_used_values, CSS::Direction direction, CSS::WritingMode writing_mode)
|
||||||
: m_context(context)
|
: m_context(context)
|
||||||
, m_layout_state(layout_state)
|
, m_layout_state(layout_state)
|
||||||
, m_containing_block_used_values(containing_block_used_values)
|
, m_containing_block_used_values(containing_block_used_values)
|
||||||
, m_direction(direction)
|
, m_direction(direction)
|
||||||
|
, m_writing_mode(writing_mode)
|
||||||
{
|
{
|
||||||
m_text_indent = m_context.containing_block().computed_values().text_indent().to_px(m_context.containing_block(), m_containing_block_used_values.content_width());
|
m_text_indent = m_context.containing_block().computed_values().text_indent().to_px(m_context.containing_block(), m_containing_block_used_values.content_width());
|
||||||
begin_new_line(false);
|
begin_new_line(false);
|
||||||
|
@ -38,7 +39,7 @@ void LineBuilder::break_line(ForcedBreak forced_break, Optional<CSSPixels> next_
|
||||||
size_t break_count = 0;
|
size_t break_count = 0;
|
||||||
bool floats_intrude_at_current_y = false;
|
bool floats_intrude_at_current_y = false;
|
||||||
do {
|
do {
|
||||||
m_containing_block_used_values.line_boxes.append(LineBox(m_direction));
|
m_containing_block_used_values.line_boxes.append(LineBox(m_direction, m_writing_mode));
|
||||||
begin_new_line(true, break_count == 0);
|
begin_new_line(true, break_count == 0);
|
||||||
break_count++;
|
break_count++;
|
||||||
floats_intrude_at_current_y = m_context.any_floats_intrude_at_block_offset(m_current_block_offset);
|
floats_intrude_at_current_y = m_context.any_floats_intrude_at_block_offset(m_current_block_offset);
|
||||||
|
@ -83,7 +84,7 @@ LineBox& LineBuilder::ensure_last_line_box()
|
||||||
{
|
{
|
||||||
auto& line_boxes = m_containing_block_used_values.line_boxes;
|
auto& line_boxes = m_containing_block_used_values.line_boxes;
|
||||||
if (line_boxes.is_empty())
|
if (line_boxes.is_empty())
|
||||||
line_boxes.append(LineBox(m_direction));
|
line_boxes.append(LineBox(m_direction, m_writing_mode));
|
||||||
return line_boxes.last();
|
return line_boxes.last();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,6 +180,10 @@ void LineBuilder::update_last_line()
|
||||||
// FIXME: Respect inline direction.
|
// FIXME: Respect inline direction.
|
||||||
CSSPixels excess_inline_space = m_available_width_for_current_line.to_px_or_zero() - line_box.inline_length();
|
CSSPixels excess_inline_space = m_available_width_for_current_line.to_px_or_zero() - line_box.inline_length();
|
||||||
|
|
||||||
|
if (m_writing_mode != CSS::WritingMode::HorizontalTb) {
|
||||||
|
block_offset = m_available_width_for_current_line.to_px_or_zero() - line_box.block_length();
|
||||||
|
}
|
||||||
|
|
||||||
// If (after justification, if any) the inline contents of a line box are too long to fit within it,
|
// If (after justification, if any) the inline contents of a line box are too long to fit within it,
|
||||||
// then the contents are start-aligned: any content that doesn't fit overflows the line box’s end edge.
|
// then the contents are start-aligned: any content that doesn't fit overflows the line box’s end edge.
|
||||||
if (excess_inline_space > 0) {
|
if (excess_inline_space > 0) {
|
||||||
|
|
|
@ -15,7 +15,7 @@ class LineBuilder {
|
||||||
AK_MAKE_NONMOVABLE(LineBuilder);
|
AK_MAKE_NONMOVABLE(LineBuilder);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LineBuilder(InlineFormattingContext&, LayoutState&, LayoutState::UsedValues& containing_block_used_values, CSS::Direction);
|
LineBuilder(InlineFormattingContext&, LayoutState&, LayoutState::UsedValues& containing_block_used_values, CSS::Direction, CSS::WritingMode);
|
||||||
~LineBuilder();
|
~LineBuilder();
|
||||||
|
|
||||||
enum class ForcedBreak {
|
enum class ForcedBreak {
|
||||||
|
@ -64,6 +64,7 @@ private:
|
||||||
CSSPixels m_max_height_on_current_line { 0 };
|
CSSPixels m_max_height_on_current_line { 0 };
|
||||||
CSSPixels m_text_indent { 0 };
|
CSSPixels m_text_indent { 0 };
|
||||||
CSS::Direction m_direction { CSS::Direction::Ltr };
|
CSS::Direction m_direction { CSS::Direction::Ltr };
|
||||||
|
CSS::WritingMode m_writing_mode { CSS::WritingMode::HorizontalTb };
|
||||||
|
|
||||||
bool m_last_line_needs_update { false };
|
bool m_last_line_needs_update { false };
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue