diff --git a/bin/unit_tests/assets/html/ss64_deferred_cascade.css b/bin/unit_tests/assets/html/ss64_deferred_cascade.css
index 8fdbd76a7..40d66f5a7 100644
--- a/bin/unit_tests/assets/html/ss64_deferred_cascade.css
+++ b/bin/unit_tests/assets/html/ss64_deferred_cascade.css
@@ -6,6 +6,17 @@ li a[href="../bash/"] {
background-color: #F8F9FA;
}
+.tbtn {
+ height: 40px;
+ line-height: 40px;
+ width: 140px;
+}
+
+.tbtn a {
+ background-color: #F8F9FA;
+ display: block;
+}
+
#external-css-loaded {
height: 37px;
}
diff --git a/include/eepp/ui/uihtmlwidget.hpp b/include/eepp/ui/uihtmlwidget.hpp
index 8282aee0e..c91fd648f 100644
--- a/include/eepp/ui/uihtmlwidget.hpp
+++ b/include/eepp/ui/uihtmlwidget.hpp
@@ -310,6 +310,8 @@ class EE_API UIHTMLWidget : public UILayout {
virtual void onParentChange();
+ virtual void onParentSizeChange( const Vector2f& sizeChange );
+
virtual void onPositionChange();
UIWidget* getContainingBlock();
@@ -368,6 +370,7 @@ class EE_API UIHTMLWidget : public UILayout {
bool mOverflowCreatesBlockFormattingContext{ false };
bool mNeedsOrderSort{ false };
bool mNeedsZIndexSort{ false };
+ Uint8 mPercentageMargins{ 0 };
UILayouter* mLayouter{ nullptr };
UIHTMLWidgetFlexState* mFlexState{ nullptr };
UIHTMLWidgetGridState* mGridState{ nullptr };
diff --git a/src/eepp/ui/uihtmlwidget.cpp b/src/eepp/ui/uihtmlwidget.cpp
index 58e80e55c..a748386c3 100644
--- a/src/eepp/ui/uihtmlwidget.cpp
+++ b/src/eepp/ui/uihtmlwidget.cpp
@@ -937,6 +937,33 @@ bool UIHTMLWidget::applyProperty( const StyleSheetProperty& attribute ) {
mOverflowCreatesBlockFormattingContext =
!String::iequals( attribute.getValue(), "visible" );
return UILayout::applyProperty( attribute );
+ case PropertyId::MarginTop:
+ case PropertyId::MarginRight:
+ case PropertyId::MarginBottom:
+ case PropertyId::MarginLeft: {
+ Uint8 marginBit = 0;
+ switch ( attribute.getPropertyDefinition()->getPropertyId() ) {
+ case PropertyId::MarginTop:
+ marginBit = 1 << 0;
+ break;
+ case PropertyId::MarginRight:
+ marginBit = 1 << 1;
+ break;
+ case PropertyId::MarginBottom:
+ marginBit = 1 << 2;
+ break;
+ case PropertyId::MarginLeft:
+ marginBit = 1 << 3;
+ break;
+ default:
+ break;
+ }
+ if ( StyleSheetLength::isPercentage( attribute.value() ) )
+ mPercentageMargins |= marginBit;
+ else
+ mPercentageMargins &= ~marginBit;
+ return UILayout::applyProperty( attribute );
+ }
case PropertyId::Width:
case PropertyId::Height:
case PropertyId::PaddingLeft:
@@ -1395,6 +1422,28 @@ void UIHTMLWidget::onParentChange() {
updateScrollListeners();
}
+void UIHTMLWidget::onParentSizeChange( const Vector2f& sizeChange ) {
+ UILayout::onParentSizeChange( sizeChange );
+
+ if ( mPercentageMargins == 0 || !getUIStyle() )
+ return;
+
+ static constexpr PropertyId MarginProperties[] = {
+ PropertyId::MarginTop,
+ PropertyId::MarginRight,
+ PropertyId::MarginBottom,
+ PropertyId::MarginLeft,
+ };
+ for ( Uint32 i = 0; i < 4; ++i ) {
+ if ( !( mPercentageMargins & ( 1 << i ) ) )
+ continue;
+ PropertyId propertyId = MarginProperties[i];
+ const StyleSheetProperty* property = getUIStyle()->getProperty( propertyId );
+ if ( property )
+ applyProperty( *property );
+ }
+}
+
void UIHTMLWidget::onPositionChange() {
UILayout::onPositionChange();
if ( mPosition == CSSPosition::Sticky && !mIsUpdatingScroll ) {
diff --git a/src/tests/unit_tests/uihtml_float_tests.cpp b/src/tests/unit_tests/uihtml_float_tests.cpp
index 937a176d7..304a1a044 100644
--- a/src/tests/unit_tests/uihtml_float_tests.cpp
+++ b/src/tests/unit_tests/uihtml_float_tests.cpp
@@ -8,10 +8,12 @@
#include
#include
#include
+#include
#include
#include
#include
#include
+#include
#include
#include
@@ -747,6 +749,102 @@ UTEST( UIHTMLFloat, ss64DeferredStyleSheetPreservesDocumentSourceOrder ) {
Engine::destroySingleton();
}
+UTEST( UIHTMLFloat, ss64DeferredStyleSheetRelayoutsFooter ) {
+ init_float_test();
+ UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode();
+ sceneNode->setThreadPool( ThreadPool::createShared( 1 ) );
+
+ UIWebView* webView = UIWebView::New();
+ webView->setParent( sceneNode->getRoot() );
+ webView->setPixelsSize( 1280, 650 );
+ webView->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed );
+
+ UISceneNode* documentScene = webView->getDocumentSceneNode();
+ ASSERT_TRUE( documentScene != nullptr );
+ documentScene->setURI( "file://" + Sys::getProcessPath() + "assets/html/" );
+ documentScene->loadLayoutFromString( HTMLFormatter::HTMLtoXML( R"html(
+
+
+
+
+
+
+
+ Command line reference.
+
+
+
+
+
+ )html" ),
+ webView->getDocumentContainer(),
+ String::hash( "ss64-deferred-footer" ) );
+ webView->refreshDocumentLayout();
+
+ auto* loaded = documentScene->getRoot()->find( "external-css-loaded" )->asType();
+ auto* heading = documentScene->getRoot()->find( "heading" )->asType();
+ auto* footer = documentScene->getRoot()->find( "footer" )->asType();
+ ASSERT_TRUE( loaded != nullptr );
+ ASSERT_TRUE( heading != nullptr );
+ ASSERT_TRUE( footer != nullptr );
+
+ for ( int i = 0; i < 500 && loaded->getPixelsSize().getHeight() < 36.f; ++i ) {
+ SceneManager::instance()->update();
+ Sys::sleep( Milliseconds( 1 ) );
+ }
+ ASSERT_NEAR( loaded->getPixelsSize().getHeight(), 37.f, 1.f );
+ EXPECT_NEAR( footer->getPixelsPosition().x, heading->getPixelsPosition().x, 1.f );
+
+ Engine::destroySingleton();
+}
+
+UTEST( UIHTMLFloat, percentageMarginReResolvesWhenContainingBlockGetsWidth ) {
+ init_float_test();
+ UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode();
+
+ sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( R"html(
+
+ )html" ) );
+
+ auto* body = sceneNode->getRoot()->querySelector( "body" )->asType();
+ auto* target = sceneNode->getRoot()->find( "target" )->asType();
+ ASSERT_TRUE( body != nullptr );
+ ASSERT_TRUE( target != nullptr );
+ const StyleSheetProperty* marginLeft =
+ target->getUIStyle()->getProperty( PropertyId::MarginLeft );
+ ASSERT_TRUE( marginLeft != nullptr );
+
+ body->setPixelsSize( 0.f, body->getPixelsSize().getHeight() );
+ target->applyProperty( *marginLeft );
+ EXPECT_NEAR( target->getLayoutPixelsMargin().Left, 0.f, 0.01f );
+
+ body->setPixelsSize( 1000.f, body->getPixelsSize().getHeight() );
+ EXPECT_NEAR( target->getLayoutPixelsMargin().Left, 65.f, 0.01f );
+
+ Engine::destroySingleton();
+}
+
UTEST( UIHTMLFloat, autoHorizontalMarginsCenterBlockInsideFloat ) {
init_float_test();
UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode();