Forráskód Böngészése

Support for new display values: flow-root, inline-flex, inline-table

Michael Ragazzon 2 éve
szülő
commit
dff3104c53

+ 16 - 1
Include/RmlUi/Core/StyleTypes.h

@@ -56,7 +56,22 @@ namespace Style {
 	using Margin = LengthPercentageAuto;
 	using Padding = LengthPercentage;
 
-	enum class Display : uint8_t { None, Block, Inline, InlineBlock, Flex, Table, TableRow, TableRowGroup, TableColumn, TableColumnGroup, TableCell };
+	enum class Display : uint8_t {
+		None,
+		Block,
+		Inline,
+		InlineBlock,
+		FlowRoot,
+		Flex,
+		InlineFlex,
+		Table,
+		InlineTable,
+		TableRow,
+		TableRowGroup,
+		TableColumn,
+		TableColumnGroup,
+		TableCell
+	};
 	enum class Position : uint8_t { Static, Relative, Absolute, Fixed };
 
 	using Top = LengthPercentageAuto;

+ 5 - 2
Source/Core/Element.cpp

@@ -2378,6 +2378,7 @@ void Element::AddToStackingContext(Vector<StackingContextChild>& stacking_childr
 		switch (display)
 		{
 		case Display::Block:
+		case Display::FlowRoot:
 		case Display::Table:
 		case Display::Flex:
 			order = RenderOrder::Block;
@@ -2386,8 +2387,10 @@ void Element::AddToStackingContext(Vector<StackingContextChild>& stacking_childr
 
 		case Display::Inline:
 		case Display::InlineBlock:
+		case Display::InlineFlex:
+		case Display::InlineTable:
 			order = RenderOrder::Inline;
-			render_as_atomic_unit = (display == Display::InlineBlock || is_flex_item);
+			render_as_atomic_unit = (display != Display::Inline || is_flex_item);
 			break;
 
 		case Display::TableCell:
@@ -2399,7 +2402,7 @@ void Element::AddToStackingContext(Vector<StackingContextChild>& stacking_childr
 		case Display::TableRowGroup:
 		case Display::TableColumn:
 		case Display::TableColumnGroup:
-		case Display::None: RMLUI_ERROR; break; /* Handled above */
+		case Display::None: RMLUI_ERROR; break; // Handled above.
 		}
 	}
 

+ 9 - 13
Source/Core/Layout/BlockFormattingContext.cpp

@@ -91,12 +91,15 @@ static OuterDisplayType GetOuterDisplayType(Style::Display display)
 {
 	switch (display)
 	{
+	case Style::Display::Block:
+	case Style::Display::FlowRoot:
 	case Style::Display::Flex:
-	case Style::Display::Table:
-	case Style::Display::Block: return OuterDisplayType::BlockLevel;
+	case Style::Display::Table: return OuterDisplayType::BlockLevel;
 
+	case Style::Display::Inline:
 	case Style::Display::InlineBlock:
-	case Style::Display::Inline: return OuterDisplayType::InlineLevel;
+	case Style::Display::InlineFlex:
+	case Style::Display::InlineTable: return OuterDisplayType::InlineLevel;
 
 	case Style::Display::TableRow:
 	case Style::Display::TableRowGroup:
@@ -277,16 +280,9 @@ bool BlockFormattingContext::FormatBlockContainerChild(BlockContainer* parent_co
 	{
 	case Style::Display::Block: return FormatBlockBox(parent_container, element);
 	case Style::Display::Inline: return FormatInlineBox(parent_container, element);
-
-	case Style::Display::TableRow:
-	case Style::Display::TableRowGroup:
-	case Style::Display::TableColumn:
-	case Style::Display::TableColumnGroup:
-	case Style::Display::TableCell:
-	case Style::Display::InlineBlock:
-	case Style::Display::Flex:
-	case Style::Display::Table:
-	case Style::Display::None: /* handled above */ RMLUI_ERROR; break;
+	default:
+		RMLUI_ERROR; // Should have been handled above.
+		break;
 	}
 
 	return true;

+ 6 - 2
Source/Core/Layout/ContainerBox.cpp

@@ -255,13 +255,15 @@ FlexContainer::FlexContainer(Element* element, ContainerBox* parent_container) :
 	RMLUI_ASSERT(element);
 }
 
-bool FlexContainer::Close(const Vector2f content_overflow_size, const Box& box)
+bool FlexContainer::Close(const Vector2f content_overflow_size, const Box& box, float element_baseline)
 {
 	if (!SubmitBox(content_overflow_size, box, -1.f))
 		return false;
 
 	ClosePositionedElements();
+
 	SubmitElementLayout();
+	SetElementBaseline(element_baseline);
 	return true;
 }
 
@@ -275,7 +277,7 @@ TableWrapper::TableWrapper(Element* element, ContainerBox* parent_container) : C
 	RMLUI_ASSERT(element);
 }
 
-void TableWrapper::Close(const Vector2f content_overflow_size, const Box& box)
+void TableWrapper::Close(const Vector2f content_overflow_size, const Box& box, float element_baseline)
 {
 	bool result = SubmitBox(content_overflow_size, box, -1.f);
 
@@ -284,7 +286,9 @@ void TableWrapper::Close(const Vector2f content_overflow_size, const Box& box)
 	(void)result;
 
 	ClosePositionedElements();
+
 	SubmitElementLayout();
+	SetElementBaseline(element_baseline);
 }
 
 String TableWrapper::DebugDumpTree(int depth) const

+ 2 - 2
Source/Core/Layout/ContainerBox.h

@@ -129,7 +129,7 @@ public:
 
 	// Submits the formatted box to the flex container element, and propagates any uncaught overflow to this box.
 	// @returns True if it succeeds, otherwise false if it needs to be formatted again because scrollbars were enabled.
-	bool Close(const Vector2f content_overflow_size, const Box& box);
+	bool Close(const Vector2f content_overflow_size, const Box& box, float element_baseline);
 
 	const Box* GetIfBox() const override { return &box; }
 	String DebugDumpTree(int depth) const override;
@@ -150,7 +150,7 @@ public:
 	TableWrapper(Element* element, ContainerBox* parent_container);
 
 	// Submits the formatted box to the table element, and propagates any uncaught overflow to this box.
-	void Close(const Vector2f content_overflow_size, const Box& box);
+	void Close(const Vector2f content_overflow_size, const Box& box, float element_baseline);
 
 	const Box* GetIfBox() const override { return &box; }
 	String DebugDumpTree(int depth) const override;

+ 18 - 5
Source/Core/Layout/FlexFormattingContext.cpp

@@ -93,7 +93,8 @@ UniquePtr<LayoutBox> FlexFormattingContext::Format(ContainerBox* parent_containe
 
 		// Format the flexbox and all its children.
 		Vector2f flex_resulting_content_size, content_overflow_size;
-		context.Format(flex_resulting_content_size, content_overflow_size);
+		float flex_baseline = 0.f;
+		context.Format(flex_resulting_content_size, content_overflow_size, flex_baseline);
 
 		// Output the size of the formatted flexbox. The width is determined as a normal block box so we don't need to change that.
 		Vector2f formatted_content_size = box_content_size;
@@ -103,8 +104,12 @@ UniquePtr<LayoutBox> FlexFormattingContext::Format(ContainerBox* parent_containe
 		Box sized_box = box;
 		sized_box.SetContent(formatted_content_size);
 
+		// Change the flex baseline coordinates to the element baseline, which is defined as the distance from the element's bottom margin edge.
+		const float element_baseline =
+			sized_box.GetSizeAcross(Box::VERTICAL, Box::BORDER) + sized_box.GetEdge(Box::MARGIN, Box::BOTTOM) - flex_baseline;
+
 		// Close the box, and break out of the loop if it did not produce any new scrollbars, otherwise continue to format the flexbox again.
-		if (flex_container_box->Close(content_overflow_size, sized_box))
+		if (flex_container_box->Close(content_overflow_size, sized_box, element_baseline))
 			break;
 	}
 
@@ -200,7 +205,7 @@ static void GetItemSizing(FlexItem::Size& destination, const ComputedAxisSize& c
 	}
 }
 
-void FlexFormattingContext::Format(Vector2f& flex_resulting_content_size, Vector2f& flex_content_overflow_size) const
+void FlexFormattingContext::Format(Vector2f& flex_resulting_content_size, Vector2f& flex_content_overflow_size, float& flex_baseline) const
 {
 	// The following procedure is based on the CSS flexible box layout algorithm.
 	// For details, see https://drafts.csswg.org/css-flexbox/#layout-algorithm
@@ -838,6 +843,8 @@ void FlexFormattingContext::Format(Vector2f& flex_resulting_content_size, Vector
 		return main_axis_horizontal ? Vector2f(v_main, v_cross) : Vector2f(v_cross, v_main);
 	};
 
+	bool baseline_set = false;
+
 	// -- Format items --
 	for (FlexLine& line : container.lines)
 	{
@@ -854,11 +861,17 @@ void FlexFormattingContext::Format(Vector2f& flex_resulting_content_size, Vector
 			// Set the position of the element within the the flex container
 			item.element->SetOffset(flex_content_offset + item_offset, element_flex);
 
+			// The flex container baseline is simply set to the first flex item that has a baseline.
+			if (!baseline_set && item_layout_box->GetBaselineOfLastLine(flex_baseline))
+			{
+				flex_baseline += flex_content_offset.y + item_offset.y;
+				baseline_set = true;
+			}
+
 			// The cell contents may overflow, propagate this to the flex container.
 			const Vector2f overflow_size = item_offset + item_layout_box->GetVisibleOverflowSize();
 
-			flex_content_overflow_size.x = Math::Max(flex_content_overflow_size.x, overflow_size.x);
-			flex_content_overflow_size.y = Math::Max(flex_content_overflow_size.y, overflow_size.y);
+			flex_content_overflow_size = Math::Max(flex_content_overflow_size, overflow_size);
 		}
 	}
 

+ 2 - 1
Source/Core/Layout/FlexFormattingContext.h

@@ -51,7 +51,8 @@ private:
 	/// Format the flexbox and its children.
 	/// @param[out] flex_resulting_content_size The final content size of the flex container.
 	/// @param[out] flex_content_overflow_size Overflow size in case flex items or their contents overflow the container.
-	void Format(Vector2f& flex_resulting_content_size, Vector2f& flex_content_overflow_size) const;
+	/// @param[out] flex_baseline The baseline of the flex contaienr, in terms of the vertical distance from its top-left border corner.
+	void Format(Vector2f& flex_resulting_content_size, Vector2f& flex_content_overflow_size, float& flex_baseline) const;
 
 	Vector2f flex_available_content_size;
 	Vector2f flex_content_containing_block;

+ 4 - 4
Source/Core/Layout/FormattingContext.cpp

@@ -49,16 +49,16 @@ UniquePtr<LayoutBox> FormattingContext::FormatIndependent(ContainerBox* parent_c
 
 	auto& computed = element->GetComputedValues();
 	const Display display = computed.display();
-	if (display == Display::Flex)
+	if (display == Display::Flex || display == Display::InlineFlex)
 	{
 		type = FormattingContextType::Flex;
 	}
-	else if (display == Display::Table)
+	else if (display == Display::Table || display == Display::InlineTable)
 	{
 		type = FormattingContextType::Table;
 	}
-	else if (computed.float_() != Float::None || computed.position() == Position::Absolute || computed.position() == Position::Fixed ||
-		computed.display() == Display::InlineBlock || computed.display() == Display::TableCell || computed.overflow_x() != Overflow::Visible ||
+	else if (display == Display::InlineBlock || display == Display::FlowRoot || display == Display::TableCell || computed.float_() != Float::None ||
+		computed.position() == Position::Absolute || computed.position() == Position::Fixed || computed.overflow_x() != Overflow::Visible ||
 		computed.overflow_y() != Overflow::Visible || !element->GetParentNode() || element->GetParentNode()->GetDisplay() == Display::Flex)
 	{
 		type = FormattingContextType::Block;

+ 2 - 1
Source/Core/Layout/LayoutDetails.cpp

@@ -258,7 +258,8 @@ float LayoutDetails::GetShrinkToFitWidth(Element* element, Vector2f containing_b
 
 	// Currently we don't support shrink-to-fit width for flexboxes or tables. Just return a zero-sized width.
 	const Style::Display display = element->GetDisplay();
-	if (display == Style::Display::Flex || display == Style::Display::Table)
+	if (display == Style::Display::Flex || display == Style::Display::InlineFlex || display == Style::Display::Table ||
+		display == Style::Display::InlineTable)
 		return 0.f;
 
 	// Use a large size for the box content width, so that it is practically unconstrained. This makes the formatting

+ 19 - 5
Source/Core/Layout/TableFormattingContext.cpp

@@ -87,9 +87,10 @@ UniquePtr<LayoutBox> TableFormattingContext::Format(ContainerBox* parent_contain
 	context.grid.Build(element_table, *table_wrapper_box);
 
 	Vector2f table_content_size, table_overflow_size;
+	float table_baseline = 0.f;
 
 	// Format the table and its children.
-	context.FormatTable(table_content_size, table_overflow_size);
+	context.FormatTable(table_content_size, table_overflow_size, table_baseline);
 
 	RMLUI_ASSERT(table_content_size.y >= 0);
 
@@ -103,12 +104,15 @@ UniquePtr<LayoutBox> TableFormattingContext::Format(ContainerBox* parent_contain
 			BuildBoxMode::Block, true);
 	}
 
-	table_wrapper_box->Close(table_overflow_size, box);
+	// Change the table baseline coordinates to the element baseline, which is defined as the distance from the element's bottom margin edge.
+	const float element_baseline = box.GetSizeAcross(Box::VERTICAL, Box::BORDER) + box.GetEdge(Box::MARGIN, Box::BOTTOM) - table_baseline;
+
+	table_wrapper_box->Close(table_overflow_size, box, element_baseline);
 
 	return table_wrapper_box;
 }
 
-void TableFormattingContext::FormatTable(Vector2f& table_content_size, Vector2f& table_overflow_size) const
+void TableFormattingContext::FormatTable(Vector2f& table_content_size, Vector2f& table_overflow_size, float& table_baseline) const
 {
 	// Defines the boxes for all columns in this table, one entry per table column (spanning columns will add multiple entries).
 	TrackBoxList columns;
@@ -127,7 +131,7 @@ void TableFormattingContext::FormatTable(Vector2f& table_content_size, Vector2f&
 
 	FormatColumns(columns, table_content_size.y);
 
-	FormatCells(cells, table_overflow_size, rows, columns);
+	FormatCells(cells, table_overflow_size, rows, columns, table_baseline);
 }
 
 void TableFormattingContext::DetermineColumnWidths(TrackBoxList& columns, float& table_content_width) const
@@ -395,10 +399,13 @@ void TableFormattingContext::FormatColumns(const TrackBoxList& columns, float ta
 	}
 }
 
-void TableFormattingContext::FormatCells(BoxList& cells, Vector2f& table_overflow_size, const TrackBoxList& rows, const TrackBoxList& columns) const
+void TableFormattingContext::FormatCells(BoxList& cells, Vector2f& table_overflow_size, const TrackBoxList& rows, const TrackBoxList& columns,
+	float& table_baseline) const
 {
 	RMLUI_ASSERT(cells.size() == grid.cells.size());
 
+	bool baseline_set = false;
+
 	for (int cell_index = 0; cell_index < (int)cells.size(); cell_index++)
 	{
 		const TableGrid::Cell& grid_cell = grid.cells[cell_index];
@@ -468,6 +475,13 @@ void TableFormattingContext::FormatCells(BoxList& cells, Vector2f& table_overflo
 		// Set the position of the element within the the table container
 		element_cell->SetOffset(cell_offset, element_table);
 
+		// The table baseline is simply set to the first cell that has a baseline.
+		if (!baseline_set && cell_box->GetBaselineOfLastLine(table_baseline))
+		{
+			table_baseline += cell_offset.y;
+			baseline_set = true;
+		}
+
 		// The cell contents may overflow, propagate this to the table.
 		table_overflow_size.x = Math::Max(table_overflow_size.x, cell_offset.x - table_content_offset.x + cell_visible_overflow_size.x);
 		table_overflow_size.y = Math::Max(table_overflow_size.y, cell_offset.y - table_content_offset.y + cell_visible_overflow_size.y);

+ 5 - 3
Source/Core/Layout/TableFormattingContext.h

@@ -56,8 +56,9 @@ private:
 	/// Format the table and its children.
 	/// @param[out] table_content_size The final size of the table which will be determined by the size of its columns, rows, and spacing.
 	/// @param[out] table_overflow_size Overflow size in case the contents of any cells overflow their cell box (without being caught by the cell).
+	/// @param[out] table_baseline The baseline of the table wrapper, in terms of the vertical distance from its top-left border corner.
 	/// @note Expects the table grid to have been built, and all table parameters to be set already.
-	void FormatTable(Vector2f& table_content_size, Vector2f& table_overflow_size) const;
+	void FormatTable(Vector2f& table_content_size, Vector2f& table_overflow_size, float& table_baseline) const;
 
 	// Determines the column widths, and populates the columns.
 	void DetermineColumnWidths(TrackBoxList& columns, float& table_content_width) const;
@@ -75,14 +76,15 @@ private:
 	void FormatColumns(const TrackBoxList& columns, float table_content_height) const;
 
 	// Format the table cell elements.
-	void FormatCells(BoxList& cells, Vector2f& table_overflow_size, const TrackBoxList& rows, const TrackBoxList& columns) const;
+	void FormatCells(BoxList& cells, Vector2f& table_overflow_size, const TrackBoxList& rows, const TrackBoxList& columns,
+		float& table_baseline) const;
 
 	Element* element_table = nullptr;
 	TableWrapper* table_wrapper_box = nullptr;
 
 	TableGrid grid;
 
-	bool table_auto_height;
+	bool table_auto_height = false;
 	Vector2f table_min_size, table_max_size;
 	Vector2f table_gap;
 	Vector2f table_content_offset;

+ 2 - 1
Source/Core/StyleSheetSpecification.cpp

@@ -319,7 +319,8 @@ void StyleSheetSpecification::RegisterDefaultProperties()
 	RegisterProperty(PropertyId::BorderBottomLeftRadius, "border-bottom-left-radius", "0px", false, false).AddParser("length");
 	RegisterShorthand(ShorthandId::BorderRadius, "border-radius", "border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius", ShorthandType::Box);
 
-	RegisterProperty(PropertyId::Display, "display", "inline", false, true).AddParser("keyword", "none, block, inline, inline-block, flex, table, table-row, table-row-group, table-column, table-column-group, table-cell");
+	RegisterProperty(PropertyId::Display, "display", "inline", false, true)
+		.AddParser("keyword", "none, block, inline, inline-block, flow-root, flex, inline-flex, table, inline-table, table-row, table-row-group, table-column, table-column-group, table-cell");
 	RegisterProperty(PropertyId::Position, "position", "static", false, true).AddParser("keyword", "static, relative, absolute, fixed");
 	RegisterProperty(PropertyId::Top, "top", "auto", false, false)
 		.AddParser("keyword", "auto")

+ 71 - 0
Tests/Data/VisualTests/flex_inline.rml

@@ -0,0 +1,71 @@
+<rml>
+<head>
+	<title>Inline flexboxes</title>
+	<link type="text/rcss" href="../style.rcss"/>
+	<link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#flex-baselines" />
+	<meta name="Description" content="Inline flexboxes. The baseline of a flex container is simply set to its first flex item that has a baseline." />
+	<style>
+		.flex {
+			display: inline-flex;
+			background-color: #555;
+			margin: 1em 0.5em;
+			border: 2dp #333;
+			justify-content: space-between;
+			color: #d44fff;
+			width: 170dp;
+		}
+		.flex div {
+			flex: 0 1 auto;
+			width: 35dp;
+			height: 30dp;
+			margin: 5dp 8dp;
+			background-color: #eee;
+			line-height: 30dp;
+			text-align: center;
+		}
+		.wrap {
+			flex-wrap: wrap;
+			width: 115dp;
+		}
+		.column {
+			flex-direction: column;
+			width: 70dp;
+			align-items: center;
+		}
+	</style>
+</head>
+
+<body>
+<p>
+Can we
+<div class="flex">
+	<div>A</div>
+	<div>B</div>
+	<div>C</div>
+</div>
+play
+<div class="flex wrap">
+	<div>A</div>
+	<div>B</div>
+	<div>C</div>
+</div>
+Tetris
+<div class="flex column">
+	<div>A</div>
+	<div>B</div>
+	<div>C</div>
+</div>
+
+with flexboxes
+<div class="flex">
+	<div>A</div>
+	<div>B</div>
+	<div>C</div>
+</div>
+?
+
+</p>
+
+<handle size_target="#document"/>
+</body>
+</rml>

+ 1 - 2
Tests/Data/VisualTests/float_excluded_by_flow_root.rml

@@ -27,8 +27,7 @@
 			border: 5px #663399;
 		}
 		.box.flow-root {
-			/*display: flow-root;*/  /* Not yet implemented */
-			overflow: hidden;        /* Use this instead to establish a BFC */
+			display: flow-root;
 			background-color: #f0f8ff;
 			border-color: #4682b4;
 		}

+ 11 - 6
Tests/Data/VisualTests/float_overflow.rml

@@ -1,9 +1,9 @@
 <rml>
 <head>
-    <title>Floats: overflow</title>
-    <link type="text/rcss" href="../style.rcss"/>
-	<link rel="help" href="http://www.w3.org/TR/CSS21/visuren.html#floats" />
-	<meta name="Description" content="Floating boxes" />
+	<title>Floats: overflow</title>
+	<link type="text/rcss" href="../style.rcss"/>
+	<link rel="help" href="https://w3c.github.io/csswg-drafts/css2/#root-height" />
+	<meta name="Description" content="Height of block formatting context roots with floated boxes inside." />
 	<style>
 		body {
 			background: #ddd;
@@ -25,7 +25,6 @@
 			height: 130dp;
 			margin: 5dp;
 		}
-		
 	</style>
 </head>
 
@@ -35,9 +34,15 @@
 	<p>The float to the left should extend past the background of the containing '.box' element.</p>
 </div>
 <hr/>
-<div class="box" style="overflow:auto">
+<div class="box" style="overflow: auto">
 	<div class="float">float: left</div>
 	<p>Using 'overflow: auto' on the containing '.box' element. This should establish a new block formatting context (thereby resolving all floats) so that the background wraps around the entire float.</p>
 </div>
+<hr/>
+<div class="box" style="display: flow-root">
+	<div class="float">float: left</div>
+	<p>Using 'display: flow-root' on the containing '.box' element. This also establishes a new block formatting context, and should be equivalent to the previous box.</p>
+</div>
+<handle size_target="#document"/>
 </body>
 </rml>

+ 74 - 0
Tests/Data/VisualTests/table_inline.rml

@@ -0,0 +1,74 @@
+<rml>
+<head>
+	<title>Inline tables</title>
+	<link type="text/rcss" href="../style.rcss"/>
+	<link rel="help" href="https://drafts.csswg.org/css2/#value-def-inline-table" />
+	<meta name="Description" content="Inline tables. The baseline of a table wrapper is simply set its first table cell that has a baseline." />
+	<style>
+		table {
+			line-height: 1.2;
+			display: inline-table;
+			margin: 1em 0.5em;
+			border: 2dp #666;
+			gap: 5dp;
+			text-align: center;
+		}
+		td {
+			background: #bbb;
+			vertical-align: middle;
+			padding: 5dp;
+		}
+		td { width: 25dp; }
+		td:first-child, td:last-child { width: 40dp; }
+	</style>
+</head>
+
+<body>
+<p>
+Inline tables
+<table>
+	<tr>
+		<td>A</td>
+		<td>B</td>
+		<td>C</td>
+	</tr>
+	<tr>
+		<td>A</td>
+		<td>B</td>
+		<td>C</td>
+	</tr>
+</table>
+can be located
+<table>
+	<tr>
+		<td>A</td>
+		<td>B</td>
+		<td>C</td>
+	</tr>
+</table>
+in-between
+<table>
+	<tr>
+		<td>A</td>
+		<td>B</td>
+	</tr>
+	<tr>
+		<td>A</td>
+		<td>B</td>
+	</tr>
+	<tr>
+		<td>A</td>
+		<td>B</td>
+	</tr>
+</table>
+text
+<table>
+	<tr>
+		<td>A<br/>B</td>
+	</tr>
+</table>
+.
+</p>
+<handle size_target="#document"/>
+</body>
+</rml>