Browse Source

LayoutEngine: Be more explicit about content sizing and overflow. Catches more overflow situations.

Michael Ragazzon 5 years ago
parent
commit
5810cf7216
2 changed files with 51 additions and 23 deletions
  1. 40 21
      Source/Core/LayoutBlockBox.cpp
  2. 11 2
      Source/Core/LayoutBlockBox.h

+ 40 - 21
Source/Core/LayoutBlockBox.cpp

@@ -41,7 +41,7 @@ namespace Rml {
 
 // Creates a new block box for rendering a block element.
 LayoutBlockBox::LayoutBlockBox(LayoutBlockBox* _parent, Element* _element, const Box& _box, float _min_height, float _max_height) 
-	: position(0), visible_outer_width(0), box(_box), min_height(_min_height), max_height(_max_height)
+	: position(0), box(_box), min_height(_min_height), max_height(_max_height)
 {
 	RMLUI_ZoneScoped;
 
@@ -194,7 +194,7 @@ LayoutBlockBox::CloseResult LayoutBlockBox::Close()
 		box.SetContent(content_area);
 	}
 	
-	visible_outer_width = 0;
+	visible_overflow_size = Vector2f(0);
 	RMLUI_ASSERTMSG(!(context == INLINE && element), "The following assumes inline contexts do not represent a particular element.");
 
 	// Set the computed box on the element.
@@ -202,16 +202,12 @@ LayoutBlockBox::CloseResult LayoutBlockBox::Close()
 	{
 		// Calculate the dimensions of the box's *internal* content; this is the tightest-fitting box around all of the
 		// internal elements, plus this element's padding.
-		Vector2f content_box(0, 0);
 
-		for (size_t i = 0; i < block_boxes.size(); i++)
-		{
-			// TODO: Only if the containing block is not an ancestor of us (ie. we are the containing block?).
-			content_box.x = Math::Max(content_box.x, block_boxes[i]->visible_outer_width);
-		}
+		// Start with the inner content size, as set by the child blocks boxes or external formatting contexts.
+		Vector2f content_box = inner_content_size;
 
 		// Check how big our floated area is.
-		Vector2f space_box = space->GetDimensions();
+		const Vector2f space_box = space->GetDimensions();
 		content_box.x = Math::Max(content_box.x, space_box.x);
 
 		// If our content is larger than our window, we can enable the horizontal scrollbar if
@@ -228,26 +224,32 @@ LayoutBlockBox::CloseResult LayoutBlockBox::Close()
 			}
 		}
 
-		content_box.x += (box.GetEdge(Box::PADDING, Box::LEFT) + box.GetEdge(Box::PADDING, Box::RIGHT));
-
-		content_box.y = box_cursor;
+		content_box.y = Math::Max(content_box.y, box_cursor);
 		content_box.y = Math::Max(content_box.y, space_box.y);
 		if (!CatchVerticalOverflow(content_box.y))
 			return LAYOUT_SELF;
 
-		content_box.y += (box.GetEdge(Box::PADDING, Box::TOP) + box.GetEdge(Box::PADDING, Box::BOTTOM));
+		const Vector2f padding_edges = Vector2f(
+			box.GetEdge(Box::PADDING, Box::LEFT) + box.GetEdge(Box::PADDING, Box::RIGHT),
+			box.GetEdge(Box::PADDING, Box::TOP) + box.GetEdge(Box::PADDING, Box::BOTTOM)
+		);
 
 		element->SetBox(box);
-		element->SetContentBox(space->GetOffset(), content_box);
+		element->SetContentBox(space->GetOffset(), content_box + padding_edges);
 
-		const float margin_width = box.GetSize(Box::MARGIN).x;
+		const Vector2f margin_size = box.GetSize(Box::MARGIN);
 
-		// Set the visible outer width so that ancestors can catch any overflow produced by us. That is, hiding it or providing a scrolling mechanism.
+		// Set the visible overflow size so that ancestors can catch any overflow produced by us. That is, hiding it or providing a scrolling mechanism.
 		// If we catch our own overflow here, then just use the normal margin box as that will effectively remove the overflow from our ancestor's perspective.
 		if (overflow_x_property != Style::Overflow::Visible)
-			visible_outer_width = margin_width;
+			visible_overflow_size.x = margin_size.x;
 		else
-			visible_outer_width = Math::Max(margin_width, space->GetOffset().x + content_box.x + box.GetEdge(Box::MARGIN, Box::LEFT) + box.GetEdge(Box::MARGIN, Box::RIGHT));
+			visible_overflow_size.x = Math::Max(margin_size.x, content_box.x + box.GetEdge(Box::MARGIN, Box::LEFT) + box.GetEdge(Box::BORDER, Box::LEFT) + box.GetEdge(Box::PADDING, Box::LEFT));
+
+		if (overflow_y_property != Style::Overflow::Visible)
+			visible_overflow_size.y = margin_size.y;
+		else
+			visible_overflow_size.y = Math::Max(margin_size.y, content_box.y + box.GetEdge(Box::MARGIN, Box::TOP) + box.GetEdge(Box::BORDER, Box::TOP) + box.GetEdge(Box::PADDING, Box::TOP));
 
 		// Format any scrollbars which were enabled on this element.
 		element->GetElementScroll()->FormatScrollbars();
@@ -258,7 +260,7 @@ LayoutBlockBox::CloseResult LayoutBlockBox::Close()
 		for (size_t i = 0; i < line_boxes.size(); i++)
 		{
 			LayoutLineBox* line_box = line_boxes[i].get();
-			visible_outer_width = Math::Max(visible_outer_width, line_box->GetBoxCursor());
+			visible_overflow_size.x = Math::Max(visible_overflow_size.x, line_box->GetBoxCursor());
 		}
 	}
 
@@ -321,7 +323,14 @@ LayoutBlockBox::CloseResult LayoutBlockBox::Close()
 bool LayoutBlockBox::CloseBlockBox(LayoutBlockBox* child)
 {
 	RMLUI_ASSERT(context == BLOCK);
-	box_cursor = (child->GetPosition().y - child->box.GetEdge(Box::MARGIN, Box::TOP) - (box.GetPosition().y + position.y)) + child->GetBox().GetSize(Box::MARGIN).y;
+	
+	const float child_position_y = child->GetPosition().y - child->box.GetEdge(Box::MARGIN, Box::TOP) - (box.GetPosition().y + position.y);
+	
+	box_cursor = child_position_y + child->GetBox().GetSize(Box::MARGIN).y;
+	
+	// Extend the inner content size. The vertical size can be larger than the box_cursor due to overflow.
+	inner_content_size.x = Math::Max(inner_content_size.x, child->visible_overflow_size.x);
+	inner_content_size.y = Math::Max(inner_content_size.y, child_position_y + child->visible_overflow_size.y);
 
 	return CatchVerticalOverflow();
 }
@@ -634,6 +643,16 @@ float LayoutBlockBox::GetShrinkToFitWidth() const
 	return content_width;
 }
 
+Vector2f LayoutBlockBox::GetVisibleOverflowSize() const
+{
+	return visible_overflow_size;
+}
+
+void LayoutBlockBox::ExtendInnerContentSize(Vector2f _inner_content_size)
+{
+	inner_content_size.x = Math::Max(inner_content_size.x, _inner_content_size.x);
+	inner_content_size.y = Math::Max(inner_content_size.y, _inner_content_size.y);
+}
 
 // Returns the block box's element.
 Element* LayoutBlockBox::GetElement() const
@@ -711,7 +730,7 @@ void LayoutBlockBox::PositionFloat(Element* element, float offset)
 bool LayoutBlockBox::CatchVerticalOverflow(float cursor)
 {
 	if (cursor == -1)
-		cursor = box_cursor;
+		cursor = Math::Max(box_cursor, inner_content_size.y);
 
 	float box_height = box.GetSize().y;
 	if (box_height < 0)

+ 11 - 2
Source/Core/LayoutBlockBox.h

@@ -134,6 +134,10 @@ public:
 
 	/// Calculate the dimensions of the box's internal content width; i.e. the size used to calculate the shrink-to-fit width.
 	float GetShrinkToFitWidth() const;
+	/// Get the visible overflow size. Note: This is only well-defined after the layout box has been closed.
+	Vector2f GetVisibleOverflowSize() const;
+	/// Set the inner content size if it is larger than the current value on each axis individually.
+	void ExtendInnerContentSize(Vector2f inner_content_size);
 
 	/// Returns the block box's element.
 	/// @return The block box's element.
@@ -227,9 +231,14 @@ private:
 	// Used by block contexts only; stores the value of the overflow property for the element.
 	Style::Overflow overflow_x_property;
 	Style::Overflow overflow_y_property;
-	//  Used by block contexts only; the content width as visible from the parent. Similar to scroll width, but shrinked if overflow is caught here. 
+
+	// The outer size of this box including overflowing content. Similar to scroll width, but shrinked if overflow is caught here. 
 	//   This can be wider than the box if we are overflowing. Only available after the box has been closed. 
-	float visible_outer_width;
+	Vector2f visible_overflow_size;
+	// The inner content size (excluding any padding/border/margins).
+	// This is extended as child block boxes are closed, or from external formatting contexts.
+	Vector2f inner_content_size;
+
 	// Used by block contexts only; if true, we've enabled our vertical scrollbar.
 	bool vertical_overflow;