Bladeren bron

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.
BenJilks 8 maanden geleden
bovenliggende
commit
80e7e6dd7d

+ 11 - 0
Tests/LibWeb/Layout/expected/writing-modes-vertical-rl.txt

@@ -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 - 0
Tests/LibWeb/Layout/input/writing-modes-vertical-rl.html

@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<style>
+body {
+    writing-mode: vertical-rl;
+}
+</style>Well, hello friends! :-)

+ 2 - 1
Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp

@@ -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

+ 22 - 1
Userland/Libraries/LibWeb/Layout/LineBox.cpp

@@ -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);

+ 6 - 4
Userland/Libraries/LibWeb/Layout/LineBox.h

@@ -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 height() const { return m_block_length; }
-    CSSPixels bottom() const { return m_bottom; }
+    CSSPixels width() const;
+    CSSPixels height() const;
+    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() };

+ 6 - 1
Userland/Libraries/LibWeb/Layout/LineBoxFragment.cpp

@@ -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 };
 }
 }
 
 

+ 2 - 1
Userland/Libraries/LibWeb/Layout/LineBoxFragment.h

@@ -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 };

+ 8 - 3
Userland/Libraries/LibWeb/Layout/LineBuilder.cpp

@@ -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) {

+ 2 - 1
Userland/Libraries/LibWeb/Layout/LineBuilder.h

@@ -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 };
 };
 };