From ce5c36c11d717ea332258fb2f9825900e78a322c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Fri, 14 Aug 2026 01:42:32 -0300 Subject: [PATCH 1/2] Fix Windows build. --- include/eepp/ui/abstract/uiabstracttableview.hpp | 7 +++++-- src/eepp/ui/abstract/uiabstracttableview.cpp | 5 ----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/include/eepp/ui/abstract/uiabstracttableview.hpp b/include/eepp/ui/abstract/uiabstracttableview.hpp index 51f2c4681..2cba6ff6e 100644 --- a/include/eepp/ui/abstract/uiabstracttableview.hpp +++ b/include/eepp/ui/abstract/uiabstracttableview.hpp @@ -2,11 +2,11 @@ #define EE_UI_UIABSTRACTTABLEVIEW_HPP #include +#include #include #include #include #include -#include #include using namespace EE::Math; @@ -212,7 +212,10 @@ class EE_API UIAbstractTableView : public UIAbstractView { bool manuallySet{ false }; UIPushButton* widget{ nullptr }; - void setWidth( Float w, bool manuallySet = false ); + void setWidth( Float w, bool manuallySet = false ) { + width = w; + this->manuallySet = manuallySet; + } }; Float mRowHeight{ 0 }; diff --git a/src/eepp/ui/abstract/uiabstracttableview.cpp b/src/eepp/ui/abstract/uiabstracttableview.cpp index 8f65466c3..1ba32dfbf 100644 --- a/src/eepp/ui/abstract/uiabstracttableview.cpp +++ b/src/eepp/ui/abstract/uiabstracttableview.cpp @@ -1483,9 +1483,4 @@ UITableCell* UIAbstractTableView::getCellFromIndex( const ModelIndex& index ) co return nullptr; } -void UIAbstractTableView::ColumnData::setWidth( Float w, bool manSet ) { - width = w; - manuallySet = manSet; -} - }}} // namespace EE::UI::Abstract From cc87ed6924feae282b8d9ad434a0037fcc0f4e4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Fri, 14 Aug 2026 01:43:34 -0300 Subject: [PATCH 2/2] Add CSS paint ordering and stacking contexts to HTML widgets Implement spec-oriented paint phases for normal flow, floats, and positioned descendants, including atomic stacking groups and applicable z-index handling for flex and grid items. Share cached ordering between drawing and hit testing, propagate invalidation across stacking scopes, and preserve ancestor transforms and clipping for promoted descendants. Add regression coverage for paint order, hit testing, reparenting, clipping, flex ordering, and the old Reddit layout. Document the supported stacking model and remove the completed implementation plan. --- .../eepp_html_paint_order_stacking_context.md | 1538 ----------------- .agent/rules/html-layout-architecture.md | 27 +- .../html/eepp-ui-reddit-old-thread.webp | Bin 63024 -> 63064 bytes include/eepp/ui/uihtmlwidget.hpp | 77 +- src/eepp/ui/uihtmlwidget.cpp | 399 ++++- src/tests/unit_tests/uihtml_flex_test.cpp | 56 +- src/tests/unit_tests/uihtml_float_tests.cpp | 265 +++ 7 files changed, 751 insertions(+), 1611 deletions(-) delete mode 100644 .agent/plans/eepp_html_paint_order_stacking_context.md diff --git a/.agent/plans/eepp_html_paint_order_stacking_context.md b/.agent/plans/eepp_html_paint_order_stacking_context.md deleted file mode 100644 index 9f1326ee7..000000000 --- a/.agent/plans/eepp_html_paint_order_stacking_context.md +++ /dev/null @@ -1,1538 +0,0 @@ -# EEPP HTML Paint Ordering and Stacking Support Plan - -## 1. Objective - -Implement a limited, efficient CSS paint-order and stacking model for EEPP’s HTML compatibility layer. - -The goal is not full browser-engine compliance. The goal is to support the most common ordering and hit-testing cases required by moderately complex traditional websites such as old Reddit, while preserving EEPP’s existing performance characteristics and avoiding a large parallel rendering architecture. - -The first concrete compatibility target is the old Reddit layout: - -```text -parent -├── .side float: right -└── .content later sibling, full parent width -``` - -The `.content` box geometrically overlaps the sidebar region and appears later in the node tree. EEPP currently hit-tests it first because hit-testing effectively follows reverse child order. - -CSS painting order places the floating `.side` above the ordinary in-flow `.content` block. Therefore, `.side` and its descendants should also be hit-tested first. - -This problem should be solved before attempting complete stacking-context support. - ---- - -# 2. Design principles - -## 2.1 Preserve the existing node tree - -The existing `Node` and `UIHTMLWidget` hierarchy remains authoritative for: - -* ownership; -* lifetime; -* layout; -* CSS inheritance; -* selectors; -* event propagation; -* relative coordinates; -* containing blocks; -* scrolling; -* transforms. - -Nodes must not be reparented for painting. - -No complete duplicate element or render tree should be introduced. - -## 2.2 Separate paint order from node order - -The node tree describes document and layout relationships. - -It does not always describe final CSS painting order. - -EEPP should derive a lightweight paint-order representation from the existing tree. Initially, this can remain local to individual containers. Later phases can add cross-parent stacking participation only where required. - -## 2.3 Keep the common path unchanged - -Containers that have none of the following should retain the current direct traversal path: - -* floats; -* positioned children requiring special ordering; -* applicable `z-index`; -* flex or grid order modification; -* supported stacking groups. - -For normal containers: - -```cpp -UILayout::drawChildren(); -UILayout::overFind(); -``` - -should remain the effective implementation. - -## 2.4 Drawing and hit-testing must share one order - -Drawing traverses the computed paint order forwards. - -Hit-testing traverses the exact same order backwards. - -There must not be separate approximations for rendering and input. - -## 2.5 Rebuild only when ordering changes - -Paint-order data must be cached. - -It should not be rebuilt: - -* every frame; -* every draw call; -* every mouse move; -* every hit-test. - -Ordering data should be invalidated only by structural or classification changes. - -## 2.6 Prefer contiguous, non-owning data - -Use: - -* `Node*` or `UIHTMLWidget*` references; -* compact enums; -* integer tree-order values; -* `SmallVector`; -* document or widget-local caches; -* generation or dirty flags. - -Avoid: - -* one allocated paint object per DOM node; -* linked render-layer hierarchies; -* general-purpose polymorphic paint nodes; -* dynamic sorting in hot paths. - -## 2.7 Prioritize practical compatibility - -The implementation should first support: - -* traditional block layouts; -* floats; -* positioned overlays; -* integer `z-index`; -* correct hit-testing; -* nested positioned stacking groups. - -Modern stacking triggers should only be added when required by real target sites. - ---- - -# 3. Existing EEPP foundation - -`UIHTMLWidget` already contains important integration points: - -* CSS `position`; -* numeric `z-index`; -* flex `order`; -* `drawChildren()`; -* `overFind()`; -* flags indicating that sorting is required; -* `buildDrawOrderVector()`, shared by drawing and hit-testing. - -The current implementation collects direct children, optionally applies flex ordering, stable-sorts them by numeric `z-index`, draws from first to last, and hit-tests from last to first. - -This is a useful foundation, but it currently assumes that child order plus numeric z-index is sufficient. - -It does not represent CSS paint phases such as floats, and it cannot handle positioned descendants that participate in the stacking scope of a non-direct ancestor. - ---- - -# 4. Supported scope - -## Initial scope - -The first implementation phases should support: - -* ordinary DOM order; -* flex order-modified child order; -* block versus float paint ordering; -* positioned elements; -* `z-index: auto`; -* integer z-index values; -* negative, zero and positive positioned ordering; -* nested atomic positioned stacking groups; -* positioned descendants crossing non-stacking ancestors; -* drawing and hit-testing agreement. - -## Initially excluded - -The initial project should not attempt complete support for: - -* opacity-created stacking contexts; -* transform-created stacking contexts; -* filters; -* masks; -* blend modes; -* `isolation`; -* `contain`; -* `will-change`; -* 3D transforms; -* compositing; -* complete inline-fragment stacking; -* all CSS Appendix E phases; -* browser-compatible outline painting; -* full pseudo-stacking behavior for every formatting construct. - -The architecture should permit future expansion without requiring those features now. - ---- - -# 5. Phase 1 — Introduce local CSS paint categories - -## Goal - -Fix common cases where direct sibling order differs from CSS paint order, especially the old Reddit float sidebar. - -This phase should require no cross-parent paint promotion and no document-wide stacking tree. - -## Initial classification - -Introduce a compact category for direct paint participants: - -```cpp -enum class HTMLPaintCategory : Uint8 { - NegativePositioned, - NormalFlow, - Float, - PositionedAutoOrZero, - PositivePositioned, -}; -``` - -The first implementation may internally use fewer categories if needed: - -```cpp -enum class HTMLPaintCategory : Uint8 { - NormalFlow, - Float, - Positioned, -}; -``` - -However, using the more complete enum from the beginning will avoid changing the cache format immediately in Phase 2. - -## Local paint item - -```cpp -struct HTMLLocalPaintItem { - Node* node{ nullptr }; - HTMLPaintCategory category{ HTMLPaintCategory::NormalFlow }; - int zIndex{ 0 }; - Uint32 treeOrder{ 0 }; -}; -``` - -The item is non-owning and compact. - -## Ordering - -The local order should be derived from: - -1. paint category; -2. z-index where applicable; -3. stable CSS tree order. - -For the old Reddit case: - -```text -.content → NormalFlow -.side → Float -``` - -Result: - -```text -drawing: - .content - .side - -hit-testing: - .side - .content -``` - -This resolves the overlap without changing geometry or layout. - -## Float treatment - -A non-positioned float should be treated as a local atomic paint participant in the float phase. - -This does not mean the float creates a full stacking context. - -Its ordinary subtree can still draw recursively as it does today. - -In later phases, explicitly stacked descendants inside a float may participate in an ancestral stacking group. - -## Container activation - -A container needs local paint ordering when it contains at least one direct child that is: - -* floated; -* positioned; -* carrying applicable z-index; -* flex/grid order-modified; -* later classified as a stacking-group root. - -Ordinary containers retain the current traversal. - -## Drawing - -```cpp -for (const HTMLLocalPaintItem& item : paintOrder) - item.node->nodeDraw(); -``` - -## Hit-testing - -```cpp -for (auto it = paintOrder.rbegin(); it != paintOrder.rend(); ++it) { - if (Node* hit = it->node->overFind(point)) - return hit; -} -``` - -## Performance - -* no cross-parent traversal; -* no new structure for ordinary containers; -* no per-frame allocation; -* a small cached vector only where special order exists; -* one category comparison per special child during rebuild. - -## Tests - -### Float over later block - -Create two overlapping siblings: - -```html -
-
-``` - -with `.side` floated and `.content` covering the parent width. - -Verify: - -* both bounds contain the test point; -* `.side` draws after `.content`; -* `.side` is hit-tested before `.content`; -* a link inside `.side` receives the hit. - -### Equal-category ordering - -Two floats with equal classification retain stable tree order. - -### Mixed normal and float children - -Normal blocks paint before floats regardless of direct child insertion order where CSS requires it. - -### Old Reddit regression - -Use the existing old Reddit fixture and assert that a point over a sidebar link resolves to that link instead of `.content`. - ---- - -# 6. Phase 2 — Cache local paint order - -## Goal - -Ensure that Phase 1 does not introduce repeated sorting or classification work into drawing and hit-testing. - -## Cache design - -Add a lazily allocated cache only to containers requiring special order: - -```cpp -struct UIHTMLPaintOrderCache { - SmallVector items; - bool dirty{ true }; -}; -``` - -Possible ownership: - -```cpp -UIHTMLPaintOrderCache* mPaintOrderCache{ nullptr }; -``` - -This avoids increasing the footprint of every HTML widget by the size of a vector. - -A container without special paint behavior has no cache allocation. - -## API - -```cpp -Span getHTMLLocalPaintOrder(); -void invalidateHTMLLocalPaintOrder(); -bool needsHTMLLocalPaintOrder() const; -``` - -## Rebuild behavior - -When dirty: - -1. collect direct children; -2. assign CSS visual tree order; -3. classify each child; -4. stable-sort only if required; -5. retain allocated capacity. - -When clean: - -* drawing reads the cached order; -* hit-testing reads the same cached order in reverse. - -## Fast path - -```cpp -void UIHTMLWidget::drawChildren() { - if (!needsHTMLLocalPaintOrder()) { - UILayout::drawChildren(); - return; - } - - drawCachedHTMLPaintOrder(); -} -``` - -Equivalent behavior should apply to `overFind()`. - -## Invalidation sources - -Invalidate local paint order on: - -* direct child insertion; -* removal; -* reparenting; -* float changes; -* position changes; -* z-index changes; -* flex/grid `order` changes; -* display changes that add or remove participation. - -## Performance tests - -Add counters in test or debug builds: - -```cpp -Uint32 getPaintOrderRebuildCount() const; -``` - -Verify: - -* repeated drawing does not rebuild; -* repeated mouse movement does not rebuild; -* unrelated style changes do not rebuild; -* one relevant property change causes one rebuild. - ---- - -# 7. Phase 3 — Correct the `z-index` representation - -## Goal - -Represent the minimum CSS semantics required for positioned stacking behavior. - -## Current limitation - -A numeric field cannot distinguish: - -```css -z-index: auto; -z-index: 0; -``` - -This distinction is essential. - -An explicit zero can establish an atomic stacking group. - -`auto` normally does not trap descendant stacking groups in the same way. - -## Data type - -Introduce: - -```cpp -struct CSSZIndex { - int value{ 0 }; - bool isAuto{ true }; - - bool operator==(const CSSZIndex&) const = default; -}; -``` - -Alternative: - -```cpp -std::optional mZIndex; -``` - -An explicit small value type is preferable because it makes the semantic meaning clear and avoids optional-related ambiguity in APIs. - -## API - -```cpp -const CSSZIndex& getCSSZIndex() const; -bool hasAutoZIndex() const; -int getZIndexValue() const; - -void setZIndexAuto(); -void setZIndex(int value); -``` - -Existing `getZIndex()` can remain temporarily for compatibility, but internal ordering code should use the tagged value. - -## Serialization - -```text -isAuto == true → "auto" -isAuto == false → integer string -``` - -## Applicability - -Introduce: - -```cpp -bool UIHTMLWidget::isCSSPositioned() const; -bool UIHTMLWidget::hasApplicableZIndex() const; -``` - -Initial behavior: - -```cpp -bool UIHTMLWidget::hasApplicableZIndex() const { - if (mZIndex.isAuto) - return false; - - return isCSSPositioned() || isFlexItem() || isGridItem(); -} -``` - -This prevents ordinary static block elements from being reordered simply because they contain a numeric z-index property. - -## Local paint classification - -Classification now becomes more precise: - -```text -positioned + negative z-index - → NegativePositioned - -float, otherwise - → Float - -positioned + auto or explicit zero - → PositionedAutoOrZero - -positioned + positive z-index - → PositivePositioned - -otherwise - → NormalFlow -``` - -The exact ordering of explicit zero versus auto can remain locally grouped until atomic stacking groups are introduced in Phase 4. - -## Tests - -* default z-index is auto; -* explicit zero remains distinguishable; -* negative values parse and serialize correctly; -* static block plus z-index does not reorder; -* positioned explicit zero is classified separately from normal flow; -* switching auto to integer invalidates the order cache; -* switching position static to relative updates applicability. - ---- - -# 8. Phase 4 — Add direct-child atomic stacking groups - -## Goal - -Support nested positioned groups among direct children without yet allowing descendants to escape non-group ancestors. - -This provides a useful intermediate capability with relatively low complexity. - -## Initial group creation rules - -A direct child creates a supported atomic stacking group when: - -* it is positioned and has explicit non-auto z-index; -* it is fixed; -* it is sticky; -* it is a flex item with explicit non-auto z-index; -* it is a grid item with explicit non-auto z-index. - -The root document is always the root stacking scope. - -## Helper - -```cpp -bool UIHTMLWidget::createsSupportedStackingGroup() const; -``` - -## Atomic behavior - -Consider: - -```html -
-
-
- -
-``` - -```css -#a { - position: relative; - z-index: 1; -} - -#high { - position: absolute; - z-index: 1000; -} - -#b { - position: relative; - z-index: 2; -} -``` - -The complete `#a` subtree remains below `#b`. - -The high z-index is only meaningful inside `#a`’s group. - -## Implementation - -The local paint item gains a flag: - -```cpp -enum HTMLPaintItemFlags : Uint8 { - HTMLPaintItemNone = 0, - HTMLPaintItemAtomicGroup = 1 << 0, -}; -``` - -A direct child that creates a supported group is drawn as one atomic local item. - -Its internal children still use its own local cached paint ordering. - -No document-wide group tree is needed yet. - -## Benefit - -This phase establishes correct nested atomicity for direct-child contexts while retaining normal recursion. - -## Limit - -It does not solve: - -```html -
-
-
- -
-``` - -where `#high-z` should participate in an ancestor stacking scope. - -That requires Phase 5. - ---- - -# 9. Phase 5 — Add limited cross-parent stacking participation - -## Goal - -Support the common positioned-descendant case that cannot be represented by sorting direct children. - -A positioned descendant of a non-stacking ancestor may need to participate in the nearest ancestral stacking group. - -## Example - -```html -
- -
- -
-``` - -```css -#popup { - position: absolute; - z-index: 10; -} - -#overlay { - position: relative; - z-index: 5; -} -``` - -If `#section` does not create a stacking group, `#popup` should paint above `#overlay`. - -The current recursive model paints the entire `#section` subtree before `#overlay`, so local sorting is insufficient. - -## Architecture - -Introduce a small document-owned stacking-group index. - -This is not a second node tree. - -It contains only: - -* the root group; -* supported real group roots; -* promoted positioned participants; -* ordering metadata. - -## Data structures - -```cpp -using HTMLStackingGroupId = Uint32; - -static constexpr HTMLStackingGroupId InvalidHTMLStackingGroup = - std::numeric_limits::max(); -``` - -```cpp -struct UIHTMLStackingItem { - UIHTMLWidget* node{ nullptr }; - HTMLStackingGroupId childGroup{ InvalidHTMLStackingGroup }; - int zIndex{ 0 }; - Uint32 treeOrder{ 0 }; - HTMLPaintCategory category{ HTMLPaintCategory::NormalFlow }; -}; -``` - -```cpp -struct UIHTMLStackingGroup { - UIHTMLWidget* owner{ nullptr }; - HTMLStackingGroupId parent{ InvalidHTMLStackingGroup }; - - SmallVector negative; - SmallVector zero; - SmallVector positive; -}; -``` - -The first implementation may use one item vector plus category keys if that is simpler. - -## Group ownership - -Each supported real group belongs atomically to its nearest ancestral real group. - -Normal non-group ancestors do not create records. - -## Build traversal - -Perform a depth-first traversal when stacking data is dirty. - -Maintain: - -```cpp -struct HTMLStackBuildState { - HTMLStackingGroupId currentGroup; - Uint32 treeOrder; -}; -``` - -### Real group - -When a node creates a supported group: - -1. create a group record; -2. add it as an atomic item in the current group; -3. traverse its descendants using the new group as current. - -### Non-group ancestor - -When a node does not create a group: - -1. leave its ordinary drawing in the normal recursive path; -2. continue searching descendants for positioned participants; -3. assign those participants to the current ancestral group. - -### Positioned `z-index:auto` - -Initially: - -* leave its ordinary box at the normal recursive location; -* do not create a real group; -* allow explicit descendant groups to continue participating in the ancestral group. - -This preserves the important distinction between auto and explicit zero. - -## Promote only special participants - -Do not create stacking items for every node. - -Collect only: - -* real supported group roots; -* positioned participants requiring ancestral ordering; -* metadata required to skip their normal recursive draw. - -All ordinary subtrees remain on the existing fast path. - ---- - -# 10. Phase 5 drawing traversal - -## Problem - -A promoted positioned descendant must not be painted when ordinary recursion reaches its immediate parent. - -It must instead be painted from its owning stacking group. - -## Paint-manager state - -Add a document-owned manager: - -```cpp -class UIHTMLPaintManager { - public: - void invalidate(); - void rebuildIfNeeded(); - - void drawDocument(); - Node* hitTestDocument(const Vector2f& point); - - bool shouldSkipDuringNormalTraversal(const UIHTMLWidget* node) const; - - private: - SmallVector mGroups; - bool mDirty{ true }; -}; -``` - -## Skip promoted descendants - -In the HTML child draw path: - -```cpp -for (const HTMLLocalPaintItem& item : localPaintOrder) { - if (paintManager && - paintManager->shouldSkipDuringNormalTraversal( - item.node->asType())) { - continue; - } - - item.node->nodeDraw(); -} -``` - -The skip lookup must be cheap. - -Possible metadata on promoted widgets: - -```cpp -HTMLStackingGroupId mHTMLPaintGroup{ InvalidHTMLStackingGroup }; -bool mHTMLPaintPromoted{ false }; -Uint32 mHTMLPaintGeneration{ 0 }; -``` - -Only `UIHTMLWidget` needs this state, not generic `Node`. - -## Group drawing order - -For the limited positioned implementation: - -1. negative positioned groups; -2. owner’s normal recursive subtree; -3. auto and zero-level positioned participants; -4. positive positioned groups. - -Pseudo-code: - -```cpp -void UIHTMLPaintManager::drawGroup(HTMLStackingGroupId groupId) { - UIHTMLStackingGroup& group = mGroups[groupId]; - - drawItems(group.negative); - - drawNormalSubtree(group.owner, groupId); - - drawItems(group.zero); - - drawItems(group.positive); -} -``` - -A child group is fully painted atomically: - -```cpp -if (item.childGroup != InvalidHTMLStackingGroup) - drawGroup(item.childGroup); -else - item.node->nodeDraw(); -``` - -## Scope limitation - -This phase should not attempt to split every widget into background, content and outline paint operations. - -It implements the common positioned ordering model around existing recursive widget painting. - -More exact CSS paint phases can be introduced later if fixtures require them. - ---- - -# 11. Phase 5 hit-testing - -Hit-testing uses the exact same group data in reverse order. - -```cpp -Node* UIHTMLPaintManager::hitTestGroup( - HTMLStackingGroupId groupId, - const Vector2f& point) { - - UIHTMLStackingGroup& group = mGroups[groupId]; - - if (Node* hit = hitTestItemsReverse(group.positive, point)) - return hit; - - if (Node* hit = hitTestItemsReverse(group.zero, point)) - return hit; - - if (Node* hit = hitTestNormalSubtree(group.owner, groupId, point)) - return hit; - - if (Node* hit = hitTestItemsReverse(group.negative, point)) - return hit; - - return hitTestOwnerSelf(group.owner, point); -} -``` - -The normal subtree hit-test must skip the same promoted descendants skipped during drawing. - -## Required invariant - -For any overlapping pair of interactive nodes: - -```text -the one painted later must be tested earlier -``` - -This invariant should be tested directly. - ---- - -# 12. Phase 6 — Refine floats and pseudo-stacking behavior - -## Goal - -Improve interactions between floats and explicit stacking descendants after the primary old Reddit case works. - -## Float behavior - -A float is not automatically a real stacking group. - -Its ordinary subtree paints together in the float phase. - -However, explicit positioned stacking groups inside the float may still participate in an ancestral real stacking group unless another supported property traps them. - -## Initial implementation - -For Phase 1: - -* treat each direct float as an atomic local paint participant; -* draw its ordinary subtree recursively; -* hit-test it atomically in reverse phase order. - -For Phase 6: - -* allow the stacking-group collector to discover explicit descendant groups inside floats; -* promote those descendants where required; -* skip them from ordinary float recursion. - -## Inline-blocks - -Inline-blocks have similar pseudo-stacking behavior but are more entangled with `RichText`. - -Do not include them until a real compatibility failure requires it. - -## RichText interaction - -EEPP’s inline content is often painted by `Graphics::RichText`, not independently by each DOM widget. - -Therefore: - -* ordinary inline spans should remain inside RichText painting; -* out-of-flow positioned children are already easier because they are excluded from normal RichText flow; -* positioned inline or atomic inline stacking should be deferred until required. - ---- - -# 13. Flex and grid paint order - -Flex `order` already modifies child drawing order in EEPP. - -This should be treated as the CSS visual tree order used when assigning `treeOrder`. - -## Local order - -For flex and grid containers: - -1. compute their CSS order-modified child sequence; -2. assign stable `treeOrder` values in that sequence; -3. classify by paint category; -4. sort by category and relevant z-index while preserving stable tree order. - -## Cross-parent traversal - -When the stacking-group collector traverses a flex or grid container, it should visit children in CSS paint order rather than raw linked-list order. - -Expose a common helper: - -```cpp -template -void UIHTMLWidget::forEachChildInCSSPaintOrder(Callback&& callback); -``` - -This avoids duplicating ordering logic between: - -* layout-related collection; -* local painting; -* stacking-group construction; -* hit-testing. - ---- - -# 14. Invalidation strategy - -## Document-level state - -Start with a single stacking-order dirty flag: - -```cpp -bool mHTMLStackingDirty{ true }; -``` - -Split it only after profiling demonstrates benefit. - -## Local cache invalidation - -A container’s local paint-order cache is invalidated by: - -* direct child insertion; -* removal; -* reparenting; -* float changes; -* position changes; -* z-index changes; -* flex/grid order changes; -* display changes; -* child group classification changes. - -## Document stacking invalidation - -The document stacking index is invalidated by: - -* any HTML node insertion or removal; -* reparenting; -* `position`; -* z-index auto/value; -* float changes where descendant traversal changes; -* display; -* flex/grid item status; -* flex/grid `order`; -* fixed/sticky classification; -* future stacking-trigger properties. - -## Changes that should not invalidate order - -* colors; -* background images; -* border colors; -* ordinary dimensions; -* ordinary positional movement after layout; -* text color; -* font changes; -* hover styles that do not affect classification. - -## Rebuild timing - -Rebuild lazily once before either: - -* document drawing; -* document hit-testing. - -It must not rebuild once for drawing and again for input in the same generation. - ---- - -# 15. Memory and performance strategy - -## No complete duplicate tree - -The stacking manager stores only: - -* actual supported group roots; -* promoted positioned participants; -* small ordering records. - -Normal HTML nodes remain absent from the stacking structure. - -## No per-frame allocation - -Vectors retain capacity across rebuilds. - -```cpp -mGroups.clear(); -mItems.clear(); -``` - -should not free memory each time. - -## No per-hit-test sorting - -All ordering is precomputed. - -Hit-testing only traverses cached arrays backwards. - -## No generic `Node` expansion - -Do not add stacking metadata to the base `Node`. - -Keep it in: - -* `UIHTMLWidget`; -* the document HTML paint manager; -* optional local caches. - -## Document fast path - -```cpp -bool UIHTMLPaintManager::hasSpecialPaintOrder() const; -``` - -When false: - -* use ordinary recursive drawing; -* use ordinary hit-testing; -* skip all stacking-group traversal. - -## Container fast path - -A container with no float, positioned participant, z-index, or order modification should not allocate a local cache. - -## Sorting strategy - -Most special containers are expected to have few relevant children. - -Use stable sorting during rebuild. - -Do not introduce more complex bucket data structures until profiling shows sorting is significant. - -For a small number of categories, a linear stable partition into phase vectors may eventually outperform general sorting: - -```cpp -negative.push_back(...) -normal.push_back(...) -floats.push_back(...) -zero.push_back(...) -positive.push_back(...) -``` - -This may be preferable from the beginning because the number of phases is fixed. - -Within negative and positive groups, sort only those vectors by z-index. - -This avoids sorting ordinary blocks and floats entirely. - ---- - -# 16. Recommended local cache layout - -A phase-based cache is likely more efficient than one globally sorted vector: - -```cpp -struct UIHTMLPaintOrderCache { - SmallVector negative; - SmallVector normal; - SmallVector floats; - SmallVector zero; - SmallVector positive; - - bool dirty{ true }; -}; -``` - -Drawing: - -```cpp -draw(negative); -draw(normal); -draw(floats); -draw(zero); -draw(positive); -``` - -Hit-testing: - -```cpp -hitReverse(positive); -hitReverse(zero); -hitReverse(floats); -hitReverse(normal); -hitReverse(negative); -``` - -Advantages: - -* no category comparison during draw or hit-test; -* no sort for normal or float phases; -* only negative and positive phases need z-index sorting; -* clear mapping to supported CSS paint behavior. - -Disadvantage: - -* several `SmallVector` objects per allocated cache. - -Because the cache is allocated only for special containers, this tradeoff is likely acceptable. - -An alternative is one vector of compact items. The final choice should be based on `SmallVector` object size and typical HTML container counts. - ---- - -# 17. Debugging support - -Add debug support before cross-parent promotion. - -## APIs - -```cpp -void dumpHTMLLocalPaintOrder() const; -void dumpHTMLStackingGroups() const; - -std::vector debugGetHTMLPaintOrder() const; -std::vector debugGetHTMLHitTestOrder() const; -``` - -## Example output - -```text -Container: body - NormalFlow: - .content - Float: - .side - PositionedAutoOrZero: - #header - PositivePositioned: - #menu z=20 -``` - -Stacking output: - -```text -Root group: html - normal subtree: body - z=1 group: #header - z=10 group: #dialog - -Group: #header - z=100 group: #dropdown -``` - -## Inspector information - -Eventually expose: - -* paint category; -* z-index auto/value; -* whether z-index applies; -* whether the node creates a supported group; -* nearest group owner; -* whether the node is promoted; -* local paint index; -* group paint index. - -This is important for diagnosing site compatibility without inspecting renderer internals manually. - ---- - -# 18. Test plan - -## 18.1 Local float ordering - -### Old Reddit shape - -```html -
-
- Link -
- -
-
-``` - -The test point must lie inside both `.side` and `.content`. - -Assertions: - -```cpp -EXPECT_TRUE(side->getWorldBounds().contains(point)); -EXPECT_TRUE(content->getWorldBounds().contains(point)); - -EXPECT_EQ(document->overFind(point), sideLink); -``` - -Debug order: - -```text -paint: - content - side - -hit: - side - content -``` - -## 18.2 Float source-order variations - -Place the float before and after the normal block and verify supported CSS phase order remains correct. - -## 18.3 Multiple floats - -Verify stable ordering between floats with equal classification. - -## 18.4 Positioned positive ordering - -Two overlapping positioned siblings with z-index 1 and 2. - -The higher value paints later and receives input first. - -## 18.5 Negative positioned ordering - -Negative positioned content paints before normal flow according to the supported subset. - -## 18.6 Auto versus zero - -Explicit zero is distinguishable and can become atomic. - -Auto does not trap descendant groups. - -## 18.7 Atomic direct-child group - -A child z-index 1000 remains inside a parent group at z-index 1 and below a sibling group at z-index 2. - -## 18.8 Cross-parent promotion - -A positioned z-index 10 descendant of a non-group ancestor paints above a sibling z-index 5 group. - -## 18.9 Promotion through a float - -An explicit positioned descendant inside a float participates in the appropriate ancestral group where supported. - -## 18.10 Dynamic invalidation - -At runtime change: - -* float to none; -* position static to relative; -* z-index auto to zero; -* zero to positive; -* flex order; -* parent node. - -Verify both drawing and hit-testing update. - -## 18.11 Cache reuse - -Repeated draw and hit-test operations must not rebuild unchanged caches. - -## 18.12 Old Reddit fixture - -Use the existing old Reddit WebView fixture and add a deterministic sidebar interaction regression. - -Avoid relying only on screenshot comparison. - ---- - -# 19. Milestones - -## Milestone A — Local float-aware paint and hit order - -Deliverables: - -* `HTMLPaintCategory`; -* float versus normal-flow ordering; -* cached shared draw/hit sequence; -* old Reddit `.side` interaction regression. - -No stacking-group tree. - -Expected risk: low to moderate. - -Expected benefit: directly fixes the stated old Reddit blocker. - -## Milestone B — Correct z-index semantics - -Deliverables: - -* auto-or-integer z-index; -* applicability rules; -* negative, zero and positive local categories; -* parser and serialization tests; -* cache invalidation. - -Expected risk: low. - -## Milestone C — Direct-child atomic groups - -Deliverables: - -* supported group classification; -* explicit zero atomicity; -* nested local group behavior; -* direct sibling stacking correctness. - -Expected risk: moderate. - -## Milestone D — Cross-parent positioned stacking - -Deliverables: - -* document-owned lightweight group index; -* positioned descendant promotion; -* skip-promoted-node traversal; -* atomic nested groups; -* reversed group hit-testing; -* debug group dump. - -Expected risk: high relative to previous phases, but still bounded. - -## Milestone E — Float and formatting refinements - -Deliverables: - -* explicit stacking descendants inside floats; -* flex/grid traversal integration; -* selected inline-block behavior where required; -* traditional-site fixture validation. - -Expected risk: moderate. - -## Milestone F — Optional modern stacking triggers - -Only when demanded by real sites: - -* transforms; -* opacity; -* containment; -* isolation; -* filters and effects. - -Expected risk: feature-dependent and potentially high. - ---- - -# 20. Recommended implementation order - -1. Add a focused test reproducing `.side` versus `.content`. -2. Add local paint categories. -3. Make float paint order override raw child order. -4. Use the same order for hit-testing. -5. Add lazy local paint-order caching. -6. Verify old Reddit sidebar interaction. -7. Represent `z-index:auto` correctly. -8. Add applicable negative, zero and positive positioned phases. -9. Add direct-child atomic stacking groups. -10. Add document-level stacking invalidation. -11. Implement cross-parent positioned participant collection. -12. Skip promoted descendants during normal recursive drawing. -13. Traverse stacking groups forwards for drawing. -14. Traverse them backwards for hit-testing. -15. Integrate float descendants, flex and grid ordering as fixtures require. -16. Profile memory, cache rebuilds and traversal overhead. -17. Only then consider transforms, opacity and other modern triggers. - ---- - -# 21. Performance acceptance criteria - -The implementation should meet these constraints: - -* no paint-order sorting every frame; -* no sorting during mouse movement; -* no complete parallel node tree; -* no stacking allocation for ordinary containers; -* no generic `Node` size increase; -* no per-node paint object; -* cached capacities reused across rebuilds; -* local ordering proportional to direct special participants; -* document stacking data proportional to real groups and promoted participants; -* documents without special ordering use the existing fast path; -* drawing and hit-testing do not perform hash lookups for every ordinary node; -* old Reddit and similar pages show no material frame-time regression. - -Suggested debug metrics: - -```cpp -struct UIHTMLPaintStats { - Uint32 localCaches; - Uint32 localCacheRebuilds; - Uint32 stackingGroups; - Uint32 promotedNodes; - Uint32 stackingRebuilds; -}; -``` - -These should be inspectable in tests and performance diagnostics. - ---- - -# 22. Final architecture target - -```text -Existing UIHTMLWidget tree - ├── ownership - ├── layout - ├── inheritance - ├── event propagation - ├── geometry - └── ordinary recursive drawing - -Optional local paint-order cache - ├── negative positioned - ├── normal flow - ├── floats - ├── zero/auto positioned - └── positive positioned - -Optional document stacking index - ├── root stacking group - ├── actual supported child groups - ├── promoted positioned descendants - ├── forward drawing order - └── reverse hit-testing order -``` - -The implementation begins with a small local correction: - -```text -normal blocks paint before floats -floats hit-test before overlapping normal blocks -``` - -It then evolves into limited positioned stacking only when direct local ordering is no longer sufficient. - -This approach solves the old Reddit case early, minimizes the amount of changed code, preserves ordinary EEPP traversal where possible, and creates an efficient foundation for broader stacking support without committing EEPP to the full complexity of a browser engine. diff --git a/.agent/rules/html-layout-architecture.md b/.agent/rules/html-layout-architecture.md index bb15a4fc2..27ce47be6 100644 --- a/.agent/rules/html-layout-architecture.md +++ b/.agent/rules/html-layout-architecture.md @@ -241,7 +241,10 @@ Requirements: - **Gaps:** `row-gap`/`column-gap` with `gap` shorthand. `gap: normal` resolves to 0px in flexbox. - **`min-width: auto` / `min-height: auto`:** Content-based minimum prevents flex items from collapsing below their min-content size. The `overflow` property suppresses this minimum when set to non-visible. - **`visibility: collapse`:** Collapsed items are invisible and zero-sized on the main axis, but their cross size still contributes to the flex line cross size (preserving layout stability). -- **`order`-modified paint order:** `UIHTMLWidget::drawChildren()` override stable-sorts children by CSS `order` when flex items have differing values. For `row-reverse`/`column-reverse`, the sorted list is also reversed before painting. +- **`order`-modified paint order:** `UIHTMLWidget::drawChildren()` stable-sorts children by CSS + `order` when flex items have differing values. Per CSS Flexbox §4.3, painting uses + order-modified document order; `row-reverse`/`column-reverse` changes layout direction but does + not reverse paint order. - **Container baseline:** After layout, the flex container's baseline is available via `FlexLayouter::getBaseline()` for use by outer formatting contexts. ### Out-Of-Flow Positioning @@ -257,6 +260,28 @@ Relative positioning should preserve normal-flow space and then offset painting/ Floats are represented in `RichText::CustomBlock` with `CSSFloat`/`CSSClear` metadata and handled by the float-aware RichText path. Float placement is edge-aligned and should not be altered by inline baseline alignment. +### Paint Order And Supported Stacking Contexts + +`UIHTMLWidget` owns HTML box paint ordering and matching reverse-order hit testing. Its lazy paint +cache implements the supported CSS 2 Appendix E phases: negative positioned stacking groups, +in-flow content, non-positioned floats, positioned descendants with `z-index: auto` or `0`, and +positive positioned stacking groups. Equal-category items retain CSS tree order; flex and grid +containers use `order`-modified document order as their input tree order. + +Positioned descendants and child stacking groups can participate in the nearest ancestor stacking +scope even when they are nested below ordinary blocks or floats. Such promoted nodes remain in their +DOM hierarchy for layout and ownership. Painting replays the skipped ancestors' transforms and exact +border/content/padding clip type, and hit testing applies the same ancestor clips. A real stacking +group is atomic: its descendant z-indices cannot escape and interleave with sibling groups. + +The current stacking-context trigger subset consists of positioned boxes with an applicable +non-`auto` `z-index`, fixed and sticky positioned boxes (including `z-index: auto`), and flex/grid +items with non-`auto` `z-index`. Relative and absolute boxes with `z-index: auto` participate in the +positioned-auto phase without becoming atomic. Opacity, transforms, filters, isolation, containment, +and other modern stacking-context triggers are not yet modeled as CSS stacking groups. Inline +fragment background/text sub-phases remain owned by `RichText`; implementing their complete Appendix +E interleaving requires fragment-level paint records rather than widget-level sorting. + ## Pixel Math All layouters must use pixel (`Px`) APIs for layout calculations: diff --git a/bin/unit_tests/assets/html/eepp-ui-reddit-old-thread.webp b/bin/unit_tests/assets/html/eepp-ui-reddit-old-thread.webp index 31db9e4957a850549f04490ff987d325f02c7753..18e60ad7eb14902a19fbbec3fae914595caf4d2a 100644 GIT binary patch delta 58303 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 J;JF~s{{hd%s_6g# delta 58317 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

vo#9>EeJ_*otu!0z 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 diff --git a/include/eepp/ui/uihtmlwidget.hpp b/include/eepp/ui/uihtmlwidget.hpp index ff8835a8b..70dedf977 100644 --- a/include/eepp/ui/uihtmlwidget.hpp +++ b/include/eepp/ui/uihtmlwidget.hpp @@ -12,6 +12,7 @@ class RichText; namespace EE { namespace UI { class UILayouter; +class UIHTMLWidget; enum class CSSFormattingRole : Uint8 { Inline, @@ -30,6 +31,16 @@ struct CSSUsedMargins { Uint8 autoSides{ 0 }; }; +struct CSSZIndex { + int value{ 0 }; + bool isAuto{ true }; + + bool operator==( const CSSZIndex& other ) const { + return value == other.value && isAuto == other.isAuto; + } + bool operator!=( const CSSZIndex& other ) const { return !( *this == other ); } +}; + struct UIHTMLWidgetFlexState { CSSFlexDirection direction{ CSSFlexDirection::Row }; CSSFlexWrap wrap{ CSSFlexWrap::NoWrap }; @@ -62,6 +73,22 @@ struct UIHTMLWidgetGridState { CSSJustifySelf justifySelf{ CSSJustifySelf::Auto }; }; +enum class HTMLPaintCategory : Uint8 { + NegativePositioned, + NormalFlow, + Float, + PositionedAutoOrZero, + PositivePositioned, +}; + +struct UIHTMLPaintOrderCache { + SmallVector items; + Uint32 rebuildCount{ 0 }; + bool dirty{ true }; +}; + +using UIHTMLPaintAncestorVector = SmallVector; + class EE_API UIHTMLWidget : public UILayout { public: static UIHTMLWidget* New(); @@ -131,10 +158,22 @@ class EE_API UIHTMLWidget : public UILayout { void setOffsets( const Rectf& offsets ); - int getZIndex() const { return mZIndex; } + const CSSZIndex& getCSSZIndex() const { return mZIndex; } + + bool hasAutoZIndex() const { return mZIndex.isAuto; } + + int getZIndex() const { return mZIndex.value; } void setZIndex( int zIndex ); + void setZIndexAuto(); + + bool isCSSPositioned() const { return mPosition != CSSPosition::Static; } + + bool hasApplicableZIndex() const; + + bool createsSupportedStackingGroup() const; + bool getNeedsOrderSort() const { return mNeedsOrderSort; } void setNeedsOrderSort( bool val ); @@ -308,10 +347,16 @@ class EE_API UIHTMLWidget : public UILayout { virtual void onChildCountChange( Node* child, const bool& removed ); - void updateZIndexSortFlag(); - void buildDrawOrderVector( SmallVector& out ) const; + Uint32 getPaintOrderRebuildCount() const; + + std::vector debugGetHTMLPaintOrder() const; + + std::vector debugGetHTMLHitTestOrder() const; + + bool isHTMLPaintPromoted() const { return mHTMLPaintOwner != nullptr; } + Float getBaseline() const; Float getContainingBlockContentWidth() const; @@ -389,14 +434,20 @@ class EE_API UIHTMLWidget : public UILayout { std::string mBottomEq{ "auto" }; std::string mLeftEq{ "auto" }; Rectf mOffsets{ 0, 0, 0, 0 }; - int mZIndex{ 0 }; + CSSZIndex mZIndex; bool mOverflowCreatesBlockFormattingContext{ false }; bool mNeedsOrderSort{ false }; - bool mNeedsZIndexSort{ false }; + bool mNeedsPaintOrder{ false }; + bool mHasStackingDescendant{ false }; + mutable bool mHasPromotedChild{ false }; + mutable bool mHasActivePromotions{ false }; Uint8 mPercentageMargins{ 0 }; UILayouter* mLayouter{ nullptr }; UIHTMLWidgetFlexState* mFlexState{ nullptr }; UIHTMLWidgetGridState* mGridState{ nullptr }; + mutable UIHTMLPaintOrderCache* mPaintOrderCache{ nullptr }; + mutable const UIHTMLWidget* mHTMLPaintOwner{ nullptr }; + mutable UIHTMLPaintAncestorVector* mHTMLPaintAncestors{ nullptr }; UnorderedMap mDataProperties; Uint32 mScrollCb{ 0 }; @@ -406,6 +457,22 @@ class EE_API UIHTMLWidget : public UILayout { void updateScrollListeners(); void onScrollTargetPositionChange(); + void invalidatePaintOrder(); + void updatePaintOrderFlag(); + const SmallVector& getPaintOrder() const; + bool isHTMLStackingScope() const; + void buildCSSChildOrder( SmallVector& out ) const; + void resetPromotedPaintState() const; + void collectStackingScopeItems( UIHTMLWidget* container, SmallVector& out, + bool directChildren ) const; + void collectStackingScopeChild( Node* child, SmallVector& out, + bool directChildren ) const; + void promoteHTMLPaintNode( UIHTMLWidget* widget ) const; + void drawHTMLPaintNode( Node* node ); + bool containsHTMLPaintClipPoint( const Vector2f& point ) const; + bool canHitHTMLPaintNode( Node* node, const Vector2f& point ) const; + bool needsHTMLPaintTraversal() const; + bool shouldSkipHTMLPaintNode( Node* node ) const; }; }} // namespace EE::UI diff --git a/src/eepp/ui/uihtmlwidget.cpp b/src/eepp/ui/uihtmlwidget.cpp index 630eada11..1e36c2bfb 100644 --- a/src/eepp/ui/uihtmlwidget.cpp +++ b/src/eepp/ui/uihtmlwidget.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -162,6 +163,8 @@ UIHTMLWidget::~UIHTMLWidget() { eeSAFE_DELETE( mLayouter ); eeSAFE_DELETE( mFlexState ); eeSAFE_DELETE( mGridState ); + eeSAFE_DELETE( mPaintOrderCache ); + eeSAFE_DELETE( mHTMLPaintAncestors ); } UILayouter* UIHTMLWidget::getLayouter() { @@ -223,6 +226,7 @@ void UIHTMLWidget::setDisplay( CSSDisplay display ) { } onDisplayChange(); + updatePaintOrderFlag(); } } @@ -351,6 +355,8 @@ void UIHTMLWidget::setVisibility( CSSVisibility val ) { void UIHTMLWidget::setCSSPosition( CSSPosition position ) { if ( mPosition != position ) { mPosition = position; + if ( Node* parent = getParent(); parent && parent->isType( UI_TYPE_HTML_WIDGET ) ) + parent->asType()->updatePaintOrderFlag(); if ( position == CSSPosition::Absolute || position == CSSPosition::Fixed ) { // Out-of-flow elements should not stretch to their containing block // until updateOutOfFlowPosition() computes the correct size from CSS @@ -369,6 +375,8 @@ void UIHTMLWidget::setCSSPosition( CSSPosition position ) { void UIHTMLWidget::setCSSFloat( CSSFloat cssFloat ) { if ( mFloat != cssFloat ) { mFloat = cssFloat; + if ( Node* parent = getParent(); parent && parent->isType( UI_TYPE_HTML_WIDGET ) ) + parent->asType()->updatePaintOrderFlag(); // A width:auto block normally fills its containing block, while a float uses the CSS // shrink-to-fit width. Represent that used-width distinction with WrapContent so the // floated box's own layouter cannot stretch it back after its parent measured it. @@ -494,103 +502,354 @@ void UIHTMLWidget::setOffsets( const Rectf& offsets ) { } void UIHTMLWidget::setZIndex( int zIndex ) { - mZIndex = zIndex; + const CSSZIndex value{ zIndex, false }; + if ( mZIndex == value ) + return; + mZIndex = value; Node* p = getParent(); if ( p && p->isType( UI_TYPE_HTML_WIDGET ) ) - p->asType()->updateZIndexSortFlag(); + p->asType()->updatePaintOrderFlag(); +} + +void UIHTMLWidget::setZIndexAuto() { + if ( mZIndex.isAuto ) + return; + mZIndex = {}; + Node* p = getParent(); + if ( p && p->isType( UI_TYPE_HTML_WIDGET ) ) + p->asType()->updatePaintOrderFlag(); +} + +bool UIHTMLWidget::hasApplicableZIndex() const { + if ( mZIndex.isAuto ) + return false; + if ( isCSSPositioned() ) + return true; + Node* parent = getParent(); + return parent && parent->isType( UI_TYPE_HTML_WIDGET ) && + ( parent->asType()->isFlex() || parent->asType()->isGrid() ); +} + +bool UIHTMLWidget::createsSupportedStackingGroup() const { + return mPosition == CSSPosition::Fixed || mPosition == CSSPosition::Sticky || + hasApplicableZIndex(); } void UIHTMLWidget::setNeedsOrderSort( bool val ) { - mNeedsOrderSort = val; + if ( mNeedsOrderSort != val ) { + mNeedsOrderSort = val; + invalidatePaintOrder(); + } } -void UIHTMLWidget::updateZIndexSortFlag() { - bool needs = false; - for ( Node* child = getFirstChild(); child; child = child->getNextNode() ) { - if ( child->isType( UI_TYPE_HTML_WIDGET ) && - child->asType()->getZIndex() != 0 ) { - needs = true; - break; +void UIHTMLWidget::invalidatePaintOrder() { + for ( Node* node = this; node; node = node->getParent() ) { + if ( node->isType( UI_TYPE_HTML_WIDGET ) ) { + auto* widget = node->asType(); + if ( widget->mPaintOrderCache ) + widget->mPaintOrderCache->dirty = true; } } - mNeedsZIndexSort = needs; +} + +void UIHTMLWidget::updatePaintOrderFlag() { + bool needsPaintOrder = false; + bool hasStackingDescendant = false; + for ( Node* child = getFirstChild(); child; child = child->getNextNode() ) { + if ( !child->isType( UI_TYPE_HTML_WIDGET ) ) + continue; + auto* htmlChild = child->asType(); + needsPaintOrder |= + ( !isFlex() && !isGrid() && htmlChild->getCSSFloat() != CSSFloat::None ) || + htmlChild->getCSSPosition() != CSSPosition::Static || htmlChild->hasApplicableZIndex(); + hasStackingDescendant |= htmlChild->isCSSPositioned() || + htmlChild->createsSupportedStackingGroup() || + htmlChild->mHasStackingDescendant; + } + const bool stackingChanged = mHasStackingDescendant != hasStackingDescendant; + mNeedsPaintOrder = needsPaintOrder; + mHasStackingDescendant = hasStackingDescendant; + invalidatePaintOrder(); + if ( stackingChanged ) { + Node* parent = getParent(); + if ( parent && parent->isType( UI_TYPE_HTML_WIDGET ) ) + parent->asType()->updatePaintOrderFlag(); + } } void UIHTMLWidget::onChildCountChange( Node* child, const bool& removed ) { UILayout::onChildCountChange( child, removed ); - if ( !removed ) - updateZIndexSortFlag(); - else if ( child->isType( UI_TYPE_HTML_WIDGET ) && - child->asType()->getZIndex() != 0 ) - updateZIndexSortFlag(); + updatePaintOrderFlag(); } void UIHTMLWidget::buildDrawOrderVector( SmallVector& out ) const { - bool flexSort = false; - bool directionReverse = false; + const auto& paintOrder = getPaintOrder(); + out.insert( out.end(), paintOrder.begin(), paintOrder.end() ); +} - if ( isFlex() ) { - CSSFlexDirection dir = getFlexDirection(); - directionReverse = - dir == CSSFlexDirection::RowReverse || dir == CSSFlexDirection::ColumnReverse; - flexSort = mNeedsOrderSort || directionReverse; +static HTMLPaintCategory getPaintCategory( Node* node ) { + // This is the supported CSS 2 Appendix E subset. Inline fragments remain owned by RichText, so + // their background/text sub-phases cannot be split here without fragment-level paint records. + if ( !node->isType( UI_TYPE_HTML_WIDGET ) ) + return HTMLPaintCategory::NormalFlow; + const auto* widget = node->asType(); + const bool positioned = widget->isCSSPositioned(); + const bool applicableZIndex = widget->hasApplicableZIndex(); + Node* parent = widget->getParent(); + const bool flexOrGridItem = + parent && parent->isType( UI_TYPE_HTML_WIDGET ) && + ( parent->asType()->isFlex() || parent->asType()->isGrid() ); + if ( applicableZIndex && widget->getZIndex() < 0 ) + return HTMLPaintCategory::NegativePositioned; + if ( !positioned && !applicableZIndex && !flexOrGridItem && + widget->getCSSFloat() != CSSFloat::None ) + return HTMLPaintCategory::Float; + if ( applicableZIndex && widget->getZIndex() > 0 ) + return HTMLPaintCategory::PositivePositioned; + if ( positioned || applicableZIndex ) + return HTMLPaintCategory::PositionedAutoOrZero; + return HTMLPaintCategory::NormalFlow; +} + +bool UIHTMLWidget::isHTMLStackingScope() const { + if ( createsSupportedStackingGroup() ) + return true; + for ( Node* parent = getParent(); parent; parent = parent->getParent() ) { + if ( parent->isType( UI_TYPE_HTML_WIDGET ) ) + return false; } + return true; +} +void UIHTMLWidget::buildCSSChildOrder( SmallVector& out ) const { for ( Node* child = getFirstChild(); child; child = child->getNextNode() ) out.push_back( child ); - - if ( flexSort && mNeedsOrderSort ) { + if ( !isFlex() && !isGrid() ) + return; + if ( mNeedsOrderSort ) { std::stable_sort( out.begin(), out.end(), []( Node* a, Node* b ) { - int aOrder = ( a->isWidget() && a->isType( UI_TYPE_HTML_WIDGET ) ) - ? a->asType()->getOrder() - : 0; - int bOrder = ( b->isWidget() && b->isType( UI_TYPE_HTML_WIDGET ) ) - ? b->asType()->getOrder() - : 0; + const int aOrder = + a->isType( UI_TYPE_HTML_WIDGET ) ? a->asType()->getOrder() : 0; + const int bOrder = + b->isType( UI_TYPE_HTML_WIDGET ) ? b->asType()->getOrder() : 0; return aOrder < bOrder; } ); } - - if ( flexSort && directionReverse ) - std::reverse( out.begin(), out.end() ); - - if ( mNeedsZIndexSort ) { - std::stable_sort( out.begin(), out.end(), []( Node* a, Node* b ) { - int aZ = - ( a->isType( UI_TYPE_HTML_WIDGET ) ) ? a->asType()->getZIndex() : 0; - int bZ = - ( b->isType( UI_TYPE_HTML_WIDGET ) ) ? b->asType()->getZIndex() : 0; - return aZ < bZ; - } ); - } } -void UIHTMLWidget::drawChildren() { - bool needsSort = mNeedsOrderSort || mNeedsZIndexSort; +void UIHTMLWidget::resetPromotedPaintState() const { + for ( Node* child = getFirstChild(); child; child = child->getNextNode() ) { + if ( !child->isType( UI_TYPE_HTML_WIDGET ) ) + continue; + auto* widget = child->asType(); + widget->mHTMLPaintOwner = nullptr; + if ( widget->mHTMLPaintAncestors ) + widget->mHTMLPaintAncestors->clear(); + widget->mHasPromotedChild = false; + if ( widget->mPaintOrderCache ) + widget->mPaintOrderCache->dirty = true; + widget->resetPromotedPaintState(); + } +} - if ( isFlex() ) { - CSSFlexDirection dir = getFlexDirection(); - if ( dir == CSSFlexDirection::RowReverse || dir == CSSFlexDirection::ColumnReverse ) - needsSort = true; +void UIHTMLWidget::promoteHTMLPaintNode( UIHTMLWidget* widget ) const { + mHasActivePromotions = true; + widget->mHTMLPaintOwner = this; + if ( !widget->mHTMLPaintAncestors ) + widget->mHTMLPaintAncestors = eeNew( UIHTMLPaintAncestorVector, () ); + for ( Node* parent = widget->getParent(); parent && parent != this; + parent = parent->getParent() ) { + if ( parent->isType( UI_TYPE_HTML_WIDGET ) ) + widget->mHTMLPaintAncestors->push_back( parent->asType() ); + } + if ( widget->getParent()->isType( UI_TYPE_HTML_WIDGET ) ) + widget->getParent()->asType()->mHasPromotedChild = true; +} + +void UIHTMLWidget::collectStackingScopeItems( UIHTMLWidget* container, SmallVector& out, + bool directChildren ) const { + if ( container->isFlex() || container->isGrid() ) { + SmallVector children; + container->buildCSSChildOrder( children ); + for ( Node* child : children ) + collectStackingScopeChild( child, out, directChildren ); + return; + } + for ( Node* child = container->getFirstChild(); child; child = child->getNextNode() ) + collectStackingScopeChild( child, out, directChildren ); +} + +void UIHTMLWidget::collectStackingScopeChild( Node* child, SmallVector& out, + bool directChildren ) const { + if ( directChildren ) + out.push_back( child ); + if ( !child->isType( UI_TYPE_HTML_WIDGET ) ) + return; + auto* widget = child->asType(); + if ( widget->createsSupportedStackingGroup() ) { + if ( !directChildren ) { + out.push_back( widget ); + promoteHTMLPaintNode( widget ); + } + return; + } + if ( !directChildren && widget->isCSSPositioned() ) { + out.push_back( widget ); + promoteHTMLPaintNode( widget ); + } + collectStackingScopeItems( widget, out, false ); +} + +const SmallVector& UIHTMLWidget::getPaintOrder() const { + if ( !mPaintOrderCache ) + mPaintOrderCache = eeNew( UIHTMLPaintOrderCache, () ); + if ( !mPaintOrderCache->dirty ) + return mPaintOrderCache->items; + + auto& out = mPaintOrderCache->items; + out.clear(); + const bool collectStacking = isHTMLStackingScope() && mHasStackingDescendant; + // A scope must clear its previous promotion metadata even when the last promoted descendant + // just stopped qualifying; otherwise its old parent can keep skipping the node indefinitely. + if ( mHasActivePromotions ) { + resetPromotedPaintState(); + mHasActivePromotions = false; + } + if ( collectStacking ) { + collectStackingScopeItems( const_cast( this ), out, true ); + } else { + buildCSSChildOrder( out ); } - if ( !needsSort ) { + if ( mNeedsPaintOrder || collectStacking ) { + std::stable_sort( out.begin(), out.end(), []( Node* a, Node* b ) { + const auto aCategory = getPaintCategory( a ); + const auto bCategory = getPaintCategory( b ); + if ( aCategory != bCategory ) + return aCategory < bCategory; + if ( aCategory == HTMLPaintCategory::NegativePositioned || + aCategory == HTMLPaintCategory::PositivePositioned ) + return a->asType()->getZIndex() < + b->asType()->getZIndex(); + return false; + } ); + } + + mPaintOrderCache->dirty = false; + ++mPaintOrderCache->rebuildCount; + return out; +} + +Uint32 UIHTMLWidget::getPaintOrderRebuildCount() const { + return mPaintOrderCache ? mPaintOrderCache->rebuildCount : 0; +} + +std::vector UIHTMLWidget::debugGetHTMLPaintOrder() const { + const auto& order = getPaintOrder(); + return { order.begin(), order.end() }; +} + +std::vector UIHTMLWidget::debugGetHTMLHitTestOrder() const { + const auto& order = getPaintOrder(); + return { order.rbegin(), order.rend() }; +} + +void UIHTMLWidget::drawHTMLPaintNode( Node* node ) { + if ( !node->isType( UI_TYPE_HTML_WIDGET ) || + node->asType()->mHTMLPaintOwner != this ) { + node->nodeDraw(); + return; + } + + auto* promoted = node->asType(); + eeASSERT( promoted->mHTMLPaintAncestors != nullptr ); + const auto& ancestors = *promoted->mHTMLPaintAncestors; + for ( auto it = ancestors.rbegin(); it != ancestors.rend(); ++it ) { + auto* ancestor = *it; + ancestor->matrixSet(); + ancestor->smartClipStart( ancestor->getClipType(), + ancestor->isMeOrParentTreeScaledOrRotatedOrFrameBuffer() ); + } + node->nodeDraw(); + for ( auto* ancestor : ancestors ) { + ancestor->smartClipEnd( ancestor->getClipType(), + ancestor->isMeOrParentTreeScaledOrRotatedOrFrameBuffer() ); + ancestor->matrixUnset(); + } +} + +bool UIHTMLWidget::containsHTMLPaintClipPoint( const Vector2f& point ) const { + if ( !isClipped() ) + return true; + const Vector2f localPoint = convertToNodeSpace( point ); + Rectf clipRect( Vector2f::Zero, getPixelsSize() ); + switch ( getClipType() ) { + case ClipType::PaddingBox: { + const Rectf& padding = getPixelsPadding(); + clipRect = Rectf( padding.Left, padding.Top, + getPixelsSize().getWidth() - padding.Left - padding.Right, + getPixelsSize().getHeight() - padding.Top - padding.Bottom ); + break; + } + case ClipType::BorderBox: { + if ( mBorder ) { + const Rectf borderDiff = mBorder->getBorderBoxDiff(); + clipRect = Rectf( borderDiff.Left, borderDiff.Top, + getPixelsSize().getWidth() + borderDiff.Right, + getPixelsSize().getHeight() + borderDiff.Bottom ); + } + break; + } + case ClipType::ContentBox: + case ClipType::None: + break; + } + return clipRect.contains( localPoint ); +} + +bool UIHTMLWidget::canHitHTMLPaintNode( Node* node, const Vector2f& point ) const { + if ( !node->isType( UI_TYPE_HTML_WIDGET ) ) + return true; + const auto* promoted = node->asType(); + if ( promoted->mHTMLPaintOwner != this || !promoted->mHTMLPaintAncestors ) + return true; + for ( auto* ancestor : *promoted->mHTMLPaintAncestors ) { + if ( !ancestor->containsHTMLPaintClipPoint( point ) ) + return false; + } + return true; +} + +bool UIHTMLWidget::needsHTMLPaintTraversal() const { + if ( mNeedsOrderSort || mNeedsPaintOrder || mHasPromotedChild || + ( isHTMLStackingScope() && mHasStackingDescendant ) ) + return true; + return false; +} + +bool UIHTMLWidget::shouldSkipHTMLPaintNode( Node* node ) const { + return node->isType( UI_TYPE_HTML_WIDGET ) && + node->asType()->mHTMLPaintOwner != nullptr && + node->asType()->mHTMLPaintOwner != this; +} + +void UIHTMLWidget::drawChildren() { + if ( !needsHTMLPaintTraversal() ) { UILayout::drawChildren(); return; } - SmallVector sortedChildren; - buildDrawOrderVector( sortedChildren ); - - for ( auto* child : sortedChildren ) { + for ( auto* child : getPaintOrder() ) { + if ( shouldSkipHTMLPaintNode( child ) ) + continue; if ( child->isVisible() ) - child->nodeDraw(); + drawHTMLPaintNode( child ); } } Node* UIHTMLWidget::overFind( const Vector2f& point ) { - if ( !mNeedsOrderSort && !mNeedsZIndexSort ) + if ( !needsHTMLPaintTraversal() ) return UILayout::overFind( point ); Node* pOver = nullptr; @@ -602,11 +861,14 @@ Node* UIHTMLWidget::overFind( const Vector2f& point ) { writeNodeFlag( NODE_FLAG_MOUSEOVER_ME_OR_CHILD, 1 ); mSceneNode->addMouseOverNode( this ); - SmallVector sortedChildren; - buildDrawOrderVector( sortedChildren ); + const auto& sortedChildren = getPaintOrder(); - // Iterate last-to-first: highest z-index = topmost = hit first + // Drawing and hit-testing share one sequence; reverse it so the last painted node wins. for ( auto it = sortedChildren.rbegin(); it != sortedChildren.rend(); ++it ) { + if ( shouldSkipHTMLPaintNode( *it ) ) + continue; + if ( !canHitHTMLPaintNode( *it, point ) ) + continue; Node* childOver = ( *it )->overFind( point ); if ( childOver ) { pOver = childOver; @@ -698,6 +960,8 @@ void UIHTMLWidget::setOrder( int val ) { auto* fs = ensureFlexState(); if ( fs->order != val ) { fs->order = val; + if ( Node* parent = getParent(); parent && parent->isType( UI_TYPE_HTML_WIDGET ) ) + parent->asType()->invalidatePaintOrder(); notifyLayoutAttrChange( LayoutInvalidation::ContainerLayout ); } } @@ -899,7 +1163,7 @@ std::string UIHTMLWidget::getPropertyString( const PropertyDefinition* propertyD case PropertyId::Left: return mLeftEq; case PropertyId::ZIndex: - return String::toString( mZIndex ); + return mZIndex.isAuto ? "auto" : String::toString( mZIndex.value ); case PropertyId::AlignmentBaseline: return std::string( CSSBaselineAlignmentHelper::toString( mBaselineAlign ) ); case PropertyId::FlexDirection: @@ -1043,7 +1307,10 @@ bool UIHTMLWidget::applyProperty( const StyleSheetProperty& attribute ) { return applied; } case PropertyId::ZIndex: { - setZIndex( attribute.asInt() ); + if ( String::trim( attribute.asString() ) == "auto" ) + setZIndexAuto(); + else + setZIndex( attribute.asInt() ); return true; } case PropertyId::Top: { @@ -1523,6 +1790,14 @@ void UIHTMLWidget::updateScrollListeners() { void UIHTMLWidget::onParentChange() { UILayout::onParentChange(); + // Promotion is derived from ancestry. Reparenting can detach this subtree before its former + // stacking scope gets a chance to rebuild, so clear all non-owning scope metadata here. + mHTMLPaintOwner = nullptr; + if ( mHTMLPaintAncestors ) + mHTMLPaintAncestors->clear(); + mHasPromotedChild = false; + resetPromotedPaintState(); + invalidatePaintOrder(); updateScrollListeners(); } diff --git a/src/tests/unit_tests/uihtml_flex_test.cpp b/src/tests/unit_tests/uihtml_flex_test.cpp index be96f7b84..d8d2e7599 100644 --- a/src/tests/unit_tests/uihtml_flex_test.cpp +++ b/src/tests/unit_tests/uihtml_flex_test.cpp @@ -2258,7 +2258,7 @@ UTEST( FlexContainer, orderPaintSortNegatives ) { Engine::destroySingleton(); } -UTEST( FlexContainer, directionReversePaintColumnReverse ) { +UTEST( FlexContainer, directionReverseLayoutKeepsColumnPaintOrder ) { Engine::instance()->createWindow( WindowSettings( 1024, 650, "Flex Test", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), @@ -2294,13 +2294,20 @@ UTEST( FlexContainer, directionReversePaintColumnReverse ) { EXPECT_GT( child1->getPixelsPosition().y, child2->getPixelsPosition().y ); EXPECT_GT( child2->getPixelsPosition().y, child3->getPixelsPosition().y ); - // All orders equal, so no order-based sort needed; direction-only reversal in drawChildren + SmallVector paintOrder; + flex->buildDrawOrderVector( paintOrder ); + ASSERT_EQ( paintOrder.size(), 3u ); + EXPECT_EQ( paintOrder[0], child1 ); + EXPECT_EQ( paintOrder[1], child2 ); + EXPECT_EQ( paintOrder[2], child3 ); + + // CSS Flexbox 4.3 uses order-modified document order, independent of flex-direction. EXPECT_FALSE( flex->getNeedsOrderSort() ); Engine::destroySingleton(); } -UTEST( FlexContainer, directionReversePaintRowReverse ) { +UTEST( FlexContainer, directionReverseLayoutKeepsRowPaintOrder ) { Engine::instance()->createWindow( WindowSettings( 1024, 650, "Flex Test", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), @@ -2336,13 +2343,20 @@ UTEST( FlexContainer, directionReversePaintRowReverse ) { EXPECT_GT( child1->getPixelsPosition().x, child2->getPixelsPosition().x ); EXPECT_GT( child2->getPixelsPosition().x, child3->getPixelsPosition().x ); - // All orders equal so no order-based sort needed + SmallVector paintOrder; + flex->buildDrawOrderVector( paintOrder ); + ASSERT_EQ( paintOrder.size(), 3u ); + EXPECT_EQ( paintOrder[0], child1 ); + EXPECT_EQ( paintOrder[1], child2 ); + EXPECT_EQ( paintOrder[2], child3 ); + + // All orders equal, so the source order is also the order-modified document order. EXPECT_FALSE( flex->getNeedsOrderSort() ); Engine::destroySingleton(); } -UTEST( FlexContainer, directionReversePaintWithOrderSort ) { +UTEST( FlexContainer, directionReverseLayoutUsesOrderModifiedPaintOrder ) { Engine::instance()->createWindow( WindowSettings( 1024, 650, "Flex Test", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), @@ -2388,6 +2402,38 @@ UTEST( FlexContainer, directionReversePaintWithOrderSort ) { EXPECT_LT( child3->getPixelsPosition().y, child2->getPixelsPosition().y ); EXPECT_TRUE( flex->getNeedsOrderSort() ); + SmallVector paintOrder; + flex->buildDrawOrderVector( paintOrder ); + ASSERT_EQ( paintOrder.size(), 3u ); + EXPECT_EQ( paintOrder[0], child2 ); + EXPECT_EQ( paintOrder[1], child3 ); + EXPECT_EQ( paintOrder[2], child1 ); + + Engine::destroySingleton(); +} + +UTEST( FlexContainer, floatDoesNotChangeFlexItemPaintPhase ) { + Engine::instance()->createWindow( WindowSettings( 640, 480, "Flex paint float", + WindowStyle::Default, WindowBackend::Default, + 32, {}, 1, false, true ), + ContextSettings( false, 0, 0, GLv_default, true, false ) ); + init_flex_test(); + UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode(); + auto* flex = UIHTMLWidget::New(); + flex->setParent( sceneNode->getRoot() ); + flex->setDisplay( CSSDisplay::Flex ); + auto* floated = UIHTMLWidget::New(); + floated->setParent( flex ); + floated->setCSSFloat( CSSFloat::Left ); + auto* normal = UIHTMLWidget::New(); + normal->setParent( flex ); + sceneNode->updateDirtyLayouts(); + + SmallVector paintOrder; + flex->buildDrawOrderVector( paintOrder ); + ASSERT_EQ( paintOrder.size(), 2u ); + EXPECT_EQ( paintOrder[0], floated ); + EXPECT_EQ( paintOrder[1], normal ); Engine::destroySingleton(); } diff --git a/src/tests/unit_tests/uihtml_float_tests.cpp b/src/tests/unit_tests/uihtml_float_tests.cpp index b9eadd484..e402df515 100644 --- a/src/tests/unit_tests/uihtml_float_tests.cpp +++ b/src/tests/unit_tests/uihtml_float_tests.cpp @@ -127,6 +127,271 @@ UTEST( UIHTMLFloat, property_GetPropertyString ) { eeDelete( w ); } +UTEST( UIHTMLFloat, zIndexPreservesAutoAndApplicability ) { + auto* child = UIHTMLWidget::New(); + + EXPECT_TRUE( child->hasAutoZIndex() ); + EXPECT_TRUE( child->getPropertyString( "z-index" ) == "auto" ); + child->applyProperty( StyleSheetProperty( "z-index", "0" ) ); + EXPECT_FALSE( child->hasAutoZIndex() ); + EXPECT_EQ( child->getZIndex(), 0 ); + EXPECT_FALSE( child->hasApplicableZIndex() ); + + child->setCSSPosition( CSSPosition::Relative ); + EXPECT_TRUE( child->hasApplicableZIndex() ); + EXPECT_TRUE( child->createsSupportedStackingGroup() ); + child->applyProperty( StyleSheetProperty( "z-index", "-3" ) ); + EXPECT_EQ( child->getZIndex(), -3 ); + EXPECT_TRUE( child->getPropertyString( "z-index" ) == "-3" ); + child->applyProperty( StyleSheetProperty( "z-index", "auto" ) ); + EXPECT_TRUE( child->hasAutoZIndex() ); + EXPECT_FALSE( child->createsSupportedStackingGroup() ); + + eeDelete( child ); +} + +UTEST( UIHTMLFloat, floatPaintsAndHitsAboveLaterNormalBlock ) { + init_float_test(); + UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode(); + + auto* parent = UIHTMLWidget::New(); + parent->setParent( sceneNode->getRoot() ); + parent->setPixelsSize( 300, 200 ); + parent->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed ); + + auto* side = UIHTMLWidget::New(); + side->setParent( parent ); + side->setPixelsSize( 100, 100 ); + side->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed ); + side->setCSSFloat( CSSFloat::Right ); + + auto* sideLink = UIHTMLWidget::New(); + sideLink->setParent( side ); + sideLink->setPixelsSize( 100, 100 ); + sideLink->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed ); + + auto* content = UIHTMLWidget::New(); + content->setParent( parent ); + content->setPixelsSize( 300, 200 ); + content->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed ); + + SmallVector paintOrder; + parent->buildDrawOrderVector( paintOrder ); + ASSERT_EQ( paintOrder.size(), 2u ); + EXPECT_EQ( paintOrder[0], content ); + EXPECT_EQ( paintOrder[1], side ); + EXPECT_EQ( parent->getPaintOrderRebuildCount(), 1u ); + + paintOrder.clear(); + parent->buildDrawOrderVector( paintOrder ); + EXPECT_EQ( parent->getPaintOrderRebuildCount(), 1u ); + EXPECT_EQ( parent->overFind( { 10, 10 } ), sideLink ); + EXPECT_EQ( parent->getPaintOrderRebuildCount(), 1u ); + + side->setCSSFloat( CSSFloat::None ); + paintOrder.clear(); + parent->buildDrawOrderVector( paintOrder ); + EXPECT_EQ( paintOrder[0], side ); + EXPECT_EQ( paintOrder[1], content ); + EXPECT_EQ( parent->getPaintOrderRebuildCount(), 2u ); + + Engine::destroySingleton(); +} + +UTEST( UIHTMLFloat, positionedDescendantParticipatesInRootStackingScope ) { + init_float_test(); + UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode(); + auto makeBox = []( Node* parent ) { + auto* box = UIHTMLWidget::New(); + box->setParent( parent ); + box->setPixelsSize( 200, 150 ); + box->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed ); + return box; + }; + + auto* root = makeBox( sceneNode->getRoot() ); + auto* section = makeBox( root ); + auto* popup = makeBox( section ); + popup->setCSSPosition( CSSPosition::Absolute ); + popup->setZIndex( 10 ); + auto* overlay = makeBox( root ); + overlay->setCSSPosition( CSSPosition::Relative ); + overlay->setZIndex( 5 ); + + auto paintOrder = root->debugGetHTMLPaintOrder(); + ASSERT_EQ( paintOrder.size(), 3u ); + EXPECT_EQ( paintOrder[0], section ); + EXPECT_EQ( paintOrder[1], overlay ); + EXPECT_EQ( paintOrder[2], popup ); + EXPECT_TRUE( popup->isHTMLPaintPromoted() ); + EXPECT_EQ( root->overFind( { 10, 10 } ), popup ); + section->setPixelsSize( 50, 50 ); + section->setClipType( ClipType::ContentBox ); + EXPECT_EQ( root->overFind( { 100, 100 } ), overlay ); + section->setPixelsSize( 100, 100 ); + section->setPadding( Rectf( 20, 20, 20, 20 ) ); + section->setClipType( ClipType::PaddingBox ); + EXPECT_EQ( root->overFind( { 10, 10 } ), overlay ); + EXPECT_EQ( root->overFind( { 30, 30 } ), popup ); + + popup->setZIndexAuto(); + paintOrder = root->debugGetHTMLPaintOrder(); + ASSERT_EQ( paintOrder.size(), 3u ); + EXPECT_EQ( paintOrder[0], section ); + EXPECT_EQ( paintOrder[1], popup ); + EXPECT_EQ( paintOrder[2], overlay ); + EXPECT_TRUE( popup->isHTMLPaintPromoted() ); + EXPECT_EQ( root->overFind( { 10, 10 } ), overlay ); + + Engine::destroySingleton(); +} + +UTEST( UIHTMLFloat, directStackingGroupIsAtomic ) { + init_float_test(); + UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode(); + auto makeGroup = []( Node* parent, int zIndex ) { + auto* group = UIHTMLWidget::New(); + group->setParent( parent ); + group->setPixelsSize( 200, 150 ); + group->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed ); + group->setCSSPosition( CSSPosition::Relative ); + group->setZIndex( zIndex ); + return group; + }; + + auto* root = UIHTMLWidget::New(); + root->setParent( sceneNode->getRoot() ); + root->setPixelsSize( 200, 150 ); + auto* lowerGroup = makeGroup( root, 1 ); + auto* highInsideLower = makeGroup( lowerGroup, 1000 ); + auto* upperGroup = makeGroup( root, 2 ); + + const auto paintOrder = root->debugGetHTMLPaintOrder(); + ASSERT_EQ( paintOrder.size(), 2u ); + EXPECT_EQ( paintOrder[0], lowerGroup ); + EXPECT_EQ( paintOrder[1], upperGroup ); + EXPECT_EQ( root->overFind( { 10, 10 } ), upperGroup ); + EXPECT_FALSE( highInsideLower->isHTMLPaintPromoted() ); + + Engine::destroySingleton(); +} + +UTEST( UIHTMLFloat, positionedDescendantPromotesThroughFloat ) { + init_float_test(); + UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode(); + auto makeBox = []( Node* parent ) { + auto* box = UIHTMLWidget::New(); + box->setParent( parent ); + box->setPixelsSize( 200, 150 ); + box->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed ); + return box; + }; + + auto* root = makeBox( sceneNode->getRoot() ); + auto* floated = makeBox( root ); + floated->setCSSFloat( CSSFloat::Left ); + auto* popup = makeBox( floated ); + popup->setCSSPosition( CSSPosition::Absolute ); + popup->setZIndex( 10 ); + auto* overlay = makeBox( root ); + overlay->setCSSPosition( CSSPosition::Relative ); + overlay->setZIndex( 5 ); + + const auto paintOrder = root->debugGetHTMLPaintOrder(); + ASSERT_EQ( paintOrder.size(), 3u ); + EXPECT_EQ( paintOrder[0], floated ); + EXPECT_EQ( paintOrder[1], overlay ); + EXPECT_EQ( paintOrder[2], popup ); + EXPECT_EQ( root->overFind( { 10, 10 } ), popup ); + + Engine::destroySingleton(); +} + +UTEST( UIHTMLFloat, removingLastPromotionClearsSkipMetadata ) { + init_float_test(); + UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode(); + auto* root = UIHTMLWidget::New(); + root->setParent( sceneNode->getRoot() ); + root->setPixelsSize( 200, 150 ); + auto* section = UIHTMLWidget::New(); + section->setParent( root ); + section->setPixelsSize( 200, 150 ); + auto* popup = UIHTMLWidget::New(); + popup->setParent( section ); + popup->setPixelsSize( 200, 150 ); + popup->setCSSPosition( CSSPosition::Absolute ); + popup->setZIndex( 10 ); + + root->debugGetHTMLPaintOrder(); + EXPECT_TRUE( popup->isHTMLPaintPromoted() ); + popup->setZIndexAuto(); + popup->setCSSPosition( CSSPosition::Static ); + root->debugGetHTMLPaintOrder(); + EXPECT_FALSE( popup->isHTMLPaintPromoted() ); + EXPECT_EQ( root->overFind( { 10, 10 } ), popup ); + + Engine::destroySingleton(); +} + +UTEST( UIHTMLFloat, nestedPositionedAutoParticipatesInAncestorPositionedPhase ) { + init_float_test(); + UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode(); + auto* root = UIHTMLWidget::New(); + root->setParent( sceneNode->getRoot() ); + root->setPixelsSize( 200, 150 ); + auto* section = UIHTMLWidget::New(); + section->setParent( root ); + section->setPixelsSize( 200, 150 ); + auto* positionedAuto = UIHTMLWidget::New(); + positionedAuto->setParent( section ); + positionedAuto->setPixelsSize( 200, 150 ); + positionedAuto->setCSSPosition( CSSPosition::Relative ); + auto* floated = UIHTMLWidget::New(); + floated->setParent( root ); + floated->setPixelsSize( 200, 150 ); + floated->setCSSFloat( CSSFloat::Left ); + + const auto paintOrder = root->debugGetHTMLPaintOrder(); + ASSERT_EQ( paintOrder.size(), 3u ); + EXPECT_EQ( paintOrder[0], section ); + EXPECT_EQ( paintOrder[1], floated ); + EXPECT_EQ( paintOrder[2], positionedAuto ); + EXPECT_TRUE( positionedAuto->isHTMLPaintPromoted() ); + EXPECT_EQ( root->overFind( { 10, 10 } ), positionedAuto ); + + Engine::destroySingleton(); +} + +UTEST( UIHTMLFloat, reparentingPromotedSubtreeClearsOldScopeMetadata ) { + init_float_test(); + UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode(); + auto* oldRoot = UIHTMLWidget::New(); + oldRoot->setParent( sceneNode->getRoot() ); + oldRoot->setPixelsSize( 200, 150 ); + auto* section = UIHTMLWidget::New(); + section->setParent( oldRoot ); + auto* popup = UIHTMLWidget::New(); + popup->setParent( section ); + popup->setPixelsSize( 100, 100 ); + popup->setCSSPosition( CSSPosition::Absolute ); + popup->setZIndex( 10 ); + auto* newRoot = UIHTMLWidget::New(); + newRoot->setParent( sceneNode->getRoot() ); + newRoot->setPixelsSize( 200, 150 ); + + oldRoot->debugGetHTMLPaintOrder(); + EXPECT_TRUE( popup->isHTMLPaintPromoted() ); + popup->setParent( newRoot ); + oldRoot->debugGetHTMLPaintOrder(); + const auto newOrder = newRoot->debugGetHTMLPaintOrder(); + ASSERT_EQ( newOrder.size(), 1u ); + EXPECT_EQ( newOrder[0], popup ); + EXPECT_FALSE( popup->isHTMLPaintPromoted() ); + EXPECT_EQ( newRoot->overFind( { 10, 10 } ), popup ); + + Engine::destroySingleton(); +} + UTEST( UIHTMLFloat, richtext_NoFloatLayout_NoChange ) { init_float_test(); UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode();