diff --git a/bin/unit_tests/assets/html/absolute_position.html b/bin/unit_tests/assets/html/absolute_position.html
new file mode 100644
index 000000000..07a71e8ac
--- /dev/null
+++ b/bin/unit_tests/assets/html/absolute_position.html
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/include/eepp/ui/uihtmltable.hpp b/include/eepp/ui/uihtmltable.hpp
index 3cfe4b669..8d2e094d0 100644
--- a/include/eepp/ui/uihtmltable.hpp
+++ b/include/eepp/ui/uihtmltable.hpp
@@ -18,8 +18,6 @@ class EE_API UIHTMLTable : public UIHTMLWidget {
virtual bool isType( const Uint32& type ) const;
- virtual void updateLayout();
-
virtual Float getMinIntrinsicWidth() const;
virtual Float getMaxIntrinsicWidth() const;
diff --git a/include/eepp/ui/uihtmlwidget.hpp b/include/eepp/ui/uihtmlwidget.hpp
index 1b6d077e1..2a750ee4a 100644
--- a/include/eepp/ui/uihtmlwidget.hpp
+++ b/include/eepp/ui/uihtmlwidget.hpp
@@ -61,6 +61,10 @@ class EE_API UIHTMLWidget : public UILayout {
protected:
CSSDisplay mDisplay{ CSSDisplay::Block };
CSSPosition mPosition{ CSSPosition::Static };
+ std::string mTopEq{ "auto" };
+ std::string mRightEq{ "auto" };
+ std::string mBottomEq{ "auto" };
+ std::string mLeftEq{ "auto" };
Rectf mOffsets{ 0, 0, 0, 0 };
int mZIndex{ 0 };
UILayouter* mLayouter{ nullptr };
diff --git a/include/eepp/ui/uilayout.hpp b/include/eepp/ui/uilayout.hpp
index ad1c7714c..0217a8476 100644
--- a/include/eepp/ui/uilayout.hpp
+++ b/include/eepp/ui/uilayout.hpp
@@ -24,6 +24,9 @@ class EE_API UILayout : public UIWidget {
bool isLayoutDirty() const { return mDirtyLayout; }
void onAutoSizeChild( UIWidget* child );
+
+ void setLayoutDirty();
+
protected:
friend class UISceneNode;
friend class UILayouter;
@@ -51,8 +54,6 @@ class EE_API UILayout : public UIWidget {
virtual void updateLayoutWrappingContents();
- void setLayoutDirty();
-
bool setMatchParentIfNeededVerticalGrowth();
};
diff --git a/include/eepp/ui/uirichtext.hpp b/include/eepp/ui/uirichtext.hpp
index f87b92f1a..6941c7bab 100644
--- a/include/eepp/ui/uirichtext.hpp
+++ b/include/eepp/ui/uirichtext.hpp
@@ -129,8 +129,6 @@ class EE_API UIRichText : public UIHTMLWidget {
String getSelectionString() const;
- virtual void updateLayout();
-
virtual RichText* getRichTextPtr() { return &mRichText; }
protected:
diff --git a/src/eepp/ui/css/stylesheetspecification.cpp b/src/eepp/ui/css/stylesheetspecification.cpp
index 5cf4ee98d..837f25f02 100644
--- a/src/eepp/ui/css/stylesheetspecification.cpp
+++ b/src/eepp/ui/css/stylesheetspecification.cpp
@@ -431,10 +431,18 @@ void StyleSheetSpecification::registerDefaultProperties() {
registerProperty( "list-style-type", "none", true ).setType( PropertyType::String );
registerProperty( "list-style-position", "outside", true ).setType( PropertyType::String );
registerProperty( "list-style-image", "none" ).setType( PropertyType::String );
- registerProperty( "top", "auto" ).setType( PropertyType::NumberLength );
- registerProperty( "right", "auto" ).setType( PropertyType::NumberLength );
- registerProperty( "bottom", "auto" ).setType( PropertyType::NumberLength );
- registerProperty( "left", "auto" ).setType( PropertyType::NumberLength );
+ registerProperty( "top", "auto" )
+ .setType( PropertyType::NumberLength )
+ .setRelativeTarget( PropertyRelativeTarget::ContainingBlockHeight );
+ registerProperty( "right", "auto" )
+ .setType( PropertyType::NumberLength )
+ .setRelativeTarget( PropertyRelativeTarget::ContainingBlockWidth );
+ registerProperty( "bottom", "auto" )
+ .setType( PropertyType::NumberLength )
+ .setRelativeTarget( PropertyRelativeTarget::ContainingBlockHeight );
+ registerProperty( "left", "auto" )
+ .setType( PropertyType::NumberLength )
+ .setRelativeTarget( PropertyRelativeTarget::ContainingBlockWidth );
registerProperty( "z-index", "auto" ).setType( PropertyType::NumberInt );
registerProperty( "inner-widget-orientation", "widgeticontextbox" )
diff --git a/src/eepp/ui/uihtmltable.cpp b/src/eepp/ui/uihtmltable.cpp
index 123b18a6e..f71b2c8f3 100644
--- a/src/eepp/ui/uihtmltable.cpp
+++ b/src/eepp/ui/uihtmltable.cpp
@@ -85,16 +85,6 @@ Float UIHTMLTable::getMaxIntrinsicWidth() const {
return 0;
}
-void UIHTMLTable::updateLayout() {
- UILayouter* layouter = const_cast( this )->getLayouter();
- if ( layouter )
- getLayouter()->updateLayout();
- else
- UIHTMLWidget::updateLayout();
-
- mDirtyLayout = false;
-}
-
Uint32 UIHTMLTable::onMessage( const NodeMessage* Msg ) {
switch ( Msg->getMsg() ) {
case NodeMessage::LayoutAttributeChange: {
diff --git a/src/eepp/ui/uihtmlwidget.cpp b/src/eepp/ui/uihtmlwidget.cpp
index 6e96404df..58157b6c2 100644
--- a/src/eepp/ui/uihtmlwidget.cpp
+++ b/src/eepp/ui/uihtmlwidget.cpp
@@ -60,6 +60,10 @@ void UIHTMLWidget::setCSSPosition( CSSPosition position ) {
void UIHTMLWidget::setOffsets( const Rectf& offsets ) {
if ( mOffsets != offsets ) {
mOffsets = offsets;
+ mTopEq = String::fromFloat( offsets.Top, "dp" );
+ mLeftEq = String::fromFloat( offsets.Left, "dp" );
+ mRightEq = String::fromFloat( offsets.Right, "dp" );
+ mBottomEq = String::fromFloat( offsets.Bottom, "dp" );
notifyLayoutAttrChange();
}
}
@@ -79,13 +83,13 @@ std::string UIHTMLWidget::getPropertyString( const PropertyDefinition* propertyD
case PropertyId::Position:
return CSSPositionHelper::toString( mPosition );
case PropertyId::Top:
- return String::fromFloat( mOffsets.Top, "dp" );
+ return mTopEq;
case PropertyId::Right:
- return String::fromFloat( mOffsets.Right, "dp" );
+ return mRightEq;
case PropertyId::Bottom:
- return String::fromFloat( mOffsets.Bottom, "dp" );
+ return mBottomEq;
case PropertyId::Left:
- return String::fromFloat( mOffsets.Left, "dp" );
+ return mLeftEq;
case PropertyId::ZIndex:
return String::toString( mZIndex );
default:
@@ -111,34 +115,22 @@ bool UIHTMLWidget::applyProperty( const StyleSheetProperty& attribute ) {
return true;
}
case PropertyId::Top: {
- if ( attribute.asString() == "auto" )
- mOffsets.Top = 0;
- else
- mOffsets.Top = lengthFromValueAsDp( attribute );
+ mTopEq = attribute.asString();
notifyLayoutAttrChange();
return true;
}
case PropertyId::Right: {
- if ( attribute.asString() == "auto" )
- mOffsets.Right = 0;
- else
- mOffsets.Right = lengthFromValueAsDp( attribute );
+ mRightEq = attribute.asString();
notifyLayoutAttrChange();
return true;
}
case PropertyId::Bottom: {
- if ( attribute.asString() == "auto" )
- mOffsets.Bottom = 0;
- else
- mOffsets.Bottom = lengthFromValueAsDp( attribute );
+ mBottomEq = attribute.asString();
notifyLayoutAttrChange();
return true;
}
case PropertyId::Left: {
- if ( attribute.asString() == "auto" )
- mOffsets.Left = 0;
- else
- mOffsets.Left = lengthFromValueAsDp( attribute );
+ mLeftEq = attribute.asString();
notifyLayoutAttrChange();
return true;
}
@@ -148,7 +140,6 @@ bool UIHTMLWidget::applyProperty( const StyleSheetProperty& attribute ) {
return UILayout::applyProperty( attribute );
}
-
void UIHTMLWidget::updateLayout() {
if ( getLayouter() )
getLayouter()->updateLayout();
@@ -156,6 +147,8 @@ void UIHTMLWidget::updateLayout() {
UILayout::updateLayout();
positionOutOfFlowChildren();
+
+ mDirtyLayout = false;
}
UIWidget* UIHTMLWidget::getContainingBlock() {
@@ -195,11 +188,22 @@ void UIHTMLWidget::positionOutOfFlowChildren() {
if ( pos == CSSPosition::Absolute || pos == CSSPosition::Fixed ) {
UIWidget* cb = htmlChild->getContainingBlock();
if ( cb ) {
- Rectf offsets = htmlChild->getOffsets();
- Float top = PixelDensity::dpToPx( offsets.Top );
- Float left = PixelDensity::dpToPx( offsets.Left );
+ Float top = htmlChild->mTopEq == "auto"
+ ? 0
+ : htmlChild->lengthFromValue(
+ htmlChild->mTopEq,
+ CSS::PropertyRelativeTarget::ContainingBlockHeight, 0 );
+ Float left = htmlChild->mLeftEq == "auto"
+ ? 0
+ : htmlChild->lengthFromValue(
+ htmlChild->mLeftEq,
+ CSS::PropertyRelativeTarget::ContainingBlockWidth, 0 );
- Vector2f cbPos( cb->getPixelsContentOffset().Left, cb->getPixelsContentOffset().Top );
+ top += htmlChild->getLayoutPixelsMargin().Top;
+ left += htmlChild->getLayoutPixelsMargin().Left;
+
+ Vector2f cbPos( cb->getPixelsContentOffset().Left,
+ cb->getPixelsContentOffset().Top );
cbPos.x += left;
cbPos.y += top;
diff --git a/src/eepp/ui/uirichtext.cpp b/src/eepp/ui/uirichtext.cpp
index a7f471589..14e9edcd6 100644
--- a/src/eepp/ui/uirichtext.cpp
+++ b/src/eepp/ui/uirichtext.cpp
@@ -10,7 +10,6 @@
#include
#include
#include
-#include
#define PUGIXML_HEADER_ONLY
#include
@@ -94,11 +93,15 @@ bool UIHTMLBody::applyProperty( const StyleSheetProperty& attribute ) {
}
UIRichText* UIRichText::NewHtml() {
- return UIHTMLHtml::New( "html" );
+ auto* html = UIHTMLHtml::New( "html" );
+ html->setClipType( ClipType::None );
+ return html;
}
UIRichText* UIRichText::NewBody() {
- return UIHTMLBody::New( "body" );
+ auto* body = UIHTMLBody::New( "body" );
+ body->setClipType( ClipType::None );
+ return body;
}
UIRichText* UIRichText::NewBr() {
@@ -261,7 +264,7 @@ bool UIRichText::applyProperty( const StyleSheetProperty& attribute ) {
break;
}
default:
- return UILayout::applyProperty( attribute );
+ return UIHTMLWidget::applyProperty( attribute );
}
return true;
@@ -305,7 +308,7 @@ std::string UIRichText::getPropertyString( const PropertyDefinition* propertyDef
? "center"
: ( getTextAlign() == TEXT_ALIGN_RIGHT ? "right" : "left" );
default:
- return UILayout::getPropertyString( propertyDef, propertyIndex );
+ return UIHTMLWidget::getPropertyString( propertyDef, propertyIndex );
}
}
@@ -701,16 +704,6 @@ void UIRichText::updateDefaultSpansStyle() {
}
}
-void UIRichText::updateLayout() {
- if ( getLayouter() ) {
- getLayouter()->updateLayout();
- } else {
- UILayout::updateLayout();
- }
-
- mDirtyLayout = false;
-}
-
Float UIRichText::getMinIntrinsicWidth() const {
if ( mWidthPolicy == SizePolicy::Fixed ) {
return getPropertyWidth();
diff --git a/src/examples/ui_html/ui_html.cpp b/src/examples/ui_html/ui_html.cpp
index ac86ca78b..4d1046afd 100644
--- a/src/examples/ui_html/ui_html.cpp
+++ b/src/examples/ui_html/ui_html.cpp
@@ -82,6 +82,7 @@ EE_MAIN_FUNC int main( int argc, char** argv ) {
auto urlBar = ui->find( "url_bar" )->asType();
auto mainContainer = ui->find( "html_doc" );
+ mainContainer->asType()->setClipType( ClipType::None );
auto backBtn = ui->find( "backbtn" )->asType();
auto fwdBtn = ui->find( "fwdbtn" )->asType();
auto scrollView = ui->find( "html_view" )->asType();
@@ -98,13 +99,31 @@ EE_MAIN_FUNC int main( int argc, char** argv ) {
if ( data.empty() )
return;
ui->ensureMainThread( [url, data, mainContainer, urlBar, ui, &app, scrollView, useHNDark] {
+ scrollView->removeEventsOfType( Event::OnSizeChange );
mainContainer->closeAllChildren();
+ scrollView->getVerticalScrollBar()->setValue( 0 );
ui->getStyleSheet().removeAllWithoutMarker( app.getStyleSheetDefaultMarker() );
ui->setURIFromURL( url );
auto urlStr = url.toString();
auto hash = String::hash( urlStr );
- scrollView->getVerticalScrollBar()->setValue( 0 );
ui->loadLayoutFromString( HTMLFormatter::HTMLtoXML( data ), mainContainer, hash );
+ auto htmlNode = ui->findByType( UI_TYPE_HTML_HTML );
+ auto bodyNode = ui->findByType( UI_TYPE_HTML_BODY );
+ if ( htmlNode && bodyNode ) {
+ auto html = htmlNode->asType();
+ auto body = bodyNode->asType();
+ html->setMinHeight( scrollView->getPixelsSize().getHeight() );
+ body->setMinHeight( scrollView->getPixelsSize().getHeight() );
+ scrollView->on( Event::OnSizeChange, [scrollView, html, body]( auto ) {
+ body->setMinHeight( scrollView->getSize().getHeight() );
+ body->setPixelsSize( { html->getPixelsSize().getWidth(), 0 } );
+ html->setMinHeight( scrollView->getSize().getHeight() );
+ html->setPixelsSize( { html->getPixelsSize().getWidth(), 0 } );
+ } );
+ body->on( Event::OnClose, [scrollView]( auto ) {
+ scrollView->removeEventsOfType( Event::OnSizeChange );
+ } );
+ }
urlBar->setText( urlStr );
if ( useHNDark && url.getAuthority() == "news.ycombinator.com" ) {
diff --git a/src/tests/unit_tests/uihtml_position_tests.cpp b/src/tests/unit_tests/uihtml_position_tests.cpp
index 28c4dccf7..a8554c750 100644
--- a/src/tests/unit_tests/uihtml_position_tests.cpp
+++ b/src/tests/unit_tests/uihtml_position_tests.cpp
@@ -196,3 +196,70 @@ UTEST( UIHTMLWidget, positionOutOfFlow_DoesNotAffectParentSize ) {
Engine::destroySingleton();
}
+
+UTEST( UIHTMLWidget, positionOutOfFlow_PercentageAndMargin ) {
+ init_ui_test();
+ UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode();
+
+ UIHTMLWidget* rootContainer = UIHTMLWidget::New();
+ rootContainer->setParent( sceneNode->getRoot() );
+ rootContainer->setCSSPosition( CSSPosition::Relative );
+ rootContainer->setPixelsSize( 800, 600 );
+ rootContainer->setPixelsPosition( 0, 0 );
+
+ UIHTMLWidget* absoluteChild = UIHTMLWidget::New();
+ absoluteChild->setParent( rootContainer );
+ absoluteChild->setPixelsSize( 400, 400 );
+
+ // Emulate the CSS parsing via applyProperty
+ absoluteChild->applyProperty( StyleSheetProperty( "position", "absolute" ) );
+ absoluteChild->applyProperty( StyleSheetProperty( "left", "50%" ) );
+ absoluteChild->applyProperty( StyleSheetProperty( "margin-left", "-200px" ) );
+ absoluteChild->applyProperty( StyleSheetProperty( "margin-top", "120px" ) );
+
+ sceneNode->updateDirtyLayouts();
+
+ UIWidget* cb = absoluteChild->getContainingBlock();
+ EXPECT_EQ( cb, rootContainer );
+
+ Vector2f worldPos = absoluteChild->convertToWorldSpace( { 0, 0 } );
+ // left should be 50% of 800 (400) plus margin-left (-200) = 200
+ // top should be 0 + margin-top (120) = 120
+ EXPECT_NEAR( 200.f, worldPos.x, 1.f );
+ EXPECT_NEAR( 120.f, worldPos.y, 1.f );
+
+ Engine::destroySingleton();
+}
+
+#include
+#include
+
+UTEST( UIHTMLWidget, positionOutOfFlow_ComplexHTML ) {
+ init_ui_test();
+ UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode();
+
+ sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/" );
+ std::string html;
+ FileSystem::fileGet( "assets/html/absolute_position.html", html );
+ std::string xml = UI::Tools::HTMLFormatter::HTMLtoXML( html );
+ sceneNode->loadLayoutFromString( xml );
+
+ sceneNode->update( Milliseconds( 16 ) );
+ sceneNode->updateDirtyLayouts();
+
+ UIWidget* mainWidget = sceneNode->getRoot()->find( "main" );
+ ASSERT_TRUE( mainWidget != nullptr );
+ EXPECT_GT( mainWidget->getPixelsSize().getHeight(), 0.f ); // This is not standard in HTML!
+
+ Vector2f worldPos = mainWidget->convertToWorldSpace( { 0, 0 } );
+ // Window size is 1024x650
+ // left: 50% of 1024 = 512
+ // margin-left: -200px
+ // 512 - 200 = 312
+ EXPECT_NEAR( 312.f, worldPos.x, 1.f );
+
+ // top should just be margin-top
+ EXPECT_NEAR( 120.f, worldPos.y, 1.f );
+
+ Engine::destroySingleton();
+}