mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 16:52:43 +00:00 
			
		
		
		
	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:
		
							parent
							
								
									311e1039b5
								
							
						
					
					
						commit
						d59ec3ab85
					
				
					 7 changed files with 56 additions and 4 deletions
				
			
		|  | @ -112,6 +112,12 @@ void InlineFormattingContext::run(LayoutMode layout_mode) | ||||||
|         for (size_t i = 0; i < line_box.fragments().size(); ++i) { |         for (size_t i = 0; i < line_box.fragments().size(); ++i) { | ||||||
|             auto& fragment = line_box.fragments()[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.
 |             // Vertically align everyone's bottom to the line.
 | ||||||
|             // FIXME: Support other kinds of vertical alignment.
 |             // 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) }); |             fragment.set_offset({ roundf(x_offset + fragment.offset().x()), content_height + (max_height - fragment.height()) - (line_spacing / 2) }); | ||||||
|  |  | ||||||
|  | @ -24,7 +24,9 @@ | ||||||
|  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | #include <LibGfx/Painter.h> | ||||||
| #include <LibWeb/DOM/Element.h> | #include <LibWeb/DOM/Element.h> | ||||||
|  | #include <LibWeb/Layout/BlockBox.h> | ||||||
| #include <LibWeb/Layout/InlineNode.h> | #include <LibWeb/Layout/InlineNode.h> | ||||||
| 
 | 
 | ||||||
| namespace Web::Layout { | 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())); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -36,6 +36,10 @@ public: | ||||||
|     virtual ~InlineNode() override; |     virtual ~InlineNode() override; | ||||||
|     virtual const char* class_name() const override { return "InlineNode"; } |     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: | private: | ||||||
|     virtual bool is_inline_node() const final { return true; } |     virtual bool is_inline_node() const final { return true; } | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -33,7 +33,7 @@ | ||||||
| 
 | 
 | ||||||
| namespace Web::Layout { | 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; |     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) { |     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().m_length = (start - m_fragments.last().m_start) + length; | ||||||
|         m_fragments.last().set_width(m_fragments.last().width() + width); |         m_fragments.last().set_width(m_fragments.last().width() + width); | ||||||
|     } else { |     } 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; |     m_width += width; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -38,7 +38,7 @@ public: | ||||||
| 
 | 
 | ||||||
|     float width() const { return m_width; } |     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; } |     const NonnullOwnPtrVector<LineBoxFragment>& fragments() const { return m_fragments; } | ||||||
|     NonnullOwnPtrVector<LineBoxFragment>& fragments() { return m_fragments; } |     NonnullOwnPtrVector<LineBoxFragment>& fragments() { return m_fragments; } | ||||||
|  |  | ||||||
|  | @ -29,6 +29,7 @@ | ||||||
| #include <LibWeb/Layout/InitialContainingBlockBox.h> | #include <LibWeb/Layout/InitialContainingBlockBox.h> | ||||||
| #include <LibWeb/Layout/LineBoxFragment.h> | #include <LibWeb/Layout/LineBoxFragment.h> | ||||||
| #include <LibWeb/Layout/TextNode.h> | #include <LibWeb/Layout/TextNode.h> | ||||||
|  | #include <LibWeb/Layout/InlineNode.h> | ||||||
| #include <LibWeb/Painting/PaintContext.h> | #include <LibWeb/Painting/PaintContext.h> | ||||||
| #include <ctype.h> | #include <ctype.h> | ||||||
| 
 | 
 | ||||||
|  | @ -41,6 +42,9 @@ void LineBoxFragment::paint(PaintContext& context, PaintPhase phase) | ||||||
|             return; |             return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (is<InlineNode>(layout_node())) | ||||||
|  |         downcast<InlineNode>(layout_node()).paint_fragment(context, *this, phase); | ||||||
|  | 
 | ||||||
|     if (is<TextNode>(layout_node())) |     if (is<TextNode>(layout_node())) | ||||||
|         downcast<TextNode>(layout_node()).paint_fragment(context, *this, phase); |         downcast<TextNode>(layout_node()).paint_fragment(context, *this, phase); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -37,12 +37,19 @@ class LineBoxFragment : public Weakable<LineBoxFragment> { | ||||||
|     friend class LineBox; |     friend class LineBox; | ||||||
| 
 | 
 | ||||||
| public: | 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_layout_node(layout_node) | ||||||
|         , m_start(start) |         , m_start(start) | ||||||
|         , m_length(length) |         , m_length(length) | ||||||
|         , m_offset(offset) |         , m_offset(offset) | ||||||
|         , m_size(size) |         , m_size(size) | ||||||
|  |         , m_type(type) | ||||||
|     { |     { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -50,12 +57,14 @@ public: | ||||||
|     int start() const { return m_start; } |     int start() const { return m_start; } | ||||||
|     int length() const { return m_length; } |     int length() const { return m_length; } | ||||||
|     const Gfx::FloatRect absolute_rect() const; |     const Gfx::FloatRect absolute_rect() const; | ||||||
|  |     Type type() const { return m_type; } | ||||||
| 
 | 
 | ||||||
|     const Gfx::FloatPoint& offset() const { return m_offset; } |     const Gfx::FloatPoint& offset() const { return m_offset; } | ||||||
|     void set_offset(const Gfx::FloatPoint& offset) { m_offset = offset; } |     void set_offset(const Gfx::FloatPoint& offset) { m_offset = offset; } | ||||||
| 
 | 
 | ||||||
|     const Gfx::FloatSize& size() const { return m_size; } |     const Gfx::FloatSize& size() const { return m_size; } | ||||||
|     void set_width(float width) { m_size.set_width(width); } |     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 width() const { return m_size.width(); } | ||||||
|     float height() const { return m_size.height(); } |     float height() const { return m_size.height(); } | ||||||
| 
 | 
 | ||||||
|  | @ -77,6 +86,7 @@ private: | ||||||
|     int m_length { 0 }; |     int m_length { 0 }; | ||||||
|     Gfx::FloatPoint m_offset; |     Gfx::FloatPoint m_offset; | ||||||
|     Gfx::FloatSize m_size; |     Gfx::FloatSize m_size; | ||||||
|  |     Type m_type { Type::Normal }; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling