diff --git a/include/eepp/ui/uihelper.hpp b/include/eepp/ui/uihelper.hpp index 3b167a512..1f1e8db2e 100644 --- a/include/eepp/ui/uihelper.hpp +++ b/include/eepp/ui/uihelper.hpp @@ -125,6 +125,8 @@ enum UINodeType { UI_TYPE_DROPDOWNMODELLIST, UI_TYPE_DIFF_VIEW, UI_TYPE_BR, + UI_TYPE_HTML_HTML, + UI_TYPE_HTML_BODY, UI_TYPE_MODULES = 10000, UI_TYPE_TERMINAL = 10001, UI_TYPE_USER = 200000, diff --git a/include/eepp/ui/uirichtext.hpp b/include/eepp/ui/uirichtext.hpp index 81f21b4e4..09a792071 100644 --- a/include/eepp/ui/uirichtext.hpp +++ b/include/eepp/ui/uirichtext.hpp @@ -30,6 +30,10 @@ class EE_API UIRichText : public UILayout { static UIRichText* NewHr(); + static UIRichText* NewHtml(); + + static UIRichText* NewBody(); + static UIRichText* NewDiv() { return UIRichText::NewWithTag( "div" ); }; static UIRichText* NewPre() { return UIRichText::NewWithTag( "pre" ); }; @@ -157,6 +161,29 @@ class EE_API UIRichText : public UILayout { void updateDefaultSpansStyle(); }; +class EE_API UIHTMLHtml : public UIRichText { + public: + static UIHTMLHtml* New( const std::string& tag ); + virtual Uint32 getType() const override; + bool isType( const Uint32& type ) const override; + + protected: + UIHTMLHtml( const std::string& tag = "html" ); +}; + +class EE_API UIHTMLBody : public UIRichText { + public: + static UIHTMLBody* New( const std::string& tag ); + virtual Uint32 getType() const override; + bool isType( const Uint32& type ) const override; + bool applyProperty( const StyleSheetProperty& attribute ) override; + + protected: + bool mPropagatedBackground{ false }; + + UIHTMLBody( const std::string& tag = "body" ); +}; + }} // namespace EE::UI #endif diff --git a/include/eepp/ui/uiwidget.hpp b/include/eepp/ui/uiwidget.hpp index a0b898fca..1d68c78b7 100644 --- a/include/eepp/ui/uiwidget.hpp +++ b/include/eepp/ui/uiwidget.hpp @@ -303,6 +303,24 @@ class EE_API UIWidget : public UINode { */ UIWidget* setLayoutMarginBottom( const Float& marginBottom ); + UIWidget* setLayoutMarginLeftAuto( bool isAuto ); + + UIWidget* setLayoutMarginRightAuto( bool isAuto ); + + UIWidget* setLayoutMarginTopAuto( bool isAuto ); + + UIWidget* setLayoutMarginBottomAuto( bool isAuto ); + + UIWidget* setLayoutMarginAuto( bool left, bool right, bool top, bool bottom ); + + bool hasLayoutMarginLeftAuto() const; + + bool hasLayoutMarginRightAuto() const; + + bool hasLayoutMarginTopAuto() const; + + bool hasLayoutMarginBottomAuto() const; + /** * @brief Sets the layout margin for all sides in pixels. * @@ -1330,6 +1348,14 @@ class EE_API UIWidget : public UINode { mutable Float mMinIntrinsicWidth{ 0 }; mutable Float mMaxIntrinsicWidth{ 0 }; mutable bool mIntrinsicWidthsDirty{ true }; + Uint8 mMarginAuto{ 0 }; + + static constexpr Uint8 MarginAutoLeft = ( 1 << 0 ); + static constexpr Uint8 MarginAutoRight = ( 1 << 1 ); + static constexpr Uint8 MarginAutoTop = ( 1 << 2 ); + static constexpr Uint8 MarginAutoBottom = ( 1 << 3 ); + + void calculateAutoMargin(); /** * @brief Default constructor. @@ -1680,6 +1706,8 @@ class EE_API UIWidget : public UINode { /* @return The size of the widget when size policy is match_parent */ Sizef getSizeFromLayoutPolicy(); + + UIWidget* setLayoutMarginAuto( Uint32 dir, bool isAuto ); }; }} // namespace EE::UI diff --git a/src/eepp/ui/uirichtext.cpp b/src/eepp/ui/uirichtext.cpp index 7c4c03134..c3709f41e 100644 --- a/src/eepp/ui/uirichtext.cpp +++ b/src/eepp/ui/uirichtext.cpp @@ -28,6 +28,72 @@ class UILineBreak : public UIRichText { } }; +UIHTMLHtml* UIHTMLHtml::New( const std::string& tag ) { + return eeNew( UIHTMLHtml, ( tag ) ); +} + +UIHTMLHtml::UIHTMLHtml( const std::string& tag ) : UIRichText( tag ) {} + +Uint32 UIHTMLHtml::getType() const { + return UI_TYPE_HTML_HTML; +} + +bool UIHTMLHtml::isType( const Uint32& type ) const { + return UIHTMLHtml::getType() == type ? true : UIRichText::isType( type ); +} + +UIHTMLBody* UIHTMLBody::New( const std::string& tag ) { + return eeNew( UIHTMLBody, ( tag ) ); +} + +UIHTMLBody::UIHTMLBody( const std::string& tag ) : UIRichText( tag ) {} + +Uint32 UIHTMLBody::getType() const { + return UI_TYPE_HTML_BODY; +} + +bool UIHTMLBody::isType( const Uint32& type ) const { + return UIHTMLBody::getType() == type ? true : UIRichText::isType( type ); +} + +bool UIHTMLBody::applyProperty( const StyleSheetProperty& attribute ) { + if ( !checkPropertyDefinition( attribute ) ) + return false; + + switch ( attribute.getPropertyDefinition()->getPropertyId() ) { + case PropertyId::BackgroundColor: + case PropertyId::BackgroundImage: + case PropertyId::BackgroundTint: + case PropertyId::BackgroundPositionX: + case PropertyId::BackgroundPositionY: + case PropertyId::BackgroundRepeat: + case PropertyId::BackgroundSize: { + if ( getParent() && getParent()->isType( UI_TYPE_HTML_HTML ) ) { + UIWidget* htmlParent = getParent()->asType(); + if ( htmlParent->getBackgroundColor() == Color::Transparent || + mPropagatedBackground ) { + mPropagatedBackground = true; + htmlParent->applyProperty( attribute ); + return true; + } + } + break; + } + default: + break; + } + + return UIRichText::applyProperty( attribute ); +} + +UIRichText* UIRichText::NewHtml() { + return UIHTMLHtml::New( "html" ); +} + +UIRichText* UIRichText::NewBody() { + return UIHTMLBody::New( "body" ); +} + UIRichText* UIRichText::NewBr() { return UILineBreak::New( "br" ); }; @@ -554,7 +620,7 @@ void UIRichText::rebuildRichText( RichText& richText, IntrinsicMode mode ) { richText.addCustomSize( Sizef( w + margin.Left + margin.Right, size.getHeight() + margin.Top + margin.Bottom ), - isBlock ); + isBlock ); } }; diff --git a/src/eepp/ui/uiwidget.cpp b/src/eepp/ui/uiwidget.cpp index 458556715..6a6a6054f 100644 --- a/src/eepp/ui/uiwidget.cpp +++ b/src/eepp/ui/uiwidget.cpp @@ -156,6 +156,60 @@ UIWidget* UIWidget::setLayoutMarginBottom( const Float& marginBottom ) { return this; } +UIWidget* UIWidget::setLayoutMarginAuto( Uint32 dir, bool isAuto ) { + if ( isAuto != ( ( mMarginAuto & dir ) != 0 ) ) { + if ( isAuto ) { + mMarginAuto |= dir; + calculateAutoMargin(); + } else { + mMarginAuto &= ~dir; + notifyLayoutAttrChange(); + notifyLayoutAttrChangeParent(); + } + } + return this; +} + +UIWidget* UIWidget::setLayoutMarginLeftAuto( bool isAuto ) { + return setLayoutMarginAuto( MarginAutoLeft, isAuto ); +} + +UIWidget* UIWidget::setLayoutMarginRightAuto( bool isAuto ) { + return setLayoutMarginAuto( MarginAutoRight, isAuto ); +} + +UIWidget* UIWidget::setLayoutMarginTopAuto( bool isAuto ) { + return setLayoutMarginAuto( MarginAutoTop, isAuto ); +} + +UIWidget* UIWidget::setLayoutMarginBottomAuto( bool isAuto ) { + return setLayoutMarginAuto( MarginAutoTop, isAuto ); +} + +UIWidget* UIWidget::setLayoutMarginAuto( bool left, bool right, bool top, bool bottom ) { + setLayoutMarginLeftAuto( left ); + setLayoutMarginRightAuto( right ); + setLayoutMarginTopAuto( top ); + setLayoutMarginBottomAuto( bottom ); + return this; +} + +bool UIWidget::hasLayoutMarginLeftAuto() const { + return mMarginAuto & MarginAutoLeft; +} + +bool UIWidget::hasLayoutMarginRightAuto() const { + return mMarginAuto & MarginAutoRight; +} + +bool UIWidget::hasLayoutMarginTopAuto() const { + return mMarginAuto & MarginAutoTop; +} + +bool UIWidget::hasLayoutMarginBottomAuto() const { + return mMarginAuto & MarginAutoBottom; +} + UIWidget* UIWidget::setLayoutPixelsMargin( const Rectf& margin ) { if ( mLayoutMargin != margin ) { mLayoutMarginPx = margin; @@ -533,8 +587,83 @@ UITooltip* UIWidget::getTooltip() { return mTooltip; } +void UIWidget::calculateAutoMargin() { + if ( !mMarginAuto || !getParent() || !getParent()->isWidget() ) + return; + + UIWidget* parent = getParent()->asType(); + Sizef parentSize = parent->getPixelsSize(); + Rectf parentPadding = parent->getPixelsPadding(); + + bool changed = false; + if ( ( mMarginAuto & MarginAutoLeft ) && ( mMarginAuto & MarginAutoRight ) ) { + Float availableWidth = parentSize.getWidth() - parentPadding.Left - parentPadding.Right - + getPixelsSize().getWidth(); + Float newMarginLeft = availableWidth > 0 ? availableWidth / 2.f : 0.f; + Float newMarginRight = availableWidth > 0 ? availableWidth / 2.f : 0.f; + if ( mLayoutMarginPx.Left != newMarginLeft || mLayoutMarginPx.Right != newMarginRight ) { + mLayoutMarginPx.Left = newMarginLeft; + mLayoutMarginPx.Right = newMarginRight; + changed = true; + } + } else if ( mMarginAuto & MarginAutoLeft ) { + Float availableWidth = parentSize.getWidth() - parentPadding.Left - parentPadding.Right - + getPixelsSize().getWidth() - mLayoutMarginPx.Right; + Float newMarginLeft = std::max( 0.f, availableWidth ); + if ( mLayoutMarginPx.Left != newMarginLeft ) { + mLayoutMarginPx.Left = newMarginLeft; + changed = true; + } + } else if ( mMarginAuto & MarginAutoRight ) { + Float availableWidth = parentSize.getWidth() - parentPadding.Left - parentPadding.Right - + getPixelsSize().getWidth() - mLayoutMarginPx.Left; + Float newMarginRight = std::max( 0.f, availableWidth ); + if ( mLayoutMarginPx.Right != newMarginRight ) { + mLayoutMarginPx.Right = newMarginRight; + changed = true; + } + } + + if ( ( mMarginAuto & MarginAutoTop ) && ( mMarginAuto & MarginAutoBottom ) ) { + Float availableHeight = parentSize.getHeight() - parentPadding.Top - parentPadding.Bottom - + getPixelsSize().getHeight(); + Float newMarginTop = availableHeight > 0 ? availableHeight / 2.f : 0.f; + Float newMarginBottom = availableHeight > 0 ? availableHeight / 2.f : 0.f; + if ( mLayoutMarginPx.Top != newMarginTop || mLayoutMarginPx.Bottom != newMarginBottom ) { + mLayoutMarginPx.Top = newMarginTop; + mLayoutMarginPx.Bottom = newMarginBottom; + changed = true; + } + } else if ( mMarginAuto & MarginAutoTop ) { + Float availableHeight = parentSize.getHeight() - parentPadding.Top - parentPadding.Bottom - + getPixelsSize().getHeight() - mLayoutMarginPx.Bottom; + Float newMarginTop = std::max( 0.f, availableHeight ); + if ( mLayoutMarginPx.Top != newMarginTop ) { + mLayoutMarginPx.Top = newMarginTop; + changed = true; + } + } else if ( mMarginAuto & MarginAutoBottom ) { + Float availableHeight = parentSize.getHeight() - parentPadding.Top - parentPadding.Bottom - + getPixelsSize().getHeight() - mLayoutMarginPx.Top; + Float newMarginBottom = std::max( 0.f, availableHeight ); + if ( mLayoutMarginPx.Bottom != newMarginBottom ) { + mLayoutMarginPx.Bottom = newMarginBottom; + changed = true; + } + } + + if ( changed ) { + mLayoutMargin = PixelDensity::pxToDp( mLayoutMarginPx ); + onMarginChange(); + notifyLayoutAttrChange(); + notifyLayoutAttrChangeParent(); + } +} + void UIWidget::onParentSizeChange( const Vector2f& sizeChange ) { updateAnchors( sizeChange ); + if ( mMarginAuto != 0 ) + calculateAutoMargin(); UINode::onParentSizeChange( sizeChange ); } @@ -551,6 +680,8 @@ void UIWidget::onVisibilityChange() { } void UIWidget::onSizeChange() { + if ( mMarginAuto != 0 ) + calculateAutoMargin(); UINode::onSizeChange(); if ( mBorder != NULL ) @@ -1845,18 +1976,46 @@ bool UIWidget::applyProperty( const StyleSheetProperty& attribute ) { } break; } - case PropertyId::MarginLeft: - setLayoutMarginLeft( lengthFromValueAsDp( attribute ) ); + case PropertyId::MarginLeft: { + if ( attribute.asString() == "auto" ) { + mMarginAuto |= MarginAutoLeft; + calculateAutoMargin(); + } else { + mMarginAuto &= ~MarginAutoLeft; + setLayoutMarginLeft( lengthFromValueAsDp( attribute ) ); + } break; - case PropertyId::MarginRight: - setLayoutMarginRight( lengthFromValueAsDp( attribute ) ); + } + case PropertyId::MarginRight: { + if ( attribute.asString() == "auto" ) { + mMarginAuto |= MarginAutoRight; + calculateAutoMargin(); + } else { + mMarginAuto &= ~MarginAutoRight; + setLayoutMarginRight( lengthFromValueAsDp( attribute ) ); + } break; - case PropertyId::MarginTop: - setLayoutMarginTop( lengthFromValueAsDp( attribute ) ); + } + case PropertyId::MarginTop: { + if ( attribute.asString() == "auto" ) { + mMarginAuto |= MarginAutoTop; + calculateAutoMargin(); + } else { + mMarginAuto &= ~MarginAutoTop; + setLayoutMarginTop( lengthFromValueAsDp( attribute ) ); + } break; - case PropertyId::MarginBottom: - setLayoutMarginBottom( lengthFromValueAsDp( attribute ) ); + } + case PropertyId::MarginBottom: { + if ( attribute.asString() == "auto" ) { + mMarginAuto |= MarginAutoBottom; + calculateAutoMargin(); + } else { + mMarginAuto &= ~MarginAutoBottom; + setLayoutMarginBottom( lengthFromValueAsDp( attribute ) ); + } break; + } case PropertyId::Tooltip: { String text = getTranslatorString( attribute.value() ); setTooltipText( text ); @@ -2410,12 +2569,14 @@ Float UIWidget::getMatchParentWidth() const { if ( getParent()->isWidget() ) padding = static_cast( getParent() )->getPixelsPadding(); - Float width = getParent()->getPixelsSize().getWidth() - mLayoutMarginPx.Left - - mLayoutMarginPx.Right - padding.Left - padding.Right; + Float marginLeft = ( mMarginAuto & MarginAutoLeft ) ? 0.f : mLayoutMarginPx.Left; + Float marginRight = ( mMarginAuto & MarginAutoRight ) ? 0.f : mLayoutMarginPx.Right; + + Float width = getParent()->getPixelsSize().getWidth() - marginLeft - marginRight - + padding.Left - padding.Right; if ( !mMaxWidthEq.empty() ) { - Float maxWidth( getMaxSizePx().getWidth() - mLayoutMarginPx.Left - mLayoutMarginPx.Right - - padding.Left - padding.Right ); + Float maxWidth( getMaxSizePx().getWidth() ); if ( maxWidth > 0 && maxWidth < width ) width = maxWidth; } @@ -2427,14 +2588,16 @@ Float UIWidget::getMatchParentHeight() const { Rectf padding = Rectf::Zero; if ( getParent()->isWidget() ) - padding = static_cast( getParent() )->getPadding(); + padding = static_cast( getParent() )->getPixelsPadding(); - Float height = getParent()->getPixelsSize().getHeight() - mLayoutMarginPx.Top - - mLayoutMarginPx.Bottom - padding.Top - padding.Bottom; + Float marginTop = ( mMarginAuto & MarginAutoTop ) ? 0.f : mLayoutMarginPx.Top; + Float marginBottom = ( mMarginAuto & MarginAutoBottom ) ? 0.f : mLayoutMarginPx.Bottom; + + Float height = getParent()->getPixelsSize().getHeight() - marginTop - marginBottom - + padding.Top - padding.Bottom; if ( !mMaxHeightEq.empty() ) { - Float maxHeight( getMaxSizePx().getHeight() - mLayoutMarginPx.Left - mLayoutMarginPx.Right - - padding.Left - padding.Right ); + Float maxHeight( getMaxSizePx().getHeight() ); if ( maxHeight > 0 && maxHeight < height ) height = maxHeight; } diff --git a/src/eepp/ui/uiwidgetcreator.cpp b/src/eepp/ui/uiwidgetcreator.cpp index fbf87f986..160ce7dbb 100644 --- a/src/eepp/ui/uiwidgetcreator.cpp +++ b/src/eepp/ui/uiwidgetcreator.cpp @@ -173,9 +173,9 @@ void UIWidgetCreator::createBaseWidgetList() { registeredWidget["section"] = [] { return UIRichText::NewWithTag( "section" ); }; registeredWidget["nav"] = [] { return UIRichText::NewWithTag( "nav" ); }; registeredWidget["center"] = [] { return UIRichText::NewWithTag( "center" ); }; - registeredWidget["html"] = [] { return UIRichText::NewWithTag( "html" ); }; + registeredWidget["html"] = UIRichText::NewHtml; registeredWidget["head"] = [] { return UIWidget::NewWithTag( "head" ); }; - registeredWidget["body"] = [] { return UIRichText::NewWithTag( "body" ); }; + registeredWidget["body"] = UIRichText::NewBody; registeredWidget["form"] = [] { return UIRichText::NewWithTag( "form" ); }; registeredWidget["table"] = UIHTMLTable::New; registeredWidget["tr"] = UIHTMLTableRow::New; diff --git a/src/tests/unit_tests/uihtml_tests.cpp b/src/tests/unit_tests/uihtml_tests.cpp index 60f98d40d..316788e34 100644 --- a/src/tests/unit_tests/uihtml_tests.cpp +++ b/src/tests/unit_tests/uihtml_tests.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -429,3 +430,139 @@ UTEST( UIHTMLTable, tableLayoutFixed ) { Engine::destroySingleton(); } + +UTEST( UIHTMLBody, backgroundColorPropagation ) { + Engine::instance()->createWindow( WindowSettings( 1024, 650, "HTML Tables Test", + WindowStyle::Default, WindowBackend::Default, + 32, {}, 1, false, true ) ); + FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); + + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); + ASSERT_TRUE( font != nullptr && font->loaded() ); + FontFamily::loadFromRegular( font ); + + UI::UISceneNode* sceneNode = UI::UISceneNode::New(); + SceneManager::instance()->add( sceneNode ); + UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager(); + themeManager->setDefaultFont( font ); + + sceneNode->loadLayoutFromString( + R"( + + + )" ); + + sceneNode->updateDirtyLayouts(); + + auto html_el = sceneNode->getRoot()->find( "html_el" ); + auto body_el = sceneNode->getRoot()->find( "body_el" ); + + ASSERT_TRUE( html_el != nullptr ); + ASSERT_TRUE( body_el != nullptr ); + + // HTML element should have inherited the red background color, and body should be transparent + EXPECT_TRUE( html_el->asType()->getBackgroundColor() == Color::Red ); + EXPECT_TRUE( body_el->asType()->getBackgroundColor() == Color::Transparent ); + + Engine::destroySingleton(); +} + +UTEST( UIHTMLBody, maxWidthResizingBug ) { + Engine::instance()->createWindow( WindowSettings( 1024, 768, "HTML Resize Bug", + WindowStyle::Default, WindowBackend::Default, + 32, {}, 1, false, true ) ); + FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); + + UI::UISceneNode* sceneNode = UI::UISceneNode::New(); + SceneManager::instance()->add( sceneNode ); + + UI::CSS::StyleSheetParser parser; + parser.loadFromFile( "/tmp/style.css" ); + sceneNode->setStyleSheet( parser.getStyleSheet() ); + + std::string htmlContent; + FileSystem::fileGet( "/tmp/dwarmstrong.html", htmlContent ); + sceneNode->loadLayoutFromString( htmlContent ); + + sceneNode->getRoot()->setSize( 1024, 768 ); + sceneNode->updateDirtyLayouts(); + + auto body_el = sceneNode->getRoot()->findByType( UI_TYPE_HTML_BODY )->asType(); + ASSERT_TRUE( body_el != nullptr ); + Float widthAt1024 = body_el->getPixelsSize().getWidth(); + EXPECT_NEAR( widthAt1024, 960.f, 10.f ); // It should be around 960px (minus some margins if any) + + sceneNode->getRoot()->setSize( 2048, 768 ); + sceneNode->updateDirtyLayouts(); + Float widthAt2048 = body_el->getPixelsSize().getWidth(); + EXPECT_NEAR( widthAt2048, 960.f, 10.f ); // Body should stay 960px even when parent is huge + + sceneNode->getRoot()->setSize( 1024, 768 ); + sceneNode->updateDirtyLayouts(); + + Float widthAfterResize = body_el->getPixelsSize().getWidth(); + EXPECT_NEAR( widthAt1024, widthAfterResize, 1.f ); + + Engine::destroySingleton(); +} + +UTEST( UILayout, marginAuto ) { + Engine::instance()->createWindow( WindowSettings( 1024, 650, "Margin Auto Test", + WindowStyle::Default, WindowBackend::Default, + 32, {}, 1, false, true ) ); + FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); + + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); + ASSERT_TRUE( font != nullptr && font->loaded() ); + FontFamily::loadFromRegular( font ); + + UI::UISceneNode* sceneNode = UI::UISceneNode::New(); + SceneManager::instance()->add( sceneNode ); + UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager(); + themeManager->setDefaultFont( font ); + + auto* container = sceneNode->loadLayoutFromString( + R"( + + )" ); + + auto child = sceneNode->getRoot()->find( "child" ); + ASSERT_TRUE( child != nullptr ); + + UIWidget* childWidget = child->asType(); + UIWidget* contWidget = container->asType(); + + contWidget->setSize( 500, 500 ); + childWidget->setSize( 100, 100 ); + sceneNode->updateDirtyLayouts(); + + Float expectedMarginX = ( contWidget->getPixelsSize().getWidth() - childWidget->getPixelsSize().getWidth() ) / 2.f; + + // Margin left/right should be auto computed to expectedMarginX + EXPECT_NEAR( childWidget->getLayoutPixelsMargin().Left, expectedMarginX, 1.f ); + EXPECT_NEAR( childWidget->getLayoutPixelsMargin().Right, expectedMarginX, 1.f ); + EXPECT_NEAR( childWidget->getLayoutPixelsMargin().Top, 0.f, 1.f ); + EXPECT_NEAR( childWidget->getLayoutPixelsMargin().Bottom, 0.f, 1.f ); + + // Resize parent and see if margins re-evaluate automatically + contWidget->setSize( 800, 800 ); + sceneNode->updateDirtyLayouts(); + + expectedMarginX = ( contWidget->getPixelsSize().getWidth() - childWidget->getPixelsSize().getWidth() ) / 2.f; + + EXPECT_NEAR( childWidget->getLayoutPixelsMargin().Left, expectedMarginX, 1.f ); + EXPECT_NEAR( childWidget->getLayoutPixelsMargin().Right, expectedMarginX, 1.f ); + + // Now test resize of child + childWidget->setSize( 200, 100 ); + sceneNode->updateDirtyLayouts(); + + expectedMarginX = ( contWidget->getPixelsSize().getWidth() - childWidget->getPixelsSize().getWidth() ) / 2.f; + + EXPECT_NEAR( childWidget->getLayoutPixelsMargin().Left, expectedMarginX, 1.f ); + EXPECT_NEAR( childWidget->getLayoutPixelsMargin().Right, expectedMarginX, 1.f ); + + Engine::destroySingleton(); +} diff --git a/src/tools/ecode/plugins/aiassistant/chatui.cpp b/src/tools/ecode/plugins/aiassistant/chatui.cpp index 5a74d1563..397247c3b 100644 --- a/src/tools/ecode/plugins/aiassistant/chatui.cpp +++ b/src/tools/ecode/plugins/aiassistant/chatui.cpp @@ -3095,7 +3095,7 @@ void LLMChatUI::updateTabTitle() { } if ( hasPendingPermissions ) - title += "- ✋ " + i18n( "action_required", "Action Required" ) + " - " + title; + title = " - ✋ " + i18n( "action_required", "Action Required" ) + " - " + title; if ( !mSummary.empty() ) title += " - " + mSummary; diff --git a/test_auto_margin_bug.html b/test_auto_margin_bug.html new file mode 100644 index 000000000..d7a848011 --- /dev/null +++ b/test_auto_margin_bug.html @@ -0,0 +1,5 @@ + + +
+ +