Commit Graph

38 Commits

Author SHA1 Message Date
Martín Lucas Golini
359dc8f157 block and inline-block layouter fixes, added a plan for a definitive fix in block layouter. 2026-05-14 01:33:00 -03:00
Martín Lucas Golini
4b58b2f61b Fix line-height. 2026-05-13 12:53:40 -03:00
Martín Lucas Golini
7be0f28c31 Fix text collapsing in UITextSpan. 2026-05-13 11:54:30 -03:00
Martín Lucas Golini
ae0150ae75 Fix inline-blocks with fixed size. 2026-05-13 01:43:03 -03:00
Martín Lucas Golini
9f257c6fef CSS: Inline-block layout, line-height inheritance, and font shorthand fixes
Core problem
Three interconnected bugs caused incorrect rendering of display: inline-block elements:
1. Inline-block elements with explicit width/height (e.g. share buttons with width: 20px; height: 20px) had zero dimensions — their internal children were never laid out.
2. Text-based inline-blocks (e.g. <a href="...">Download</a> styled as display: inline-block) were affected by an incorrect font shorthand that failed to reset line-height to normal, allowing an inherited line-height: 10 on <body> to inflate element heights via the line box strut.
3. Inline-block elements with long text did not wrap atomically to the next line when they exceeded the container width.
Changes by file
include/eepp/graphics/richtext.hpp / src/eepp/graphics/richtext.cpp
- Added lineHeight (Float) and isAtomic (bool) fields to SpanBlock. The lineHeight field provides per-span line-height overriding the global mLineHeight — this is used by inline-block spans to set their line height to the font's em height rather than inheriting the container's line-height. The isAtomic flag marks a span as an atomic inline-level box: during RichText Pass 1 (both the non-float and float-aware paths), atomic spans check whether the full text width plus margins fits on the current line; if not, they push to the next line before any text wrapping occurs. This matches the CSS behavior where an inline-block drops to a new line as an opaque unit rather than having its text wrap within the parent line.
- Updated addSpan() to accept lineHeight and isAtomic parameters (with defaults of 0 and false to preserve existing call sites).
- Added per-span line-height priority (3-tier cascade): a span's own lineHeight takes precedence over the global mLineHeight, which takes precedence over the font's intrinsic line spacing. This ensures inline-block spans use font em height (set by UIRichText::rebuildRichText), pure inline text uses the container's line-height, and all fall back to font metrics.
- Added line box strut in Pass 2 (both paths): line.height = std::max(line.height, mLineHeight) ensures every line box respects the containing block's line-height, per CSS 2.1 §10.8.1. Previously a line box's height was determined solely by the tallest inline element on that line; now the container's line-height acts as a minimum.
- Propagated pText->lineHeight and pText->isAtomic through to the SpanBlock construction in RenderSpan creation in both the non-float and float-aware paths, so the per-span values persist from addSpan() through to layout.
include/eepp/ui/uitextspan.hpp / src/eepp/ui/uitextspan.cpp
- isMergeable() now returns true for both CSSDisplay::Inline and CSSDisplay::InlineBlock. Previously only Inline was mergeable. This allows text-based inline-block content to participate in the parent container's RichText flow (baseline-aligned), while the widget still draws its own border and background independently.
- Added isInlineBlock() helper method that returns mDisplay == CSSDisplay::InlineBlock, used by callers throughout the layouter and RichText code to distinguish inline-block treatment from pure inline.
- Fixed drawBorder(): inline-block widgets now draw their border at the widget bounds (mScreenPos, mSize) without expanding by padding, because padding has already been incorporated into the widget's size via the RichText bounds expansion. Pure inline spans continue to draw the border expanded by padding (the old behavior).
src/eepp/ui/uirichtext.cpp
- In rebuildRichText(), inline-block widgets are now handled via two distinct code paths:
  - Text-based inline-block (has its own text content): treated as mergeable — text flows into the parent container's RichText as a SpanBlock with isAtomic = true for atomic line-wrapping, and the span's lineHeight set to the font's em height. Padding and margins are propagated through the SpanBlock. The widget's size and hit-boxes are computed from the parent RichText's layout (via BlockLayouter::positionRichTextChildren bounds expansion).
  - Inline-block without text (has only child widgets, e.g. share buttons containing nested <span> elements): diverted to the CustomBlock path — the widget runs its own BlockLayouter::updateLayout() to process internal children and determine its intrinsic size, and is added to the parent's RichText as a CustomBlock (atomic inline-level box). This ensures inner children are properly laid out rather than left at zero dimensions.
