mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-05-28 17:16:29 +03:00
block and inline-block layouter fixes, added a plan for a definitive fix in block layouter.
This commit is contained in:
126
.agent/plans/css_block_semantics_full_compliance_plan.md
Normal file
126
.agent/plans/css_block_semantics_full_compliance_plan.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# CSS Block Semantics — Full Compliance Plan
|
||||
|
||||
This plan addresses the remaining pragmatic deviations between the RichText-based layout engine and the CSS 2.1 block formatting context model. The goal is a fully compliant web engine with no special-case "richtext mode" vs. "HTML mode" distinction.
|
||||
|
||||
**AGENT DIRECTIVE (CRITICAL):** Compile and run the unit tests after EVERY step. Take a git stash snapshot (`git stash push -m "Step X.Y passed"`) upon passing a step. Do NOT proceed if any test regresses.
|
||||
|
||||
---
|
||||
|
||||
## Current Deviation Summary
|
||||
|
||||
| # | Deviation | Impact |
|
||||
|---|-----------|--------|
|
||||
| D1 | `isBlock && flowX > 0` allows blocks to sit beside floats when no inline content preceded them. CSS spec: block always starts on a new line (§9.4.1). | Blocks float-side-by-side in float-aware path instead of stacking vertically. |
|
||||
| D2 | RichText `CustomBlock` with `isBlock` piggybacks line-break behavior onto a text-flow engine. A block formatting context should stack child boxes vertically, not inline them with `isBlock` flags. | Semantic confusion: the RichText engine conflates text flow and block layout. |
|
||||
| D3 | Inline-block spans are decomposed into RichText text segments that wrap line-by-line, rather than being treated as a single opaque box. | The "solid box" semantics of inline-block are partially emulated with `atomicMaxX` / forced line breaks, but edge cases remain. |
|
||||
| D4 | `<richtext>` widgets and `<div>` containers are both `UIRichText` with `CSSDisplay::Block`, yet `<richtext>` has historically flowed children inline. The tag-based distinction was removed; now both use CSS display semantics, which is correct. | No remaining deviation — this is resolved in the current PR. |
|
||||
|
||||
## Phase 1: Isolate RichText from Block Layout
|
||||
|
||||
The RichText engine (`Graphics::RichText`) should handle **text formatting only** — word wrapping, line breaking, glyph placement. It should NOT decide whether an element breaks to a new line. That decision belongs to the Block Formatting Context established by the `BlockLayouter`.
|
||||
|
||||
### Step 1.1: Remove `isBlock` from `RichText::CustomBlock` and `RichText::addCustomSize`
|
||||
|
||||
- `CustomBlock` drops the `isBlock` field.
|
||||
- `addCustomSize` drops the `isBlock` parameter.
|
||||
- `RichText::updateLayout()` removes all `isBlock`-gated line-break logic (both non-float and float-aware paths). Every `CustomBlock` flows as an inline-level atomic box.
|
||||
- All `fillParent` / width-override logic stays in `UIRichText::rebuildRichText`.
|
||||
- **Validation:** Compile. Many tests will fail — this is expected. Continue.
|
||||
|
||||
### Step 1.2: Move block-level line breaking into `BlockLayouter`
|
||||
|
||||
- `BlockLayouter::updateLayout()` now builds the RichText content in **per-child layers** rather than delegating everything to a single `rebuildRichText` call.
|
||||
- For each child widget:
|
||||
- If the child is mergeable (text span, inline), it is added to the current RichText via `UIRichText::rebuildRichText`.
|
||||
- If the child is a block-level element (display != Inline and != InlineBlock), a **new line** is forced before the child. This is done by inserting a `\n` into the RichText or by finalizing the current line and starting a fresh `RenderParagraph`.
|
||||
- The container's own `positionRichTextChildren` is called once after ALL children are placed.
|
||||
- **Validation:** All tests pass. (Snapshot)
|
||||
|
||||
### Step 1.3: Remove the `flowX` workaround
|
||||
|
||||
- With block-line-breaking moved into `BlockLayouter`, the `flowX` / `curX` saving logic in the float-aware path is no longer needed. Blocks always start on a new line (per CSS).
|
||||
- Remove the `flowX` variable and associated logic from `RichText::updateLayout()` float-aware path.
|
||||
- Remove the "block overflow below floats" block (it's superseded by the block-layouter approach).
|
||||
- **Validation:** `UIHTML.blockFlow`, `UIHTML.blockFlowFloat`, all `UIHTMLFloat.*` tests pass. (Snapshot)
|
||||
|
||||
## Phase 2: Full Block Formatting Context
|
||||
|
||||
### Step 2.1: Block children interact with floats
|
||||
|
||||
- When `BlockLayouter` places a block child and there are active floats (from the RichText engine at the current Y), the block child's margin box must respect the float constraints:
|
||||
- If the block fits in the available width (between left-floats and right-floats), it stays on the same line but its width is narrowed.
|
||||
- If the block does NOT fit, it moves to the next "float-free" Y (below all active floats) — effectively an implicit clear.
|
||||
- Implement this in `BlockLayouter::updateLayout()` by querying `RichText` for the current float state.
|
||||
- The existing float overflow logic in `RichText` (for float-on-float overflow) stays in place. The block-vs-float overflow is now handled in `BlockLayouter`.
|
||||
- **Validation:** `UIHTMLFloat.floatWrapsContentBelowWhenTooWide` and all float tests pass. (Snapshot)
|
||||
|
||||
### Step 2.2: `positionRichTextChildren` handles block-level Y offsets
|
||||
|
||||
- Currently, block and inline children are positioned by the same `positionRichTextChildren` loop, which walks RichText lines and assigns positions.
|
||||
- With the new approach, block children are placed at the start of a new RichText line (their own line). Their Y position is determined by the line they occupy.
|
||||
- `BlockLayouter` may need to track which children are block vs. inline so it can query the correct line Y.
|
||||
- **Validation:** All block-positioning tests pass (`UITextNode_BlockLayouter.*`, `UIHTML.blockFlow`, `UIRichText.MarginsTest`). (Snapshot)
|
||||
|
||||
## Phase 3: Inline-Block Box Semantics
|
||||
|
||||
### Step 3.1: Treat inline-block as an opaque `CustomBlock`
|
||||
|
||||
- In `UIRichText::rebuildRichText`, when a child span has `isInlineBlock() == true`, do NOT flatten its text into the parent RichText.
|
||||
- Instead, render the inline-block's content into its OWN `RichText` instance (via its own `BlockLayouter`), producing a single `Sizef` representing the box.
|
||||
- Add this box to the parent RichText via `addCustomSize` (with `floatType = None`, `clearType = None`). No `isBlock` flag needed.
|
||||
- This makes the inline-block a single opaque rectangle in the parent's inline flow — exactly matching CSS.
|
||||
- Remove the `atomicMaxX` tracking and multiline forced-break logic from `RichText::updateLayout()` (both paths).
|
||||
- **Validation:** `UIHTML.InlineBlockBrowserTest`, `UIHTML.InlineBlock*` tests pass. (Snapshot)
|
||||
|
||||
### Step 3.2: Inline-block height and baseline alignment
|
||||
|
||||
- CSS inline-blocks align to the parent's baseline. The `RichText` line layout must handle the inline-block's height correctly (via `maxAscent` and `lineHeight` in `RenderParagraph`).
|
||||
- If the inline-block contains text, its own RichText produces a height. This height must be passed to the parent's `addCustomSize` as the box height.
|
||||
- Baseline of the inline-block = baseline of its last line of text (or bottom if no text).
|
||||
- **Validation:** Inline-block vertical alignment tests pass. (Snapshot)
|
||||
|
||||
## Phase 4: Cleanup and Regression
|
||||
|
||||
### Step 4.1: Remove dead code
|
||||
|
||||
- Remove `isBlock`-related fields from `SpanBlock` and `CustomBlock` structs.
|
||||
- Remove `isBlock` parameter from `RichText::addSpan` (line-height variant), `RichText::addCustomSize`.
|
||||
- Remove `atomicMaxX` tracking from both layout paths.
|
||||
- Remove `flowX` logic from the float-aware path.
|
||||
- Remove the "block overflow below floats" special case from the float-aware path.
|
||||
- **Validation:** Full test suite (280 tests). Must all pass. (Snapshot)
|
||||
|
||||
### Step 4.2: Add spec-compliance regression tests
|
||||
|
||||
- Write tests for edge cases identified during migration:
|
||||
- Block after float with no inline content: block must start on a new line (D1 resolution).
|
||||
- Block with explicit width after float: block starts below float if it doesn't fit.
|
||||
- Float → Block → Inline sequence: block goes below float, inline flows beside block.
|
||||
- Inline-block beside float: inline-block box flows beside float, not decomposed.
|
||||
- Nested inline-blocks: outer inline-block contains inner inline-block.
|
||||
- **Validation:** All new tests pass. (Snapshot)
|
||||
|
||||
---
|
||||
|
||||
## Migration Order (Dependency Graph)
|
||||
|
||||
```
|
||||
Phase 1 (isolate RichText)
|
||||
├─ 1.1 Remove isBlock from RichText
|
||||
├─ 1.2 Move line breaking to BlockLayouter
|
||||
└─ 1.3 Remove flowX workaround
|
||||
↓
|
||||
Phase 2 (block formatting context)
|
||||
├─ 2.1 Block-float interaction in BlockLayouter
|
||||
└─ 2.2 positionRichTextChildren block Y offsets
|
||||
↓
|
||||
Phase 3 (inline-block box semantics)
|
||||
├─ 3.1 Inline-block as opaque CustomBlock
|
||||
└─ 3.2 Baseline alignment
|
||||
↓
|
||||
Phase 4 (cleanup)
|
||||
├─ 4.1 Remove dead code
|
||||
└─ 4.2 Spec-compliance regression tests
|
||||
```
|
||||
|
||||
Each phase is a self-contained checkpoint. No phase should leave the test suite in a broken state.
|
||||
87
bin/unit_tests/assets/html/block_flow.html
Normal file
87
bin/unit_tests/assets/html/block_flow.html
Normal file
@@ -0,0 +1,87 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Block Layout</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #222;
|
||||
}
|
||||
.container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.language-section {
|
||||
width: 48%;
|
||||
margin: 4px;
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
background-color: white;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.lang-name {
|
||||
font-size: 1.25em;
|
||||
font-weight: bold;
|
||||
margin-bottom: 12px;
|
||||
color: #0066cc;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
li {
|
||||
margin: 5px 0;
|
||||
font-size: 1.05em;
|
||||
}
|
||||
.rtl {
|
||||
direction: rtl;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="language-section">
|
||||
<div class="lang-name">English</div>
|
||||
<ul>
|
||||
<li>Hello</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="language-section">
|
||||
<div class="lang-name">Chinese</div>
|
||||
<ul>
|
||||
<li>Nǐ hǎo</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="language-section">
|
||||
<div class="lang-name">Japanese</div>
|
||||
<ul>
|
||||
<li>Konnichiwa</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="language-section rtl">
|
||||
<div class="lang-name">Arabic</div>
|
||||
<ul>
|
||||
<li>Marhaban</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="language-section rtl">
|
||||
<div class="lang-name">Hebrew</div>
|
||||
<ul>
|
||||
<li>Shalom</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="language-section">
|
||||
<div class="lang-name">Russian</div>
|
||||
<ul>
|
||||
<li>Privet</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
88
bin/unit_tests/assets/html/block_flow_float_left.html
Normal file
88
bin/unit_tests/assets/html/block_flow_float_left.html
Normal file
@@ -0,0 +1,88 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Block Layout</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #222;
|
||||
}
|
||||
.container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.language-section {
|
||||
float: left;
|
||||
width: 48%;
|
||||
margin: 4px;
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
background-color: white;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.lang-name {
|
||||
font-size: 1.25em;
|
||||
font-weight: bold;
|
||||
margin-bottom: 12px;
|
||||
color: #0066cc;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
li {
|
||||
margin: 5px 0;
|
||||
font-size: 1.05em;
|
||||
}
|
||||
.rtl {
|
||||
direction: rtl;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="language-section">
|
||||
<div class="lang-name">English</div>
|
||||
<ul>
|
||||
<li>Hello</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="language-section">
|
||||
<div class="lang-name">Chinese</div>
|
||||
<ul>
|
||||
<li>Nǐ hǎo</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="language-section">
|
||||
<div class="lang-name">Japanese</div>
|
||||
<ul>
|
||||
<li>Konnichiwa</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="language-section rtl">
|
||||
<div class="lang-name">Arabic</div>
|
||||
<ul>
|
||||
<li>Marhaban</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="language-section rtl">
|
||||
<div class="lang-name">Hebrew</div>
|
||||
<ul>
|
||||
<li>Shalom</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="language-section">
|
||||
<div class="lang-name">Russian</div>
|
||||
<ul>
|
||||
<li>Privet</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
26
bin/unit_tests/assets/html/is_inline_block.html
Normal file
26
bin/unit_tests/assets/html/is_inline_block.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<style>
|
||||
.parent-container {
|
||||
width: 450px;
|
||||
}
|
||||
.target {
|
||||
background-color: #e0f7fa;
|
||||
}
|
||||
.is-inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
.is-inline {
|
||||
display: inline;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="parent-container" id="parent-ib">
|
||||
<span id="t1">Here is some normal starting text.</span>
|
||||
<span id="ib" class="target is-inline-block">This is the target inline-block element. If the container gets too narrow, this solid block drops to the next line, and its internal text will wrap, making the block taller without breaking.</span>
|
||||
<span id="t2">And here is the text that comes immediately after. It gets pushed down correctly.</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -537,6 +537,7 @@ void RichText::updateLayout() {
|
||||
wrapInfo.wraps.push_back( span->getString().size() );
|
||||
|
||||
// Emit a RenderSpan for each segment, wrapping to new lines as needed.
|
||||
Float atomicMaxX = 0;
|
||||
for ( size_t i = 0; i < wrapInfo.wraps.size() - 1; ++i ) {
|
||||
size_t startIdx = wrapInfo.wraps[i];
|
||||
size_t endIdx = wrapInfo.wraps[i + 1];
|
||||
@@ -573,6 +574,8 @@ void RichText::updateLayout() {
|
||||
|
||||
curX += spanWidth;
|
||||
currentLine.width += spanWidth;
|
||||
if ( pText->isAtomic )
|
||||
atomicMaxX = std::max( atomicMaxX, curX );
|
||||
}
|
||||
|
||||
// After the last segment, add trailing margin and check if the
|
||||
@@ -581,6 +584,8 @@ void RichText::updateLayout() {
|
||||
Float extraRight = pText->margin.Right + pText->padding.Right;
|
||||
curX += extraRight;
|
||||
mLines.back().width += extraRight;
|
||||
if ( pText->isAtomic )
|
||||
atomicMaxX = std::max( atomicMaxX, curX );
|
||||
if ( !isNewline && mMaxWidth > 0 && curX > mMaxWidth ) {
|
||||
maxWidth = std::max( maxWidth, curX );
|
||||
mLines.push_back( RenderParagraph() );
|
||||
@@ -604,6 +609,22 @@ void RichText::updateLayout() {
|
||||
curX = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Atomic (inline-block) spans reserve the width of their widest line
|
||||
// so subsequent content does not flow beside a shorter last line.
|
||||
if ( pText->isAtomic && atomicMaxX > curX ) {
|
||||
curX = atomicMaxX;
|
||||
if ( !mLines.empty() )
|
||||
mLines.back().width = std::max( mLines.back().width, curX );
|
||||
}
|
||||
|
||||
// If the inline-block spanned multiple lines, force a new line
|
||||
// so trailing content starts below the entire block.
|
||||
if ( pText->isAtomic && wrapInfo.wraps.size() > 2 && curX > 0 ) {
|
||||
maxWidth = std::max( maxWidth, curX );
|
||||
mLines.push_back( RenderParagraph() );
|
||||
curX = 0;
|
||||
}
|
||||
} else {
|
||||
// Drawable or CustomBlock (non-float).
|
||||
Sizef blockSize;
|
||||
@@ -831,6 +852,7 @@ void RichText::updateLayout() {
|
||||
wrapInfo.wraps.back() != (Float)span->getString().size() )
|
||||
wrapInfo.wraps.push_back( span->getString().size() );
|
||||
|
||||
Float atomicMaxX = 0;
|
||||
for ( size_t i = 0; i < wrapInfo.wraps.size() - 1; ++i ) {
|
||||
size_t startIdx = wrapInfo.wraps[i];
|
||||
size_t endIdx = wrapInfo.wraps[i + 1];
|
||||
@@ -866,6 +888,8 @@ void RichText::updateLayout() {
|
||||
|
||||
curX += spanWidth;
|
||||
currentLine.width += spanWidth;
|
||||
if ( pText->isAtomic )
|
||||
atomicMaxX = std::max( atomicMaxX, curX );
|
||||
}
|
||||
|
||||
// After the last segment, add trailing margin and check if the
|
||||
@@ -874,6 +898,8 @@ void RichText::updateLayout() {
|
||||
Float extraRight = pText->margin.Right + pText->padding.Right;
|
||||
curX += extraRight;
|
||||
mLines.back().width += extraRight;
|
||||
if ( pText->isAtomic )
|
||||
atomicMaxX = std::max( atomicMaxX, curX );
|
||||
if ( effW > 0 && effW < 1e9f && curX > effW ) {
|
||||
maxWidth = std::max( maxWidth, curX );
|
||||
mLines.push_back( RenderParagraph() );
|
||||
@@ -897,6 +923,21 @@ void RichText::updateLayout() {
|
||||
curX = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Atomic (inline-block) spans reserve the width of their widest line.
|
||||
if ( pText->isAtomic && atomicMaxX > curX ) {
|
||||
curX = atomicMaxX;
|
||||
if ( !mLines.empty() )
|
||||
mLines.back().width = std::max( mLines.back().width, curX );
|
||||
}
|
||||
|
||||
// If the inline-block spanned multiple lines, force a new line
|
||||
// so trailing content starts below the entire block.
|
||||
if ( pText->isAtomic && wrapInfo.wraps.size() > 2 && curX > 0 ) {
|
||||
maxWidth = std::max( maxWidth, curX );
|
||||
mLines.push_back( RenderParagraph() );
|
||||
curX = 0;
|
||||
}
|
||||
} else {
|
||||
// ── Drawable or CustomBlock ────────────────────────────
|
||||
Sizef blockSize;
|
||||
@@ -934,9 +975,41 @@ void RichText::updateLayout() {
|
||||
Float posX;
|
||||
if ( floatType == UI::CSSFloat::Left ) {
|
||||
posX = le;
|
||||
Float availW = floatRightEdge( curY );
|
||||
if ( availW > 0 && availW < 1e9f &&
|
||||
posX + blockSize.getWidth() > availW + 0.01f ) {
|
||||
Float maxBottom = curY;
|
||||
for ( auto& f : leftFloats )
|
||||
maxBottom = std::max( maxBottom, f.Bottom );
|
||||
for ( auto& f : rightFloats )
|
||||
maxBottom = std::max( maxBottom, f.Bottom );
|
||||
if ( maxBottom > curY ) {
|
||||
maxWidth = std::max( maxWidth, curX );
|
||||
mLines.push_back( RenderParagraph() );
|
||||
curX = 0;
|
||||
curY = maxBottom;
|
||||
posX = floatLeftEdge( curY );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Float re = floatRightEdge( curY );
|
||||
posX = re - blockSize.getWidth();
|
||||
if ( blockSize.getWidth() > re - le + 0.01f ) {
|
||||
Float maxBottom = curY;
|
||||
for ( auto& f : leftFloats )
|
||||
maxBottom = std::max( maxBottom, f.Bottom );
|
||||
for ( auto& f : rightFloats )
|
||||
maxBottom = std::max( maxBottom, f.Bottom );
|
||||
if ( maxBottom > curY ) {
|
||||
maxWidth = std::max( maxWidth, curX );
|
||||
mLines.push_back( RenderParagraph() );
|
||||
curX = 0;
|
||||
curY = maxBottom;
|
||||
re = floatRightEdge( curY );
|
||||
le = floatLeftEdge( curY );
|
||||
posX = re - blockSize.getWidth();
|
||||
}
|
||||
}
|
||||
if ( posX < le )
|
||||
posX = le;
|
||||
}
|
||||
@@ -955,19 +1028,44 @@ void RichText::updateLayout() {
|
||||
rightFloats.push_back( fr );
|
||||
} else {
|
||||
// ── Normal (non-float) block ────────────────────
|
||||
Float flowX = curX;
|
||||
if ( curX < le )
|
||||
curX = le;
|
||||
|
||||
// Block elements force a line break before.
|
||||
if ( isBlock && curX > 0 ) {
|
||||
maxWidth = std::max( maxWidth, curX );
|
||||
// Block elements force a line break before
|
||||
// only when there is inline-flow content on the line.
|
||||
if ( isBlock && flowX > 0 ) {
|
||||
maxWidth = std::max( maxWidth, flowX );
|
||||
mLines.push_back( RenderParagraph() );
|
||||
curX = 0;
|
||||
if ( curX < le )
|
||||
curX = le;
|
||||
}
|
||||
|
||||
Float effW = effectiveMaxWidthAt( curY );
|
||||
|
||||
// When a block does not fit beside active floats,
|
||||
// advance curY below them.
|
||||
if ( isBlock && effW > 0 && effW < 1e9f &&
|
||||
curX + blockSize.getWidth() > effW + 0.01f && curX > 0 ) {
|
||||
Float maxBottom = curY;
|
||||
for ( auto& f : leftFloats )
|
||||
maxBottom = std::max( maxBottom, f.Bottom );
|
||||
for ( auto& f : rightFloats )
|
||||
maxBottom = std::max( maxBottom, f.Bottom );
|
||||
if ( maxBottom > curY ) {
|
||||
maxWidth = std::max( maxWidth, curX );
|
||||
mLines.push_back( RenderParagraph() );
|
||||
curX = 0;
|
||||
curY = maxBottom;
|
||||
le = floatLeftEdge( curY );
|
||||
if ( curX < le )
|
||||
curX = le;
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap if the block doesn't fit in the available width
|
||||
// (narrowed by active floats).
|
||||
Float effW = effectiveMaxWidthAt( curY );
|
||||
if ( effW > 0 && effW < 1e9f && !isBlock &&
|
||||
( curX + blockSize.getWidth() >= effW || curX >= effW ) && curX > 0 ) {
|
||||
maxWidth = std::max( maxWidth, curX );
|
||||
|
||||
@@ -1032,12 +1032,15 @@ void UIRichText::rebuildRichText( UILayout* container, RichText& richText, Intri
|
||||
CSSDisplay display = widget->asType<UIHTMLWidget>()->getDisplay();
|
||||
if ( display == CSSDisplay::Inline || display == CSSDisplay::InlineBlock )
|
||||
isBlock = false;
|
||||
else if ( display == CSSDisplay::ListItem )
|
||||
else if ( display != CSSDisplay::None )
|
||||
isBlock = true;
|
||||
}
|
||||
|
||||
bool fillParent =
|
||||
isBlock && widget->getLayoutWidthPolicy() == SizePolicy::MatchParent;
|
||||
|
||||
if ( mode == IntrinsicMode::None ) {
|
||||
if ( isBlock ) {
|
||||
if ( fillParent ) {
|
||||
if ( container->getPixelsSize().getWidth() != 0 ) {
|
||||
Float maxSize =
|
||||
eemax( 0.f, container->getPixelsSize().getWidth() -
|
||||
@@ -1070,7 +1073,7 @@ void UIRichText::rebuildRichText( UILayout* container, RichText& richText, Intri
|
||||
}
|
||||
|
||||
Float w = size.getWidth();
|
||||
if ( isBlock && mode == IntrinsicMode::None &&
|
||||
if ( fillParent && mode == IntrinsicMode::None &&
|
||||
container->getPixelsSize().getWidth() != 0 ) {
|
||||
w = eemax( 0.f, container->getPixelsSize().getWidth() -
|
||||
container->getPixelsContentOffset().Left -
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
#include <eepp/ui/uilinearlayout.hpp>
|
||||
#include <eepp/ui/uirichtext.hpp>
|
||||
#include <eepp/ui/uiscenenode.hpp>
|
||||
#include <eepp/ui/uitextspan.hpp>
|
||||
#include <eepp/ui/uitextnode.hpp>
|
||||
#include <eepp/ui/uitextspan.hpp>
|
||||
#include <eepp/ui/uithememanager.hpp>
|
||||
#include <eepp/window/engine.hpp>
|
||||
|
||||
@@ -29,8 +29,8 @@ using namespace EE::UI::Tools;
|
||||
|
||||
static UI::UISceneNode* createRichTextScene() {
|
||||
Engine::instance()->createWindow( WindowSettings( 800, 600, "RichText Test",
|
||||
WindowStyle::Default, WindowBackend::Default,
|
||||
32, {}, 1, false, true ) );
|
||||
WindowStyle::Default, WindowBackend::Default,
|
||||
32, {}, 1, false, true ) );
|
||||
FileSystem::changeWorkingDirectory( Sys::getProcessPath() );
|
||||
|
||||
FontTrueType* font = FontTrueType::New( "NotoSans-Regular" );
|
||||
@@ -837,25 +837,19 @@ UTEST( UIRichText, MarginsTest ) {
|
||||
|
||||
// Check the layout position of the second div
|
||||
Vector2f pos2 = d2->getPixelsPosition();
|
||||
// The widgets flow inline (horizontally) since total width < 800.
|
||||
// Block elements each occupy their own line; d2 sits below d1 at the same x.
|
||||
// d1 footprint width: 40 (left) + 50 (width) + 20 (right) = 110.
|
||||
// d2 left margin: 5.
|
||||
// Therefore d2 x position = 110 + 5 = 115.
|
||||
// Line height is determined by max footprint height.
|
||||
// d1 footprint height: 10 + 50 + 30 = 90.
|
||||
// d2 footprint height: 5 + 50 + 5 = 60.
|
||||
// Max ascent = 90.
|
||||
// RichText baseline aligns elements to the bottom by default.
|
||||
// d2 offsetY = 90 - 60 = 30.
|
||||
// d2 y position = offsetY (30) + d2 margin top (5) = 35.
|
||||
EXPECT_EQ( 115.f, pos2.x );
|
||||
EXPECT_EQ( 35.f, pos2.y );
|
||||
// d2 x = d1 left margin = 5 (its own left margin).
|
||||
EXPECT_EQ( 5.f, pos2.x );
|
||||
// d2 y = d1 footprint height (90) + d2 margin top (5) = 95.
|
||||
EXPECT_EQ( 95.f, pos2.y );
|
||||
|
||||
// Check UIRichText bounds
|
||||
// Width = d1 footprint (110) + d2 footprint (60) = 170.
|
||||
// Height = line height (90).
|
||||
EXPECT_EQ( 170.f, rt->getPixelsSize().getWidth() );
|
||||
EXPECT_EQ( 90.f, rt->getPixelsSize().getHeight() );
|
||||
// Width = max(d1 footprint: 110, d2 footprint: 60) = 110.
|
||||
// Height = sum of line heights: d1 line 90 + d2 line 60 = 150.
|
||||
EXPECT_EQ( 110.f, rt->getPixelsSize().getWidth() );
|
||||
EXPECT_EQ( 150.f, rt->getPixelsSize().getHeight() );
|
||||
|
||||
destroyRichTextScene( sceneNode );
|
||||
}
|
||||
|
||||
@@ -27,9 +27,9 @@ UTEST( CSSInheritance, HtmlXmlLoadingInheritance ) {
|
||||
UIApplication app(
|
||||
WindowSettings( 800, 600, "eepp - CSS Inheritance Test", WindowStyle::Default,
|
||||
WindowBackend::Default, 32 ),
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash() ), 1 );
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) );
|
||||
|
||||
std::string xml = R"(
|
||||
std::string xml = R"html(
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
@@ -43,7 +43,7 @@ UTEST( CSSInheritance, HtmlXmlLoadingInheritance ) {
|
||||
<div id="testdiv">This is not color black</div>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
)html";
|
||||
|
||||
UIWidget* root = app.getUI()->loadLayoutFromString( xml );
|
||||
EXPECT_TRUE( root != nullptr );
|
||||
@@ -66,7 +66,7 @@ UTEST( CSSInheritance, ComputedFontSize ) {
|
||||
UIApplication::Settings(
|
||||
Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), scale ) );
|
||||
|
||||
std::string xml = R"(
|
||||
std::string xml = R"html(
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
@@ -82,7 +82,7 @@ UTEST( CSSInheritance, ComputedFontSize ) {
|
||||
<h1 id="testh1">test text</h1>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
)html";
|
||||
|
||||
UIWidget* root = app.getUI()->loadLayoutFromString( xml );
|
||||
EXPECT_TRUE( root != nullptr );
|
||||
@@ -114,7 +114,7 @@ UTEST( CSSInheritance, ComputedFontSizePercentageAndRem ) {
|
||||
UIApplication::Settings(
|
||||
Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), scale ) );
|
||||
|
||||
std::string xml = R"(
|
||||
std::string xml = R"html(
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
@@ -145,7 +145,7 @@ UTEST( CSSInheritance, ComputedFontSizePercentageAndRem ) {
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
)html";
|
||||
|
||||
UIWidget* root = app.getUI()->loadLayoutFromString( xml );
|
||||
EXPECT_TRUE( root != nullptr );
|
||||
@@ -161,9 +161,9 @@ UTEST( CSSInheritance, ExplicitColorInherit ) {
|
||||
UIApplication app(
|
||||
WindowSettings( 800, 600, "eepp - CSS Color Inherit Test", WindowStyle::Default,
|
||||
WindowBackend::Default, 32 ),
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash() ), 1 );
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) );
|
||||
|
||||
std::string xml = R"(
|
||||
std::string xml = R"html(
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
@@ -179,7 +179,7 @@ UTEST( CSSInheritance, ExplicitColorInherit ) {
|
||||
<div>color set on body<div id="child">should be red via inherit</div></div>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
)html";
|
||||
|
||||
UIWidget* root = app.getUI()->loadLayoutFromString( xml );
|
||||
EXPECT_TRUE( root != nullptr );
|
||||
@@ -198,7 +198,7 @@ UTEST( CSSInheritance, ExplicitFontSizeInherit ) {
|
||||
UIApplication::Settings(
|
||||
Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), scale ) );
|
||||
|
||||
std::string xml = R"(
|
||||
std::string xml = R"html(
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
@@ -217,7 +217,7 @@ UTEST( CSSInheritance, ExplicitFontSizeInherit ) {
|
||||
<div id="parent">parent<div id="child">child with inherit</div></div>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
)html";
|
||||
|
||||
UIWidget* root = app.getUI()->loadLayoutFromString( xml );
|
||||
EXPECT_TRUE( root != nullptr );
|
||||
@@ -236,7 +236,7 @@ UTEST( CSSInheritance, ExplicitFontSizeInheritEm ) {
|
||||
UIApplication::Settings(
|
||||
Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), scale ) );
|
||||
|
||||
std::string xml = R"(
|
||||
std::string xml = R"html(
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
@@ -255,7 +255,7 @@ UTEST( CSSInheritance, ExplicitFontSizeInheritEm ) {
|
||||
<div id="parent">parent (1.5em = 36px)<div id="child">inherit should be 36px</div></div>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
)html";
|
||||
|
||||
UIWidget* root = app.getUI()->loadLayoutFromString( xml );
|
||||
EXPECT_TRUE( root != nullptr );
|
||||
@@ -271,9 +271,9 @@ UTEST( CSSInheritance, ExplicitFontFamilyInherit ) {
|
||||
UIApplication app(
|
||||
WindowSettings( 800, 600, "eepp - CSS FontFamily Inherit Test", WindowStyle::Default,
|
||||
WindowBackend::Default, 32 ),
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash() ), 1 );
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) );
|
||||
|
||||
std::string xml = R"(
|
||||
std::string xml = R"html(
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
@@ -289,7 +289,7 @@ UTEST( CSSInheritance, ExplicitFontFamilyInherit ) {
|
||||
<div id="parent"><div id="child">child with font-family: inherit</div></div>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
)html";
|
||||
|
||||
UIWidget* root = app.getUI()->loadLayoutFromString( xml );
|
||||
EXPECT_TRUE( root != nullptr );
|
||||
@@ -308,9 +308,9 @@ UTEST( CSSInheritance, ExplicitBackgroundColorInherit ) {
|
||||
UIApplication app(
|
||||
WindowSettings( 800, 600, "eepp - CSS BGColor Inherit Test", WindowStyle::Default,
|
||||
WindowBackend::Default, 32 ),
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash() ), 1 );
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) );
|
||||
|
||||
std::string xml = R"(
|
||||
std::string xml = R"html(
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
@@ -326,7 +326,7 @@ UTEST( CSSInheritance, ExplicitBackgroundColorInherit ) {
|
||||
<div id="child">child with background-color: inherit</div>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
)html";
|
||||
|
||||
UIWidget* root = app.getUI()->loadLayoutFromString( xml );
|
||||
EXPECT_TRUE( root != nullptr );
|
||||
@@ -353,7 +353,7 @@ UTEST( CSSUnits, ExChWithFont ) {
|
||||
UIApplication app(
|
||||
WindowSettings( 800, 600, "eepp - CSS Units Ex/Ch Test", WindowStyle::Default,
|
||||
WindowBackend::Default, 32 ),
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash() ), 1.f );
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) );
|
||||
|
||||
Graphics::Font* font = app.getUI()->getUIThemeManager()->getDefaultFont();
|
||||
EXPECT_TRUE( font != nullptr );
|
||||
@@ -378,9 +378,9 @@ UTEST( CSSVariables, VariableReferencesSimple ) {
|
||||
UIApplication app(
|
||||
WindowSettings( 800, 600, "eepp - CSS Var Ref Test 1", WindowStyle::Default,
|
||||
WindowBackend::Default, 32 ),
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash() ), 1 );
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) );
|
||||
|
||||
std::string xml = R"(
|
||||
std::string xml = R"html(
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
@@ -395,7 +395,7 @@ UTEST( CSSVariables, VariableReferencesSimple ) {
|
||||
<div id="testdiv">Test text</div>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
)html";
|
||||
|
||||
UIWidget* root = app.getUI()->loadLayoutFromString( xml );
|
||||
EXPECT_TRUE( root != nullptr );
|
||||
@@ -410,9 +410,9 @@ UTEST( CSSVariables, VariableReferencesChain ) {
|
||||
UIApplication app(
|
||||
WindowSettings( 800, 600, "eepp - CSS Var Ref Test 2", WindowStyle::Default,
|
||||
WindowBackend::Default, 32 ),
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash() ), 1 );
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) );
|
||||
|
||||
std::string xml = R"(
|
||||
std::string xml = R"html(
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
@@ -428,7 +428,7 @@ UTEST( CSSVariables, VariableReferencesChain ) {
|
||||
<div id="testdiv">Test text</div>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
)html";
|
||||
|
||||
UIWidget* root = app.getUI()->loadLayoutFromString( xml );
|
||||
EXPECT_TRUE( root != nullptr );
|
||||
@@ -442,10 +442,10 @@ UTEST( CSSVariables, VariableReferencesChain ) {
|
||||
UTEST( CSSVariables, VariableReferencesWithPadding ) {
|
||||
UIApplication app(
|
||||
WindowSettings( 800, 600, "eepp - CSS Var Ref Test 3", WindowStyle::Default,
|
||||
WindowBackend::Default, 32 ),
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash() ), 1 );
|
||||
WindowBackend::Default, 32, {}, 1 ),
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) );
|
||||
|
||||
std::string xml = R"(
|
||||
std::string xml = R"html(
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
@@ -460,7 +460,7 @@ UTEST( CSSVariables, VariableReferencesWithPadding ) {
|
||||
<div id="testdiv">Test text</div>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
)html";
|
||||
|
||||
UIWidget* root = app.getUI()->loadLayoutFromString( xml );
|
||||
EXPECT_TRUE( root != nullptr );
|
||||
@@ -476,9 +476,9 @@ UTEST( CSSVariables, VariableReferencesMultiple ) {
|
||||
UIApplication app(
|
||||
WindowSettings( 800, 600, "eepp - CSS Var Ref Test 4", WindowStyle::Default,
|
||||
WindowBackend::Default, 32 ),
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash() ), 1 );
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) );
|
||||
|
||||
std::string xml = R"(
|
||||
std::string xml = R"html(
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
@@ -494,7 +494,7 @@ UTEST( CSSVariables, VariableReferencesMultiple ) {
|
||||
<div id="testdiv">Test text</div>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
)html";
|
||||
|
||||
UIWidget* root = app.getUI()->loadLayoutFromString( xml );
|
||||
EXPECT_TRUE( root != nullptr );
|
||||
@@ -509,9 +509,9 @@ UTEST( CSSVariables, VariableReferencesCircular ) {
|
||||
UIApplication app(
|
||||
WindowSettings( 800, 600, "eepp - CSS Var Ref Test 5", WindowStyle::Default,
|
||||
WindowBackend::Default, 32 ),
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash() ), 1 );
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) );
|
||||
|
||||
std::string xml = R"(
|
||||
std::string xml = R"html(
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
@@ -526,7 +526,7 @@ UTEST( CSSVariables, VariableReferencesCircular ) {
|
||||
<div id="testdiv">Test text</div>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
)html";
|
||||
|
||||
UIWidget* root = app.getUI()->loadLayoutFromString( xml );
|
||||
EXPECT_TRUE( root != nullptr );
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
#include <eepp/ui/uihtmlwidget.hpp>
|
||||
#include <eepp/ui/uirichtext.hpp>
|
||||
#include <eepp/ui/uiscenenode.hpp>
|
||||
#include <eepp/ui/uithememanager.hpp>
|
||||
#include <eepp/ui/uitheme.hpp>
|
||||
#include <eepp/ui/uithememanager.hpp>
|
||||
#include <eepp/window/engine.hpp>
|
||||
#include <eepp/window/window.hpp>
|
||||
|
||||
@@ -19,9 +19,9 @@ using namespace EE::Window;
|
||||
using namespace EE::Graphics;
|
||||
|
||||
static void init_float_test() {
|
||||
Engine::instance()->createWindow(
|
||||
WindowSettings( 800, 600, "Float Layout Test", WindowStyle::Default, WindowBackend::Default,
|
||||
32, {}, 1, false, true ) );
|
||||
Engine::instance()->createWindow( WindowSettings( 800, 600, "Float Layout Test",
|
||||
WindowStyle::Default, WindowBackend::Default,
|
||||
32, {}, 1, false, true ) );
|
||||
FileSystem::changeWorkingDirectory( Sys::getProcessPath() );
|
||||
|
||||
FontTrueType* font = FontTrueType::New( "NotoSans-Regular" );
|
||||
@@ -118,11 +118,13 @@ UTEST( UIHTMLFloat, richtext_NoFloatLayout_NoChange ) {
|
||||
child1->setParent( container );
|
||||
child1->setPixelsSize( 100, 50 );
|
||||
child1->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed );
|
||||
child1->setDisplay( CSSDisplay::InlineBlock );
|
||||
|
||||
UIHTMLWidget* child2 = UIHTMLWidget::New();
|
||||
child2->setParent( container );
|
||||
child2->setPixelsSize( 150, 30 );
|
||||
child2->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed );
|
||||
child2->setDisplay( CSSDisplay::InlineBlock );
|
||||
|
||||
sceneNode->updateDirtyLayouts();
|
||||
|
||||
@@ -542,10 +544,10 @@ UTEST( UIHTMLFloat, floatLeftNonHTMLwidget_NoCrash ) {
|
||||
}
|
||||
|
||||
UTEST( UIHTMLFloat, floatNotAffectedByTextAlignCenter ) {
|
||||
Engine::instance()->createWindow(
|
||||
WindowSettings( 800, 600, "Float + TextAlign Test", WindowStyle::Default,
|
||||
WindowBackend::Default, 32, {}, 1, false, true ),
|
||||
ContextSettings( false, 0, 0, GLv_default, true, false ) );
|
||||
Engine::instance()->createWindow( WindowSettings( 800, 600, "Float + TextAlign Test",
|
||||
WindowStyle::Default, WindowBackend::Default,
|
||||
32, {}, 1, false, true ),
|
||||
ContextSettings( false, 0, 0, GLv_default, true, false ) );
|
||||
FileSystem::changeWorkingDirectory( Sys::getProcessPath() );
|
||||
|
||||
FontTrueType* font = FontTrueType::New( "NotoSans-Regular" );
|
||||
|
||||
@@ -971,9 +971,6 @@ static UISceneNode* init_test_inline_block() {
|
||||
SceneManager::instance()->setCurrentUISceneNode( sceneNode );
|
||||
UIThemeManager* themeManager = sceneNode->getUIThemeManager();
|
||||
themeManager->setDefaultFont( font );
|
||||
UITheme* theme = UITheme::New( "default", "default" );
|
||||
theme->setDefaultFont( font );
|
||||
themeManager->setDefaultTheme( theme );
|
||||
themeManager->applyDefaultTheme( sceneNode->getRoot() );
|
||||
return sceneNode;
|
||||
}
|
||||
@@ -986,7 +983,7 @@ UTEST( UIHTML, InlineBlock ) {
|
||||
|
||||
UISceneNode* sceneNode = init_test_inline_block();
|
||||
|
||||
const std::string html = R"HTML(
|
||||
const std::string html = R"html(
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
@@ -1008,7 +1005,7 @@ ul > li {
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
)HTML";
|
||||
)html";
|
||||
|
||||
sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) );
|
||||
|
||||
@@ -1046,12 +1043,12 @@ UTEST( UIHTML, BlockList ) {
|
||||
|
||||
UISceneNode* sceneNode = init_test_inline_block();
|
||||
|
||||
const std::string html = R"HTML(
|
||||
const std::string html = R"html(
|
||||
<ul id="block-list">
|
||||
<li style="height: 20px">Item 1</li>
|
||||
<li style="height: 20px">Item 2</li>
|
||||
</ul>
|
||||
)HTML";
|
||||
)html";
|
||||
|
||||
sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) );
|
||||
sceneNode->update( Seconds( 1 ) );
|
||||
@@ -1091,12 +1088,12 @@ UTEST( UIHTML, InlineList ) {
|
||||
|
||||
UISceneNode* sceneNode = init_test_inline_block();
|
||||
|
||||
const std::string html = R"HTML(
|
||||
const std::string html = R"html(
|
||||
<ul style="display: block">
|
||||
<li style="display: inline">Item 1</li>
|
||||
<li style="display: inline">Item 2</li>
|
||||
</ul>
|
||||
)HTML";
|
||||
)html";
|
||||
|
||||
sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) );
|
||||
sceneNode->update( Seconds( 1 ) );
|
||||
@@ -1130,12 +1127,12 @@ UTEST( UIHTML, InlineBlockExplicitWidth ) {
|
||||
|
||||
UISceneNode* sceneNode = init_test_inline_block();
|
||||
|
||||
const std::string html = R"HTML(
|
||||
const std::string html = R"html(
|
||||
<div style="width: 200px">
|
||||
<div id="d1" style="display: inline-block; width: 150px; height: 50px"></div>
|
||||
<div id="d2" style="display: inline-block; width: 150px; height: 50px"></div>
|
||||
</div>
|
||||
)HTML";
|
||||
)html";
|
||||
|
||||
sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) );
|
||||
sceneNode->update( Seconds( 1 ) );
|
||||
@@ -1159,13 +1156,13 @@ UTEST( UIHTML, InlineBlockMixedContent ) {
|
||||
|
||||
UISceneNode* sceneNode = init_test_inline_block();
|
||||
|
||||
const std::string html = R"HTML(
|
||||
const std::string html = R"html(
|
||||
<div>
|
||||
Some text
|
||||
<div id="ib" style="display: inline-block; width: 50px; height: 50px; background-color: red"></div>
|
||||
more text
|
||||
</div>
|
||||
)HTML";
|
||||
)html";
|
||||
|
||||
sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) );
|
||||
sceneNode->update( Seconds( 1 ) );
|
||||
@@ -1212,35 +1209,8 @@ UTEST( UIHTML, InlineBlockBrowserTest ) {
|
||||
ContextSettings( false, 0, 0, GLv_default, true, false ) );
|
||||
|
||||
UI::UISceneNode* sceneNode = init_test_inline_block();
|
||||
|
||||
const std::string html = R"HTML(
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<style>
|
||||
.parent-container {
|
||||
width: 450px;
|
||||
}
|
||||
.target {
|
||||
background-color: #e0f7fa;
|
||||
}
|
||||
.is-inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
.is-inline {
|
||||
display: inline;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="parent-container" id="parent-ib">
|
||||
<span id="t1">Here is some normal starting text.</span>
|
||||
<span id="ib" class="target is-inline-block">This is the target inline-block element. If the container gets too narrow, this solid block drops to the next line, and its internal text will wrap, making the block taller without breaking.</span>
|
||||
<span id="t2">And here is the text that comes immediately after. It gets pushed down correctly.</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
)HTML";
|
||||
std::string html;
|
||||
FileSystem::fileGet( "assets/html/is_inline_block.html", html );
|
||||
|
||||
sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) );
|
||||
sceneNode->update( Seconds( 1 ) );
|
||||
@@ -1254,11 +1224,13 @@ UTEST( UIHTML, InlineBlockBrowserTest ) {
|
||||
// If it drops to the next line:
|
||||
EXPECT_GT( ib->getPixelsPosition().y, t1->getPixelsPosition().y );
|
||||
// And t2 should be AFTER ib (either horizontally or vertically)
|
||||
EXPECT_GE( t2->getPixelsPosition().y, ib->getPixelsPosition().y );
|
||||
EXPECT_GE( t2->getPixelsPosition().y,
|
||||
ib->getPixelsPosition().y + ib->getPixelsSize().getHeight() );
|
||||
if ( t2->getPixelsPosition().y == ib->getPixelsPosition().y ) {
|
||||
EXPECT_GE( t2->getPixelsPosition().x,
|
||||
ib->getPixelsPosition().x + ib->getPixelsSize().getWidth() );
|
||||
}
|
||||
EXPECT_EQ( ib->getPixelsPosition().x, t2->getPixelsPosition().x );
|
||||
|
||||
Engine::destroySingleton();
|
||||
}
|
||||
@@ -1321,7 +1293,7 @@ UTEST( UIHTML, HeightExpansion_FixedDoesNotExpand ) {
|
||||
|
||||
UI::UISceneNode* sceneNode = init_test_inline_block();
|
||||
|
||||
const std::string html = R"HTML(
|
||||
const std::string html = R"html(
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body style="margin: 0; padding: 0;">
|
||||
@@ -1329,7 +1301,7 @@ UTEST( UIHTML, HeightExpansion_FixedDoesNotExpand ) {
|
||||
<div style="position: fixed; top: 500px; height: 50px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
)HTML";
|
||||
)html";
|
||||
|
||||
sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) );
|
||||
sceneNode->update( Seconds( 1 ) );
|
||||
@@ -1602,3 +1574,71 @@ UTEST( UIHTML, AnchorsSizing ) {
|
||||
|
||||
Engine::destroySingleton();
|
||||
}
|
||||
|
||||
static UISceneNode* createWinAndLoadHTML( std::string winName, std::string htmlPath ) {
|
||||
auto win = Engine::instance()->createWindow(
|
||||
WindowSettings( 1024, 653, winName, WindowStyle::Default, WindowBackend::Default, 32, {}, 1,
|
||||
false, true ),
|
||||
ContextSettings( false, 0, 0, GLv_default, true, false ) );
|
||||
FileSystem::changeWorkingDirectory( Sys::getProcessPath() );
|
||||
|
||||
FontTrueType* font = FontTrueType::New( "NotoSans-Regular" );
|
||||
font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" );
|
||||
if ( font == nullptr || !font->loaded() )
|
||||
return nullptr;
|
||||
FontFamily::loadFromRegular( font );
|
||||
|
||||
UI::UISceneNode* sceneNode = UI::UISceneNode::New();
|
||||
SceneManager::instance()->add( sceneNode );
|
||||
UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager();
|
||||
themeManager->setDefaultFont( font );
|
||||
sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/" );
|
||||
std::string html;
|
||||
FileSystem::fileGet( htmlPath, html );
|
||||
sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) );
|
||||
win->setClearColor( Color::White );
|
||||
|
||||
win->getInput()->update();
|
||||
SceneManager::instance()->update();
|
||||
|
||||
win->clear();
|
||||
SceneManager::instance()->draw();
|
||||
win->display();
|
||||
|
||||
return sceneNode;
|
||||
}
|
||||
|
||||
UTEST( UIHTML, blockFlow ) {
|
||||
auto sceneNode = createWinAndLoadHTML( "HTML Block Flow", "assets/html/block_flow.html" );
|
||||
ASSERT_TRUE( sceneNode != nullptr );
|
||||
auto sections = sceneNode->getRoot()->findAllByClass( "language-section" );
|
||||
|
||||
ASSERT_EQ( sections.size(), (size_t)6 );
|
||||
|
||||
// Each section is display block so we expect a single section per line
|
||||
// if sections position are not equal it means that some sections are floating
|
||||
Float ref = sections[0]->getPixelsPosition().x;
|
||||
for ( auto section : sections )
|
||||
EXPECT_EQ( section->getPixelsPosition().x, ref );
|
||||
|
||||
Engine::destroySingleton();
|
||||
}
|
||||
|
||||
UTEST( UIHTML, blockFlowFloat ) {
|
||||
auto sceneNode =
|
||||
createWinAndLoadHTML( "HTML Block Flow", "assets/html/block_flow_float_left.html" );
|
||||
ASSERT_TRUE( sceneNode != nullptr );
|
||||
auto sections = sceneNode->getRoot()->findAllByClass( "language-section" );
|
||||
|
||||
ASSERT_EQ( sections.size(), (size_t)6 );
|
||||
|
||||
// Each section is display block with float: left and width 48% so we expect two sections
|
||||
// per line, and each odd index should be to the right
|
||||
Float refLeft = sections[0]->getPixelsPosition().x;
|
||||
Float refRight = sections[1]->getPixelsPosition().x;
|
||||
for ( size_t idx = 0; idx < sections.size(); idx++ ) {
|
||||
Float expected = idx % 2 == 0 ? refLeft : refRight;
|
||||
EXPECT_EQ( sections[idx]->getPixelsPosition().x, expected );
|
||||
}
|
||||
Engine::destroySingleton();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user