mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-10-02 11:10:31 +03:00
Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -241,7 +241,10 @@ Requirements:
|
|||||||
- **Gaps:** `row-gap`/`column-gap` with `gap` shorthand. `gap: normal` resolves to 0px in flexbox.
|
- **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.
|
- **`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).
|
- **`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.
|
- **Container baseline:** After layout, the flex container's baseline is available via `FlexLayouter::getBaseline()` for use by outer formatting contexts.
|
||||||
|
|
||||||
### Out-Of-Flow Positioning
|
### 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.
|
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
|
## Pixel Math
|
||||||
|
|
||||||
All layouters must use pixel (`Px`) APIs for layout calculations:
|
All layouters must use pixel (`Px`) APIs for layout calculations:
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
@@ -2,11 +2,11 @@
|
|||||||
#define EE_UI_UIABSTRACTTABLEVIEW_HPP
|
#define EE_UI_UIABSTRACTTABLEVIEW_HPP
|
||||||
|
|
||||||
#include <eepp/math/rect.hpp>
|
#include <eepp/math/rect.hpp>
|
||||||
|
#include <eepp/thirdparty/nlohmann/json_fwd.hpp>
|
||||||
#include <eepp/ui/abstract/uiabstractview.hpp>
|
#include <eepp/ui/abstract/uiabstractview.hpp>
|
||||||
#include <eepp/ui/uitablecell.hpp>
|
#include <eepp/ui/uitablecell.hpp>
|
||||||
#include <eepp/ui/uitableheadercolumn.hpp>
|
#include <eepp/ui/uitableheadercolumn.hpp>
|
||||||
#include <eepp/ui/uitablerow.hpp>
|
#include <eepp/ui/uitablerow.hpp>
|
||||||
#include <eepp/thirdparty/nlohmann/json_fwd.hpp>
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
using namespace EE::Math;
|
using namespace EE::Math;
|
||||||
@@ -212,7 +212,10 @@ class EE_API UIAbstractTableView : public UIAbstractView {
|
|||||||
bool manuallySet{ false };
|
bool manuallySet{ false };
|
||||||
UIPushButton* widget{ nullptr };
|
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 };
|
Float mRowHeight{ 0 };
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ class RichText;
|
|||||||
namespace EE { namespace UI {
|
namespace EE { namespace UI {
|
||||||
|
|
||||||
class UILayouter;
|
class UILayouter;
|
||||||
|
class UIHTMLWidget;
|
||||||
|
|
||||||
enum class CSSFormattingRole : Uint8 {
|
enum class CSSFormattingRole : Uint8 {
|
||||||
Inline,
|
Inline,
|
||||||
@@ -30,6 +31,16 @@ struct CSSUsedMargins {
|
|||||||
Uint8 autoSides{ 0 };
|
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 {
|
struct UIHTMLWidgetFlexState {
|
||||||
CSSFlexDirection direction{ CSSFlexDirection::Row };
|
CSSFlexDirection direction{ CSSFlexDirection::Row };
|
||||||
CSSFlexWrap wrap{ CSSFlexWrap::NoWrap };
|
CSSFlexWrap wrap{ CSSFlexWrap::NoWrap };
|
||||||
@@ -62,6 +73,22 @@ struct UIHTMLWidgetGridState {
|
|||||||
CSSJustifySelf justifySelf{ CSSJustifySelf::Auto };
|
CSSJustifySelf justifySelf{ CSSJustifySelf::Auto };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class HTMLPaintCategory : Uint8 {
|
||||||
|
NegativePositioned,
|
||||||
|
NormalFlow,
|
||||||
|
Float,
|
||||||
|
PositionedAutoOrZero,
|
||||||
|
PositivePositioned,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UIHTMLPaintOrderCache {
|
||||||
|
SmallVector<Node*, 16> items;
|
||||||
|
Uint32 rebuildCount{ 0 };
|
||||||
|
bool dirty{ true };
|
||||||
|
};
|
||||||
|
|
||||||
|
using UIHTMLPaintAncestorVector = SmallVector<UIHTMLWidget*, 8>;
|
||||||
|
|
||||||
class EE_API UIHTMLWidget : public UILayout {
|
class EE_API UIHTMLWidget : public UILayout {
|
||||||
public:
|
public:
|
||||||
static UIHTMLWidget* New();
|
static UIHTMLWidget* New();
|
||||||
@@ -131,10 +158,22 @@ class EE_API UIHTMLWidget : public UILayout {
|
|||||||
|
|
||||||
void setOffsets( const Rectf& offsets );
|
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 setZIndex( int zIndex );
|
||||||
|
|
||||||
|
void setZIndexAuto();
|
||||||
|
|
||||||
|
bool isCSSPositioned() const { return mPosition != CSSPosition::Static; }
|
||||||
|
|
||||||
|
bool hasApplicableZIndex() const;
|
||||||
|
|
||||||
|
bool createsSupportedStackingGroup() const;
|
||||||
|
|
||||||
bool getNeedsOrderSort() const { return mNeedsOrderSort; }
|
bool getNeedsOrderSort() const { return mNeedsOrderSort; }
|
||||||
|
|
||||||
void setNeedsOrderSort( bool val );
|
void setNeedsOrderSort( bool val );
|
||||||
@@ -308,10 +347,16 @@ class EE_API UIHTMLWidget : public UILayout {
|
|||||||
|
|
||||||
virtual void onChildCountChange( Node* child, const bool& removed );
|
virtual void onChildCountChange( Node* child, const bool& removed );
|
||||||
|
|
||||||
void updateZIndexSortFlag();
|
|
||||||
|
|
||||||
void buildDrawOrderVector( SmallVector<Node*, 127>& out ) const;
|
void buildDrawOrderVector( SmallVector<Node*, 127>& out ) const;
|
||||||
|
|
||||||
|
Uint32 getPaintOrderRebuildCount() const;
|
||||||
|
|
||||||
|
std::vector<Node*> debugGetHTMLPaintOrder() const;
|
||||||
|
|
||||||
|
std::vector<Node*> debugGetHTMLHitTestOrder() const;
|
||||||
|
|
||||||
|
bool isHTMLPaintPromoted() const { return mHTMLPaintOwner != nullptr; }
|
||||||
|
|
||||||
Float getBaseline() const;
|
Float getBaseline() const;
|
||||||
|
|
||||||
Float getContainingBlockContentWidth() const;
|
Float getContainingBlockContentWidth() const;
|
||||||
@@ -389,14 +434,20 @@ class EE_API UIHTMLWidget : public UILayout {
|
|||||||
std::string mBottomEq{ "auto" };
|
std::string mBottomEq{ "auto" };
|
||||||
std::string mLeftEq{ "auto" };
|
std::string mLeftEq{ "auto" };
|
||||||
Rectf mOffsets{ 0, 0, 0, 0 };
|
Rectf mOffsets{ 0, 0, 0, 0 };
|
||||||
int mZIndex{ 0 };
|
CSSZIndex mZIndex;
|
||||||
bool mOverflowCreatesBlockFormattingContext{ false };
|
bool mOverflowCreatesBlockFormattingContext{ false };
|
||||||
bool mNeedsOrderSort{ false };
|
bool mNeedsOrderSort{ false };
|
||||||
bool mNeedsZIndexSort{ false };
|
bool mNeedsPaintOrder{ false };
|
||||||
|
bool mHasStackingDescendant{ false };
|
||||||
|
mutable bool mHasPromotedChild{ false };
|
||||||
|
mutable bool mHasActivePromotions{ false };
|
||||||
Uint8 mPercentageMargins{ 0 };
|
Uint8 mPercentageMargins{ 0 };
|
||||||
UILayouter* mLayouter{ nullptr };
|
UILayouter* mLayouter{ nullptr };
|
||||||
UIHTMLWidgetFlexState* mFlexState{ nullptr };
|
UIHTMLWidgetFlexState* mFlexState{ nullptr };
|
||||||
UIHTMLWidgetGridState* mGridState{ nullptr };
|
UIHTMLWidgetGridState* mGridState{ nullptr };
|
||||||
|
mutable UIHTMLPaintOrderCache* mPaintOrderCache{ nullptr };
|
||||||
|
mutable const UIHTMLWidget* mHTMLPaintOwner{ nullptr };
|
||||||
|
mutable UIHTMLPaintAncestorVector* mHTMLPaintAncestors{ nullptr };
|
||||||
UnorderedMap<std::string, StyleSheetProperty> mDataProperties;
|
UnorderedMap<std::string, StyleSheetProperty> mDataProperties;
|
||||||
|
|
||||||
Uint32 mScrollCb{ 0 };
|
Uint32 mScrollCb{ 0 };
|
||||||
@@ -406,6 +457,22 @@ class EE_API UIHTMLWidget : public UILayout {
|
|||||||
|
|
||||||
void updateScrollListeners();
|
void updateScrollListeners();
|
||||||
void onScrollTargetPositionChange();
|
void onScrollTargetPositionChange();
|
||||||
|
void invalidatePaintOrder();
|
||||||
|
void updatePaintOrderFlag();
|
||||||
|
const SmallVector<Node*, 16>& getPaintOrder() const;
|
||||||
|
bool isHTMLStackingScope() const;
|
||||||
|
void buildCSSChildOrder( SmallVector<Node*, 16>& out ) const;
|
||||||
|
void resetPromotedPaintState() const;
|
||||||
|
void collectStackingScopeItems( UIHTMLWidget* container, SmallVector<Node*, 16>& out,
|
||||||
|
bool directChildren ) const;
|
||||||
|
void collectStackingScopeChild( Node* child, SmallVector<Node*, 16>& 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
|
}} // namespace EE::UI
|
||||||
|
|||||||
@@ -1483,9 +1483,4 @@ UITableCell* UIAbstractTableView::getCellFromIndex( const ModelIndex& index ) co
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIAbstractTableView::ColumnData::setWidth( Float w, bool manSet ) {
|
|
||||||
width = w;
|
|
||||||
manuallySet = manSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
}}} // namespace EE::UI::Abstract
|
}}} // namespace EE::UI::Abstract
|
||||||
|
|||||||
+332
-57
@@ -3,6 +3,7 @@
|
|||||||
#include <eepp/ui/css/stylesheetlength.hpp>
|
#include <eepp/ui/css/stylesheetlength.hpp>
|
||||||
#include <eepp/ui/flexlayouter.hpp>
|
#include <eepp/ui/flexlayouter.hpp>
|
||||||
#include <eepp/ui/gridlayouter.hpp>
|
#include <eepp/ui/gridlayouter.hpp>
|
||||||
|
#include <eepp/ui/uiborderdrawable.hpp>
|
||||||
#include <eepp/ui/uihtmlwidget.hpp>
|
#include <eepp/ui/uihtmlwidget.hpp>
|
||||||
#include <eepp/ui/uilayouter.hpp>
|
#include <eepp/ui/uilayouter.hpp>
|
||||||
#include <eepp/ui/uilayoutermanager.hpp>
|
#include <eepp/ui/uilayoutermanager.hpp>
|
||||||
@@ -162,6 +163,8 @@ UIHTMLWidget::~UIHTMLWidget() {
|
|||||||
eeSAFE_DELETE( mLayouter );
|
eeSAFE_DELETE( mLayouter );
|
||||||
eeSAFE_DELETE( mFlexState );
|
eeSAFE_DELETE( mFlexState );
|
||||||
eeSAFE_DELETE( mGridState );
|
eeSAFE_DELETE( mGridState );
|
||||||
|
eeSAFE_DELETE( mPaintOrderCache );
|
||||||
|
eeSAFE_DELETE( mHTMLPaintAncestors );
|
||||||
}
|
}
|
||||||
|
|
||||||
UILayouter* UIHTMLWidget::getLayouter() {
|
UILayouter* UIHTMLWidget::getLayouter() {
|
||||||
@@ -223,6 +226,7 @@ void UIHTMLWidget::setDisplay( CSSDisplay display ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onDisplayChange();
|
onDisplayChange();
|
||||||
|
updatePaintOrderFlag();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -351,6 +355,8 @@ void UIHTMLWidget::setVisibility( CSSVisibility val ) {
|
|||||||
void UIHTMLWidget::setCSSPosition( CSSPosition position ) {
|
void UIHTMLWidget::setCSSPosition( CSSPosition position ) {
|
||||||
if ( mPosition != position ) {
|
if ( mPosition != position ) {
|
||||||
mPosition = position;
|
mPosition = position;
|
||||||
|
if ( Node* parent = getParent(); parent && parent->isType( UI_TYPE_HTML_WIDGET ) )
|
||||||
|
parent->asType<UIHTMLWidget>()->updatePaintOrderFlag();
|
||||||
if ( position == CSSPosition::Absolute || position == CSSPosition::Fixed ) {
|
if ( position == CSSPosition::Absolute || position == CSSPosition::Fixed ) {
|
||||||
// Out-of-flow elements should not stretch to their containing block
|
// Out-of-flow elements should not stretch to their containing block
|
||||||
// until updateOutOfFlowPosition() computes the correct size from CSS
|
// until updateOutOfFlowPosition() computes the correct size from CSS
|
||||||
@@ -369,6 +375,8 @@ void UIHTMLWidget::setCSSPosition( CSSPosition position ) {
|
|||||||
void UIHTMLWidget::setCSSFloat( CSSFloat cssFloat ) {
|
void UIHTMLWidget::setCSSFloat( CSSFloat cssFloat ) {
|
||||||
if ( mFloat != cssFloat ) {
|
if ( mFloat != cssFloat ) {
|
||||||
mFloat = cssFloat;
|
mFloat = cssFloat;
|
||||||
|
if ( Node* parent = getParent(); parent && parent->isType( UI_TYPE_HTML_WIDGET ) )
|
||||||
|
parent->asType<UIHTMLWidget>()->updatePaintOrderFlag();
|
||||||
// A width:auto block normally fills its containing block, while a float uses the CSS
|
// 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
|
// 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.
|
// 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 ) {
|
void UIHTMLWidget::setZIndex( int zIndex ) {
|
||||||
mZIndex = zIndex;
|
const CSSZIndex value{ zIndex, false };
|
||||||
|
if ( mZIndex == value )
|
||||||
|
return;
|
||||||
|
mZIndex = value;
|
||||||
Node* p = getParent();
|
Node* p = getParent();
|
||||||
if ( p && p->isType( UI_TYPE_HTML_WIDGET ) )
|
if ( p && p->isType( UI_TYPE_HTML_WIDGET ) )
|
||||||
p->asType<UIHTMLWidget>()->updateZIndexSortFlag();
|
p->asType<UIHTMLWidget>()->updatePaintOrderFlag();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UIHTMLWidget::setZIndexAuto() {
|
||||||
|
if ( mZIndex.isAuto )
|
||||||
|
return;
|
||||||
|
mZIndex = {};
|
||||||
|
Node* p = getParent();
|
||||||
|
if ( p && p->isType( UI_TYPE_HTML_WIDGET ) )
|
||||||
|
p->asType<UIHTMLWidget>()->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<UIHTMLWidget>()->isFlex() || parent->asType<UIHTMLWidget>()->isGrid() );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UIHTMLWidget::createsSupportedStackingGroup() const {
|
||||||
|
return mPosition == CSSPosition::Fixed || mPosition == CSSPosition::Sticky ||
|
||||||
|
hasApplicableZIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIHTMLWidget::setNeedsOrderSort( bool val ) {
|
void UIHTMLWidget::setNeedsOrderSort( bool val ) {
|
||||||
|
if ( mNeedsOrderSort != val ) {
|
||||||
mNeedsOrderSort = val;
|
mNeedsOrderSort = val;
|
||||||
|
invalidatePaintOrder();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIHTMLWidget::updateZIndexSortFlag() {
|
void UIHTMLWidget::invalidatePaintOrder() {
|
||||||
bool needs = false;
|
for ( Node* node = this; node; node = node->getParent() ) {
|
||||||
|
if ( node->isType( UI_TYPE_HTML_WIDGET ) ) {
|
||||||
|
auto* widget = node->asType<UIHTMLWidget>();
|
||||||
|
if ( widget->mPaintOrderCache )
|
||||||
|
widget->mPaintOrderCache->dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UIHTMLWidget::updatePaintOrderFlag() {
|
||||||
|
bool needsPaintOrder = false;
|
||||||
|
bool hasStackingDescendant = false;
|
||||||
for ( Node* child = getFirstChild(); child; child = child->getNextNode() ) {
|
for ( Node* child = getFirstChild(); child; child = child->getNextNode() ) {
|
||||||
if ( child->isType( UI_TYPE_HTML_WIDGET ) &&
|
if ( !child->isType( UI_TYPE_HTML_WIDGET ) )
|
||||||
child->asType<UIHTMLWidget>()->getZIndex() != 0 ) {
|
continue;
|
||||||
needs = true;
|
auto* htmlChild = child->asType<UIHTMLWidget>();
|
||||||
break;
|
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<UIHTMLWidget>()->updatePaintOrderFlag();
|
||||||
}
|
}
|
||||||
mNeedsZIndexSort = needs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIHTMLWidget::onChildCountChange( Node* child, const bool& removed ) {
|
void UIHTMLWidget::onChildCountChange( Node* child, const bool& removed ) {
|
||||||
UILayout::onChildCountChange( child, removed );
|
UILayout::onChildCountChange( child, removed );
|
||||||
|
|
||||||
if ( !removed )
|
updatePaintOrderFlag();
|
||||||
updateZIndexSortFlag();
|
|
||||||
else if ( child->isType( UI_TYPE_HTML_WIDGET ) &&
|
|
||||||
child->asType<UIHTMLWidget>()->getZIndex() != 0 )
|
|
||||||
updateZIndexSortFlag();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIHTMLWidget::buildDrawOrderVector( SmallVector<Node*, 127>& out ) const {
|
void UIHTMLWidget::buildDrawOrderVector( SmallVector<Node*, 127>& out ) const {
|
||||||
bool flexSort = false;
|
const auto& paintOrder = getPaintOrder();
|
||||||
bool directionReverse = false;
|
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<UIHTMLWidget>();
|
||||||
|
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<UIHTMLWidget>()->isFlex() || parent->asType<UIHTMLWidget>()->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<Node*, 16>& out ) const {
|
||||||
for ( Node* child = getFirstChild(); child; child = child->getNextNode() )
|
for ( Node* child = getFirstChild(); child; child = child->getNextNode() )
|
||||||
out.push_back( child );
|
out.push_back( child );
|
||||||
|
if ( !isFlex() && !isGrid() )
|
||||||
if ( flexSort && mNeedsOrderSort ) {
|
return;
|
||||||
|
if ( mNeedsOrderSort ) {
|
||||||
std::stable_sort( out.begin(), out.end(), []( Node* a, Node* b ) {
|
std::stable_sort( out.begin(), out.end(), []( Node* a, Node* b ) {
|
||||||
int aOrder = ( a->isWidget() && a->isType( UI_TYPE_HTML_WIDGET ) )
|
const int aOrder =
|
||||||
? a->asType<UIHTMLWidget>()->getOrder()
|
a->isType( UI_TYPE_HTML_WIDGET ) ? a->asType<UIHTMLWidget>()->getOrder() : 0;
|
||||||
: 0;
|
const int bOrder =
|
||||||
int bOrder = ( b->isWidget() && b->isType( UI_TYPE_HTML_WIDGET ) )
|
b->isType( UI_TYPE_HTML_WIDGET ) ? b->asType<UIHTMLWidget>()->getOrder() : 0;
|
||||||
? b->asType<UIHTMLWidget>()->getOrder()
|
|
||||||
: 0;
|
|
||||||
return aOrder < bOrder;
|
return aOrder < bOrder;
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( flexSort && directionReverse )
|
void UIHTMLWidget::resetPromotedPaintState() const {
|
||||||
std::reverse( out.begin(), out.end() );
|
for ( Node* child = getFirstChild(); child; child = child->getNextNode() ) {
|
||||||
|
if ( !child->isType( UI_TYPE_HTML_WIDGET ) )
|
||||||
|
continue;
|
||||||
|
auto* widget = child->asType<UIHTMLWidget>();
|
||||||
|
widget->mHTMLPaintOwner = nullptr;
|
||||||
|
if ( widget->mHTMLPaintAncestors )
|
||||||
|
widget->mHTMLPaintAncestors->clear();
|
||||||
|
widget->mHasPromotedChild = false;
|
||||||
|
if ( widget->mPaintOrderCache )
|
||||||
|
widget->mPaintOrderCache->dirty = true;
|
||||||
|
widget->resetPromotedPaintState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( mNeedsZIndexSort ) {
|
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<UIHTMLWidget>() );
|
||||||
|
}
|
||||||
|
if ( widget->getParent()->isType( UI_TYPE_HTML_WIDGET ) )
|
||||||
|
widget->getParent()->asType<UIHTMLWidget>()->mHasPromotedChild = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UIHTMLWidget::collectStackingScopeItems( UIHTMLWidget* container, SmallVector<Node*, 16>& out,
|
||||||
|
bool directChildren ) const {
|
||||||
|
if ( container->isFlex() || container->isGrid() ) {
|
||||||
|
SmallVector<Node*, 16> 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<Node*, 16>& out,
|
||||||
|
bool directChildren ) const {
|
||||||
|
if ( directChildren )
|
||||||
|
out.push_back( child );
|
||||||
|
if ( !child->isType( UI_TYPE_HTML_WIDGET ) )
|
||||||
|
return;
|
||||||
|
auto* widget = child->asType<UIHTMLWidget>();
|
||||||
|
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<Node*, 16>& 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<UIHTMLWidget*>( this ), out, true );
|
||||||
|
} else {
|
||||||
|
buildCSSChildOrder( out );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( mNeedsPaintOrder || collectStacking ) {
|
||||||
std::stable_sort( out.begin(), out.end(), []( Node* a, Node* b ) {
|
std::stable_sort( out.begin(), out.end(), []( Node* a, Node* b ) {
|
||||||
int aZ =
|
const auto aCategory = getPaintCategory( a );
|
||||||
( a->isType( UI_TYPE_HTML_WIDGET ) ) ? a->asType<UIHTMLWidget>()->getZIndex() : 0;
|
const auto bCategory = getPaintCategory( b );
|
||||||
int bZ =
|
if ( aCategory != bCategory )
|
||||||
( b->isType( UI_TYPE_HTML_WIDGET ) ) ? b->asType<UIHTMLWidget>()->getZIndex() : 0;
|
return aCategory < bCategory;
|
||||||
return aZ < bZ;
|
if ( aCategory == HTMLPaintCategory::NegativePositioned ||
|
||||||
|
aCategory == HTMLPaintCategory::PositivePositioned )
|
||||||
|
return a->asType<UIHTMLWidget>()->getZIndex() <
|
||||||
|
b->asType<UIHTMLWidget>()->getZIndex();
|
||||||
|
return false;
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mPaintOrderCache->dirty = false;
|
||||||
|
++mPaintOrderCache->rebuildCount;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint32 UIHTMLWidget::getPaintOrderRebuildCount() const {
|
||||||
|
return mPaintOrderCache ? mPaintOrderCache->rebuildCount : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Node*> UIHTMLWidget::debugGetHTMLPaintOrder() const {
|
||||||
|
const auto& order = getPaintOrder();
|
||||||
|
return { order.begin(), order.end() };
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Node*> 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<UIHTMLWidget>()->mHTMLPaintOwner != this ) {
|
||||||
|
node->nodeDraw();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* promoted = node->asType<UIHTMLWidget>();
|
||||||
|
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<UIHTMLWidget>();
|
||||||
|
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<UIHTMLWidget>()->mHTMLPaintOwner != nullptr &&
|
||||||
|
node->asType<UIHTMLWidget>()->mHTMLPaintOwner != this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIHTMLWidget::drawChildren() {
|
void UIHTMLWidget::drawChildren() {
|
||||||
bool needsSort = mNeedsOrderSort || mNeedsZIndexSort;
|
if ( !needsHTMLPaintTraversal() ) {
|
||||||
|
|
||||||
if ( isFlex() ) {
|
|
||||||
CSSFlexDirection dir = getFlexDirection();
|
|
||||||
if ( dir == CSSFlexDirection::RowReverse || dir == CSSFlexDirection::ColumnReverse )
|
|
||||||
needsSort = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !needsSort ) {
|
|
||||||
UILayout::drawChildren();
|
UILayout::drawChildren();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SmallVector<Node*, 127> sortedChildren;
|
for ( auto* child : getPaintOrder() ) {
|
||||||
buildDrawOrderVector( sortedChildren );
|
if ( shouldSkipHTMLPaintNode( child ) )
|
||||||
|
continue;
|
||||||
for ( auto* child : sortedChildren ) {
|
|
||||||
if ( child->isVisible() )
|
if ( child->isVisible() )
|
||||||
child->nodeDraw();
|
drawHTMLPaintNode( child );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Node* UIHTMLWidget::overFind( const Vector2f& point ) {
|
Node* UIHTMLWidget::overFind( const Vector2f& point ) {
|
||||||
if ( !mNeedsOrderSort && !mNeedsZIndexSort )
|
if ( !needsHTMLPaintTraversal() )
|
||||||
return UILayout::overFind( point );
|
return UILayout::overFind( point );
|
||||||
|
|
||||||
Node* pOver = nullptr;
|
Node* pOver = nullptr;
|
||||||
@@ -602,11 +861,14 @@ Node* UIHTMLWidget::overFind( const Vector2f& point ) {
|
|||||||
writeNodeFlag( NODE_FLAG_MOUSEOVER_ME_OR_CHILD, 1 );
|
writeNodeFlag( NODE_FLAG_MOUSEOVER_ME_OR_CHILD, 1 );
|
||||||
mSceneNode->addMouseOverNode( this );
|
mSceneNode->addMouseOverNode( this );
|
||||||
|
|
||||||
SmallVector<Node*, 127> sortedChildren;
|
const auto& sortedChildren = getPaintOrder();
|
||||||
buildDrawOrderVector( sortedChildren );
|
|
||||||
|
|
||||||
// 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 ) {
|
for ( auto it = sortedChildren.rbegin(); it != sortedChildren.rend(); ++it ) {
|
||||||
|
if ( shouldSkipHTMLPaintNode( *it ) )
|
||||||
|
continue;
|
||||||
|
if ( !canHitHTMLPaintNode( *it, point ) )
|
||||||
|
continue;
|
||||||
Node* childOver = ( *it )->overFind( point );
|
Node* childOver = ( *it )->overFind( point );
|
||||||
if ( childOver ) {
|
if ( childOver ) {
|
||||||
pOver = childOver;
|
pOver = childOver;
|
||||||
@@ -698,6 +960,8 @@ void UIHTMLWidget::setOrder( int val ) {
|
|||||||
auto* fs = ensureFlexState();
|
auto* fs = ensureFlexState();
|
||||||
if ( fs->order != val ) {
|
if ( fs->order != val ) {
|
||||||
fs->order = val;
|
fs->order = val;
|
||||||
|
if ( Node* parent = getParent(); parent && parent->isType( UI_TYPE_HTML_WIDGET ) )
|
||||||
|
parent->asType<UIHTMLWidget>()->invalidatePaintOrder();
|
||||||
notifyLayoutAttrChange( LayoutInvalidation::ContainerLayout );
|
notifyLayoutAttrChange( LayoutInvalidation::ContainerLayout );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -899,7 +1163,7 @@ std::string UIHTMLWidget::getPropertyString( const PropertyDefinition* propertyD
|
|||||||
case PropertyId::Left:
|
case PropertyId::Left:
|
||||||
return mLeftEq;
|
return mLeftEq;
|
||||||
case PropertyId::ZIndex:
|
case PropertyId::ZIndex:
|
||||||
return String::toString( mZIndex );
|
return mZIndex.isAuto ? "auto" : String::toString( mZIndex.value );
|
||||||
case PropertyId::AlignmentBaseline:
|
case PropertyId::AlignmentBaseline:
|
||||||
return std::string( CSSBaselineAlignmentHelper::toString( mBaselineAlign ) );
|
return std::string( CSSBaselineAlignmentHelper::toString( mBaselineAlign ) );
|
||||||
case PropertyId::FlexDirection:
|
case PropertyId::FlexDirection:
|
||||||
@@ -1043,6 +1307,9 @@ bool UIHTMLWidget::applyProperty( const StyleSheetProperty& attribute ) {
|
|||||||
return applied;
|
return applied;
|
||||||
}
|
}
|
||||||
case PropertyId::ZIndex: {
|
case PropertyId::ZIndex: {
|
||||||
|
if ( String::trim( attribute.asString() ) == "auto" )
|
||||||
|
setZIndexAuto();
|
||||||
|
else
|
||||||
setZIndex( attribute.asInt() );
|
setZIndex( attribute.asInt() );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1523,6 +1790,14 @@ void UIHTMLWidget::updateScrollListeners() {
|
|||||||
|
|
||||||
void UIHTMLWidget::onParentChange() {
|
void UIHTMLWidget::onParentChange() {
|
||||||
UILayout::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();
|
updateScrollListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2258,7 +2258,7 @@ UTEST( FlexContainer, orderPaintSortNegatives ) {
|
|||||||
Engine::destroySingleton();
|
Engine::destroySingleton();
|
||||||
}
|
}
|
||||||
|
|
||||||
UTEST( FlexContainer, directionReversePaintColumnReverse ) {
|
UTEST( FlexContainer, directionReverseLayoutKeepsColumnPaintOrder ) {
|
||||||
Engine::instance()->createWindow( WindowSettings( 1024, 650, "Flex Test", WindowStyle::Default,
|
Engine::instance()->createWindow( WindowSettings( 1024, 650, "Flex Test", WindowStyle::Default,
|
||||||
WindowBackend::Default, 32, {}, 1, false,
|
WindowBackend::Default, 32, {}, 1, false,
|
||||||
true ),
|
true ),
|
||||||
@@ -2294,13 +2294,20 @@ UTEST( FlexContainer, directionReversePaintColumnReverse ) {
|
|||||||
EXPECT_GT( child1->getPixelsPosition().y, child2->getPixelsPosition().y );
|
EXPECT_GT( child1->getPixelsPosition().y, child2->getPixelsPosition().y );
|
||||||
EXPECT_GT( child2->getPixelsPosition().y, child3->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<Node*, 127> 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() );
|
EXPECT_FALSE( flex->getNeedsOrderSort() );
|
||||||
|
|
||||||
Engine::destroySingleton();
|
Engine::destroySingleton();
|
||||||
}
|
}
|
||||||
|
|
||||||
UTEST( FlexContainer, directionReversePaintRowReverse ) {
|
UTEST( FlexContainer, directionReverseLayoutKeepsRowPaintOrder ) {
|
||||||
Engine::instance()->createWindow( WindowSettings( 1024, 650, "Flex Test", WindowStyle::Default,
|
Engine::instance()->createWindow( WindowSettings( 1024, 650, "Flex Test", WindowStyle::Default,
|
||||||
WindowBackend::Default, 32, {}, 1, false,
|
WindowBackend::Default, 32, {}, 1, false,
|
||||||
true ),
|
true ),
|
||||||
@@ -2336,13 +2343,20 @@ UTEST( FlexContainer, directionReversePaintRowReverse ) {
|
|||||||
EXPECT_GT( child1->getPixelsPosition().x, child2->getPixelsPosition().x );
|
EXPECT_GT( child1->getPixelsPosition().x, child2->getPixelsPosition().x );
|
||||||
EXPECT_GT( child2->getPixelsPosition().x, child3->getPixelsPosition().x );
|
EXPECT_GT( child2->getPixelsPosition().x, child3->getPixelsPosition().x );
|
||||||
|
|
||||||
// All orders equal so no order-based sort needed
|
SmallVector<Node*, 127> 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() );
|
EXPECT_FALSE( flex->getNeedsOrderSort() );
|
||||||
|
|
||||||
Engine::destroySingleton();
|
Engine::destroySingleton();
|
||||||
}
|
}
|
||||||
|
|
||||||
UTEST( FlexContainer, directionReversePaintWithOrderSort ) {
|
UTEST( FlexContainer, directionReverseLayoutUsesOrderModifiedPaintOrder ) {
|
||||||
Engine::instance()->createWindow( WindowSettings( 1024, 650, "Flex Test", WindowStyle::Default,
|
Engine::instance()->createWindow( WindowSettings( 1024, 650, "Flex Test", WindowStyle::Default,
|
||||||
WindowBackend::Default, 32, {}, 1, false,
|
WindowBackend::Default, 32, {}, 1, false,
|
||||||
true ),
|
true ),
|
||||||
@@ -2388,6 +2402,38 @@ UTEST( FlexContainer, directionReversePaintWithOrderSort ) {
|
|||||||
EXPECT_LT( child3->getPixelsPosition().y, child2->getPixelsPosition().y );
|
EXPECT_LT( child3->getPixelsPosition().y, child2->getPixelsPosition().y );
|
||||||
|
|
||||||
EXPECT_TRUE( flex->getNeedsOrderSort() );
|
EXPECT_TRUE( flex->getNeedsOrderSort() );
|
||||||
|
SmallVector<Node*, 127> 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<Node*, 127> paintOrder;
|
||||||
|
flex->buildDrawOrderVector( paintOrder );
|
||||||
|
ASSERT_EQ( paintOrder.size(), 2u );
|
||||||
|
EXPECT_EQ( paintOrder[0], floated );
|
||||||
|
EXPECT_EQ( paintOrder[1], normal );
|
||||||
|
|
||||||
Engine::destroySingleton();
|
Engine::destroySingleton();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,6 +127,271 @@ UTEST( UIHTMLFloat, property_GetPropertyString ) {
|
|||||||
eeDelete( w );
|
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<Node*, 127> 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 ) {
|
UTEST( UIHTMLFloat, richtext_NoFloatLayout_NoChange ) {
|
||||||
init_float_test();
|
init_float_test();
|
||||||
UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode();
|
UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode();
|
||||||
|
|||||||
Reference in New Issue
Block a user