- Whitespace collapse logic around inline-block spans now accounts for whether the adjacent nodes are inline, preventing incorrect stripping of leading/trailing spaces.
src/eepp/ui/blocklayouter.cpp
- BlockLayouter::updateLayout(): the early return for mergeable widgets now makes an exception for inline-block widgets that have no text of their own — these still need their internal children laid out, so the layouter proceeds rather than returning early.
- BlockLayouter::positionRichTextChildren(): inline-block widgets are processed in two ways depending on whether they have text:
  - Text-based: bounds computed from the parent RichText's span positions, expanded by padding. The widget's position and size are set from these bounds.
  - No-text (CustomBlock): positioned via getNextCustomSpan() from the parent RichText's CustomBlock spans, with padding expansion for bounds.

  Both paths expand the widget's pixel bounds by padding for inline-block styling. A bool handled flag replaces the previous goto-based control flow.
- Added bounds and hit-box padding expansion for inline-block widgets in the CustomBlock positioning path (previously only the mergeable path had this).
src/eepp/ui/css/stylesheetspecification.cpp
- Fixed font shorthand registration: changed the component list from { font-style, font-size, line-spacing, font-family } to { font-style, font-weight, font-size, line-height, font-family }. The line-spacing property does not participate in the font shorthand per the CSS Fonts specification (§3.1); line-height does.
- Fixed font shorthand parser:
  - font-weight keywords (bold, bolder, lighter) are now emitted as a separate font-weight property instead of being incorrectly merged into the font-style property string.
  - When the font shorthand does not include an explicit /line-height value, the parser now always emits line-height: normal to reset any previously inherited line-height. Previously it emitted nothing, allowing an inherited line-height (e.g. line-height: 10 on <body>) to leak into child elements that used the font shorthand.
src/tests/unit_tests/uihtml_tests.cpp
- Added UIRichText.anchorPadding test: verifies that an inline-block <a> tag with padding and border renders correctly at the expected size (81×28 px for "Download" text in 11px Noto Sans with padding: 6px 14px and border: 1px solid), using both pixel-diff comparison and explicit size assertions. The HTML uses line-height: 1.5 on <body> and font: 11px on the inline-block anchor.
- Added UIRichText.anchorPaddingLineHeight test: same layout but with line-height: 10 on <body>, verifying that the per-span line-height prevents the inherited line-height: 10 from inflating the inline-block anchor's dimensions.
- Added UIBackground.InlineBlockImageSpans test: verifies that inline-block <a> elements with fixed width/height (20×20 px share buttons) and nested child <span> elements (with display: block and text-indent: -9999px) have non-zero dimensions and render correctly with pixel-diff comparison. This covers the CustomBlock path for inline-blocks without text of their own.
2026-05-13 00:49:36 -03:00
Martín Lucas Golini
0c3df503b4 Fix in background sizing when using pixel-density != 1. Updated new background tests due to the windowing size issues in the macOS CI. 2026-05-09 00:30:33 -03:00
Martín Lucas Golini
e94550a049 ui: Full HTML background property support + whitespace collapsing rewrite
Part 1: CSS background properties (UINodeDrawable, LayerDrawable)
Add `BackgroundMode` (Native / Html) to `UINodeDrawable`. When Html mode
is active, background rendering follows CSS/HTML semantics (repeat defaults
to repeat, clip plane always enabled, etc.). Native mode preserves the
original eepp behavior (no-repeat by default, clip only when repeating).
Add per-layer `background-origin`, `background-clip`, `background-attachment`
with `fromText` parsers and CSS property registration. `background-origin`
controls the reference box for position/percentage resolution.
`background-clip` enables per-layer content-box clipping.
`background-attachment: fixed` anchors the background to the scene root;
`local` is stubbed (needs per-widget scroll offset plumbing).
Replace the single `Repeat` enum with `RepeatX` / `RepeatY` enums supporting
two-value repeat (e.g. "repeat no-repeat", "space round"). Implement space
and round repeat rendering in `LayerDrawable::draw()`.
Fix `background-size: contain` to scale up (not just down) like HTML.
Rewrite the comma-separated multi-layer `background` shorthand parser with
`/size` separator, box keyword disambiguation, and attachment keywords.
UINode::{getBackground} detects `UI_HTML_ELEMENT` flag and lazily sets
`BackgroundMode::Html`.
Add golden image test: 20-tile image atlas via
`background: url(bnp.png) pos / size no-repeat` with browser-comparable HTML.
Part 2: HTML whitespace collapsing (HTMLFormatter → UIRichText)
Move whitespace collapsing from parse time to layout time. Previously
`HTMLFormatter::collapseXmlWhitespace` ran on the pugixml DOM before
CSS was resolved, using tag-name heuristics for inline/block detection.
This caused whitespace between elements with `display: inline-block`
(via CSS) to be incorrectly stripped.
The new pipeline preserves raw whitespace in `UITextNode` widgets (after
internal whitespace collapsing via `UIRichText::collapseInternalWhitespace`)
and defers boundary stripping to `UIRichText::rebuildRichText`, where every
widget's computed `CSSDisplay` is available via `UIWidget::isInlineDisplay()`.
- `UIWidget::isInlineDisplay()` returns true for text nodes and widgets
  with `CSSDisplay::Inline | InlineBlock`, used by boundary logic.
- `UITextNode::isWhitespaceOnly()` identifies collapsible text nodes.
- `findLogicalPrev/Next` walks up through inline ancestors to find the
  correct boundary element, matching HTML's whitespace transparency rule.
- Boundary stripping: leading/trailing spaces removed at block edges,
  preserved between inline-level siblings.
- Double-space prevention: when adding text that starts with a space,
  peeks at the last `SpanBlock` in RichText if it ends with a space,
  the leading space is stripped. This prevents consecutive spaces when
  whitespace nodes are separated by empty inline elements.
- `UITextNode::mLayoutCharCount` syncs the character index between
  `rebuildRichText` (where boundary-stripped text is added) and
  `positionRichTextChildren` (where widgets are mapped to render spans),
  fixing hitbox alignment for all text-bearing widgets.
Remove `HTMLFormatter::collapseXmlWhitespace`, `isInlineNode`,
`hasSignificantText`, `getLogicalPrev`, `getLogicalNext`, and the
`precomputeDisplayStyles` hack (~200 lines removed). `HTMLFormatter`
now only exposes `HTMLtoXML`.
Other fixes
- Remove `@import url(https://fonts.googleapis.com/css?family=Lato)`
  from ensoft.css (async HTTP use-after-free in test suite).
- `BlockLayouter::positionRichTextChildren` skips whitespace-only text
  nodes when advancing the character index.
