Bladeren bron

LibWeb: Implement `vertical-align: middle` correctly for atomic inlines

This makes inline icons pop into the right place on https://ahrefs.com/
Andreas Kling 10 maanden geleden
bovenliggende
commit
c22acc2551

+ 16 - 0
Tests/LibWeb/Layout/expected/block-and-inline/inline-block-vertical-align-middle.txt

@@ -0,0 +1,16 @@
+Viewport <#document> at (0,0) content-size 800x600 children: not-inline
+  BlockContainer <html> at (0,0) content-size 800x116 [BFC] children: not-inline
+    BlockContainer <body> at (8,8) content-size 784x100 children: inline
+      InlineNode <span>
+        frag 0 from TextNode start: 0, length: 4, rect: [8,49 35.15625x17] baseline: 13.296875
+            "foo "
+        frag 1 from BlockContainer start: 0, length: 0, rect: [43,8 100x100] baseline: 54.296875
+        TextNode <#text>
+        BlockContainer <span.thing> at (43,8) content-size 100x100 inline-block [BFC] children: not-inline
+
+ViewportPaintable (Viewport<#document>) [0,0 800x600]
+  PaintableWithLines (BlockContainer<HTML>) [0,0 800x116]
+    PaintableWithLines (BlockContainer<BODY>) [8,8 784x100]
+      InlinePaintable (InlineNode<SPAN>)
+        TextPaintable (TextNode<#text>)
+        PaintableWithLines (BlockContainer<SPAN>.thing) [43,8 100x100]

+ 9 - 0
Tests/LibWeb/Layout/input/block-and-inline/inline-block-vertical-align-middle.html

@@ -0,0 +1,9 @@
+<!doctype html><style>
+    * { outline: 1px solid black; }
+    .thing {
+        display: inline-block;
+        vertical-align: middle;
+        width: 100px;
+        height: 100px;
+    }
+</style><body><span>foo <span class="thing"></span>

+ 13 - 6
Userland/Libraries/LibWeb/Layout/LineBuilder.cpp

@@ -261,25 +261,32 @@ void LineBuilder::update_last_line()
         CSSPixels new_fragment_y = 0;
 
         auto y_value_for_alignment = [&](CSS::VerticalAlign vertical_align) {
-            CSSPixels effective_box_top = fragment.border_box_top();
+            CSSPixels effective_box_top_offset = fragment.border_box_top();
+            CSSPixels effective_box_bottom_offset = fragment.border_box_top();
             if (fragment.is_atomic_inline()) {
                 auto const& fragment_box_state = m_layout_state.get(static_cast<Box const&>(fragment.layout_node()));
-                effective_box_top = fragment_box_state.margin_box_top();
+                effective_box_top_offset = fragment_box_state.margin_box_top();
+                effective_box_bottom_offset = fragment_box_state.margin_box_bottom();
             }
 
             switch (vertical_align) {
             case CSS::VerticalAlign::Baseline:
-                return m_current_y + line_box_baseline - fragment.baseline() + effective_box_top;
+                return m_current_y + line_box_baseline - fragment.baseline() + effective_box_top_offset;
             case CSS::VerticalAlign::Top:
-                return m_current_y + effective_box_top;
-            case CSS::VerticalAlign::Middle:
+                return m_current_y + effective_box_top_offset;
+            case CSS::VerticalAlign::Middle: {
+                // Align the vertical midpoint of the box with the baseline of the parent box
+                // plus half the x-height of the parent.
+                auto const x_height = CSSPixels::nearest_value_for(m_context.containing_block().first_available_font().pixel_metrics().x_height);
+                return m_current_y + line_box_baseline + ((effective_box_top_offset - effective_box_bottom_offset - x_height - fragment.height()) / 2);
+            }
             case CSS::VerticalAlign::Bottom:
             case CSS::VerticalAlign::Sub:
             case CSS::VerticalAlign::Super:
             case CSS::VerticalAlign::TextBottom:
             case CSS::VerticalAlign::TextTop:
                 // FIXME: These are all 'baseline'
-                return m_current_y + line_box_baseline - fragment.baseline() + effective_box_top;
+                return m_current_y + line_box_baseline - fragment.baseline() + effective_box_top_offset;
             }
             VERIFY_NOT_REACHED();
         };