- Fix in DrawableSearcher not finding already loaded textures.
2026-05-08 22:13:07 -03:00
Martín Lucas Golini
03a798820d Fix percentage resolution when parent is WrapContent (only for height's, for width we need to do some extra changes in layouters, added a plan to fix it later). 2026-05-03 12:32:27 -03:00
Martín Lucas Golini
2dcc787ed7 Fix top + bottom and left + right html element expanding. 2026-05-03 00:33:32 -03:00
Martín Lucas Golini
fcb2cca844 Fixes for position: sticky, absolute and fixed. 2026-05-02 19:41:10 -03:00
Martín Lucas Golini
d54fd398f6 Fix bad includes.
Add very hacky and basic overflow support.
2026-05-01 19:06:36 -03:00
Martín Lucas Golini
157bc2962f Added HTML forms support.
Added cookies support (now it's possible to login in websites).
Improved redirect handling in HTTP client.
Fix in `Node::find*` and `UIWidget::find*` functions to ignore closing nodes.
Fix in Node min width and height handling with pixel scale different than 1.
2026-05-01 18:52:46 -03:00
Martín Lucas Golini
2b5d08d3b0 UITextSpan now inherits from UIRichText, if it's inline-block it will behave as a block otherwise it will continue behaving as inline. This improves inline-block behavior and it's much close to the specification. 2026-04-30 16:49:03 -03:00
Martín Lucas Golini
10b8ed8622 Fix incorrect wrap in inline-blocks. 2026-04-30 13:31:36 -03:00
Martín Lucas Golini
6691d9c174 Improvements for ul and ol.
Added a few tags: dl, dt, dd.
Load style tags inside UIRichText blocks.
Added `display: list-item`.
2026-04-30 12:58:05 -03:00
Martín Lucas Golini
80faf01cf4 Tolerate a minor difference in border visual tests due to graphics drivers behaving slightly different to render rounded corners. 2026-04-29 02:03:52 -03:00
Martín Lucas Golini
84331ad9ad 1024x653 px width seems to be the upper limit to create windows on macOS CI, no idea why. 2026-04-29 01:16:13 -03:00
Martín Lucas Golini
dcd8ecee55 Split borders tests into two to fit in 650px height, which seems to be the maximum height we can use in macOS CI worker. 2026-04-29 00:56:01 -03:00
Martín Lucas Golini
f765eae28d Greatly improve borders rendering, added a few tests.
Fixed `UIStyle::getProperty`, now respects specificity.
Fixed `StyleSheetPropertiesParser::addProperty` when inserted an already existing property.
2026-04-28 21:52:31 -03:00
Martín Lucas Golini
bbd91d2399 Added support for margin and padding in text spans. 2026-04-27 23:58:13 -03:00
Martín Lucas Golini
82a93dada4 Added UIHTMLListItem to handle list items properly. 2026-04-27 01:38:37 -03:00
Martín Lucas Golini
04db5aa2f7 Fix text span attribute changes not updating the layout.
Fix UIHTMLBody.maxWidthResizingBug.
2026-04-14 01:24:50 -03:00
Martín Lucas Golini
16579dad0e margin: auto support.
background from body to html when html background is transparent.
2026-04-14 00:54:47 -03:00
Martín Lucas Golini
7d09138bb0 Add woff2 font format support.
Try again luck with the test that it's failing in macOS...
2026-04-09 00:54:22 -03:00
Martín Lucas Golini
b43508b106 CI fails on macOS just because (window has a different size for some unrelated bug I cannot replicate or control), trying different window size. 2026-04-09 00:26:38 -03:00
Martín Lucas Golini
8bbb61d8f5 Open folder if valid on press enter in path input (SpartanJ/ecode#872).
Improved resources path solving.
Minor changes in tests.
2026-04-08 20:05:52 -03:00
Martín Lucas Golini
b2a4239c14 Fix path issues in Windows. 2026-04-08 16:47:59 -03:00
Martín Lucas Golini
7c86ced332 Disable some test until i found out why they fail badly on windows.
Minor fixes in UIRichText for late recomputations (InvalidWidthLengthComputation4 test).
2026-04-08 02:01:47 -03:00
Martín Lucas Golini
fd01c1ad2c Add a few HTML visual tests. 2026-04-08 01:02:23 -03:00
Martín Lucas Golini
a79fde17d2 Improve table-layout algorithms and added fixed table layout support. 2026-04-03 19:24:14 -03:00
Martín Lucas Golini
c039536b26 Added input-mode to UITextInput and deprecated UITextInputPassword to simplify the implementation.
Added Http::setDefaultUserAgent.
2026-04-03 15:53:08 -03:00
Martín Lucas Golini
dd6b6a11bf Added basic HTML inputs support.
Fixes in StyleSheet::removeAllWithMarker and added StyleSheet::removeAllWithoutMarker.
2026-04-02 01:17:11 -03:00
Martín Lucas Golini
6ed9193b9e Improvements in the HTML Table performance, still WIP. 2026-04-01 01:48:28 -03:00
Martín Lucas Golini
e1f64d3d51 Tests should pass with these changes. 2026-03-29 01:04:50 -03:00
Martín Lucas Golini
8b111779a0 Some extra WIP for the HTML support, fixed layout calculation on HTML tables and rich text elements. Fixes in the demo, probably test won't pass yet (i cannot repro the failure!). 2026-03-28 23:14:28 -03:00
Martín Lucas Golini
08e62828b7 Try to catch the segmentation faults in linux unit tests. 2026-03-28 16:06:20 -03:00
Martín Lucas Golini
f9023edcbe Mmh?? 2026-03-28 03:03:45 -03:00
Martín Lucas Golini
a14b8f4de0 Minor fix in UIHTMLTable layouting.
Add *very* basic HTML test. And *very* basic HTML demo. This is more than basic, it's just that I need something to quickly test stuff.
2026-03-28 02:45:24 -03:00