From 82a93dada4050f9f2fc31d528e0d6b2b6dbecfc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Mon, 27 Apr 2026 01:38:37 -0300 Subject: [PATCH] Added UIHTMLListItem to handle list items properly. --- bin/assets/ui/breeze.css | 48 +---- include/eepp/ui/css/propertydefinition.hpp | 3 + include/eepp/ui/csslayouttypes.hpp | 26 +++ include/eepp/ui/uihelper.hpp | 1 + include/eepp/ui/uihtmllistitem.hpp | 51 +++++ include/eepp/ui/uirichtext.hpp | 2 - src/eepp/graphics/csslayouttypes.cpp | 54 ++++++ src/eepp/ui/css/stylesheetspecification.cpp | 40 ++++ src/eepp/ui/uihtmllistitem.cpp | 198 ++++++++++++++++++++ src/eepp/ui/uiwidgetcreator.cpp | 3 +- src/tests/unit_tests/uihtml_tests.cpp | 155 +++++++++++++++ 11 files changed, 534 insertions(+), 47 deletions(-) create mode 100644 include/eepp/ui/uihtmllistitem.hpp create mode 100644 src/eepp/ui/uihtmllistitem.cpp diff --git a/bin/assets/ui/breeze.css b/bin/assets/ui/breeze.css index 48c6b0385..240c59a3d 100644 --- a/bin/assets/ui/breeze.css +++ b/bin/assets/ui/breeze.css @@ -148,52 +148,12 @@ ol > li { padding-left: 2em; } -ol > li { - background-tint: var(--font); - background-position: 0.6em 0.3em; +ul { + list-style-type: disc; } -ol > li:nth-child(1) { - background-image: glyph("monospace", 1em, "1"); -} - -ol > li:nth-child(2) { - background-image: glyph("monospace", 1em, "2"); -} - -ol > li:nth-child(3) { - background-image: glyph("monospace", 1em, "3"); -} - -ol > li:nth-child(4) { - background-image: glyph("monospace", 1em, "4"); -} - -ol > li:nth-child(5) { - background-image: glyph("monospace", 1em, "5"); -} - -ol > li:nth-child(6) { - background-image: glyph("monospace", 1em, "6"); -} - -ol > li:nth-child(7) { - background-image: glyph("monospace", 1em, "7"); -} - -ol > li:nth-child(8) { - background-image: glyph("monospace", 1em, "8"); -} - -ol > li:nth-child(9) { - background-image: glyph("monospace", 1em, "9"); -} - -ul > li { - background-image: url("data:image/svg,"); - background-tint: var(--font); - background-position: 0.6em 0.45em; - background-size: 0.5em 0.5em; +ol { + list-style-type: decimal; } a { diff --git a/include/eepp/ui/css/propertydefinition.hpp b/include/eepp/ui/css/propertydefinition.hpp index 8205dee39..9e24f1282 100644 --- a/include/eepp/ui/css/propertydefinition.hpp +++ b/include/eepp/ui/css/propertydefinition.hpp @@ -247,6 +247,9 @@ enum class PropertyId : Uint32 { Bottom = String::hash( "bottom" ), Left = String::hash( "left" ), ZIndex = String::hash( "z-index" ), + ListStyleType = String::hash( "list-style-type" ), + ListStylePosition = String::hash( "list-style-position" ), + ListStyleImage = String::hash( "list-style-image" ), DataLanguage = String::hash( "data-language" ), // Minor hack }; diff --git a/include/eepp/ui/csslayouttypes.hpp b/include/eepp/ui/csslayouttypes.hpp index 743865f60..e7860a0a9 100644 --- a/include/eepp/ui/csslayouttypes.hpp +++ b/include/eepp/ui/csslayouttypes.hpp @@ -34,6 +34,32 @@ struct CSSPositionHelper { static CSSPosition fromString( std::string_view val ); }; +enum class CSSListStyleType { + None, + Disc, + Circle, + Square, + Decimal, + LowerAlpha, + UpperAlpha, + LowerRoman, + UpperRoman +}; + +struct CSSListStyleTypeHelper { + static std::string toString( CSSListStyleType type ); + + static CSSListStyleType fromString( std::string_view val ); +}; + +enum class CSSListStylePosition { Outside, Inside }; + +struct CSSListStylePositionHelper { + static std::string toString( CSSListStylePosition pos ); + + static CSSListStylePosition fromString( std::string_view val ); +}; + }} // namespace EE::UI #endif diff --git a/include/eepp/ui/uihelper.hpp b/include/eepp/ui/uihelper.hpp index 23f3bfdf0..9c2ff2ae3 100644 --- a/include/eepp/ui/uihelper.hpp +++ b/include/eepp/ui/uihelper.hpp @@ -128,6 +128,7 @@ enum UINodeType { UI_TYPE_BR, UI_TYPE_HTML_HTML, UI_TYPE_HTML_BODY, + UI_TYPE_HTML_LIST_ITEM, UI_TYPE_MODULES = 10000, UI_TYPE_TERMINAL = 10001, UI_TYPE_USER = 200000, diff --git a/include/eepp/ui/uihtmllistitem.hpp b/include/eepp/ui/uihtmllistitem.hpp new file mode 100644 index 000000000..c25a0c22c --- /dev/null +++ b/include/eepp/ui/uihtmllistitem.hpp @@ -0,0 +1,51 @@ +#ifndef EE_UI_UIHTMLLISTITEM_HPP +#define EE_UI_UIHTMLLISTITEM_HPP + +#include +#include +#include + +namespace EE { namespace UI { + +class EE_API UIHTMLListItem : public UIRichText { + public: + static UIHTMLListItem* New(); + + virtual Uint32 getType() const; + + virtual bool isType( const Uint32& type ) const; + + virtual void draw(); + + virtual bool applyProperty( const StyleSheetProperty& attribute ); + + virtual std::string getPropertyString( const PropertyDefinition* propertyDef, + const Uint32& propertyIndex = 0 ) const; + + virtual std::vector getPropertiesImplemented() const; + + CSSListStyleType getListStyleType() const { return mListStyleType; } + + void setListStyleType( CSSListStyleType type ); + + CSSListStylePosition getListStylePosition() const { return mListStylePosition; } + + void setListStylePosition( CSSListStylePosition pos ); + + protected: + UIHTMLListItem(); + + CSSListStyleType mListStyleType{ CSSListStyleType::None }; + CSSListStylePosition mListStylePosition{ CSSListStylePosition::Outside }; + std::unique_ptr mListMarkerText; + + int countPrecedingLiSiblings() const; + + String::View getListMarkerString() const; + + void invalidateList(); +}; + +}} // namespace EE::UI + +#endif diff --git a/include/eepp/ui/uirichtext.hpp b/include/eepp/ui/uirichtext.hpp index ad265075e..f87b92f1a 100644 --- a/include/eepp/ui/uirichtext.hpp +++ b/include/eepp/ui/uirichtext.hpp @@ -44,8 +44,6 @@ class EE_API UIRichText : public UIHTMLWidget { static UIRichText* NewPre() { return UIRichText::NewWithTag( "pre" ); }; - static UIRichText* NewListItem() { return UIRichText::NewWithTag( "li" ); }; - static UIRichText* NewBlockquote() { return UIRichText::NewWithTag( "blockquote" ); }; virtual Uint32 getType() const; diff --git a/src/eepp/graphics/csslayouttypes.cpp b/src/eepp/graphics/csslayouttypes.cpp index a50e58a7b..995ca39d6 100644 --- a/src/eepp/graphics/csslayouttypes.cpp +++ b/src/eepp/graphics/csslayouttypes.cpp @@ -85,4 +85,58 @@ CSSPosition CSSPositionHelper::fromString( std::string_view val ) { return position; } +std::string CSSListStyleTypeHelper::toString( CSSListStyleType type ) { + switch ( type ) { + case CSSListStyleType::Disc: + return "disc"; + case CSSListStyleType::Circle: + return "circle"; + case CSSListStyleType::Square: + return "square"; + case CSSListStyleType::Decimal: + return "decimal"; + case CSSListStyleType::LowerAlpha: + return "lower-alpha"; + case CSSListStyleType::UpperAlpha: + return "upper-alpha"; + case CSSListStyleType::LowerRoman: + return "lower-roman"; + case CSSListStyleType::UpperRoman: + return "upper-roman"; + case CSSListStyleType::None: + default: + return "none"; + } +} + +CSSListStyleType CSSListStyleTypeHelper::fromString( std::string_view val ) { + if ( val == "disc" ) + return CSSListStyleType::Disc; + if ( val == "circle" ) + return CSSListStyleType::Circle; + if ( val == "square" ) + return CSSListStyleType::Square; + if ( val == "decimal" ) + return CSSListStyleType::Decimal; + if ( val == "lower-alpha" ) + return CSSListStyleType::LowerAlpha; + if ( val == "upper-alpha" ) + return CSSListStyleType::UpperAlpha; + if ( val == "lower-roman" ) + return CSSListStyleType::LowerRoman; + if ( val == "upper-roman" ) + return CSSListStyleType::UpperRoman; + return CSSListStyleType::None; +} + +std::string CSSListStylePositionHelper::toString( CSSListStylePosition pos ) { + return pos == CSSListStylePosition::Inside ? "inside" : "outside"; +} + +CSSListStylePosition CSSListStylePositionHelper::fromString( std::string_view val ) { + if ( val == "inside" ) + return CSSListStylePosition::Inside; + return CSSListStylePosition::Outside; +} + }} // namespace EE::UI diff --git a/src/eepp/ui/css/stylesheetspecification.cpp b/src/eepp/ui/css/stylesheetspecification.cpp index e0b6f043c..5aec8e9ba 100644 --- a/src/eepp/ui/css/stylesheetspecification.cpp +++ b/src/eepp/ui/css/stylesheetspecification.cpp @@ -428,6 +428,9 @@ void StyleSheetSpecification::registerDefaultProperties() { registerProperty( "hidden", "" ).setType( PropertyType::Bool ); registerProperty( "display", "inline" ).setType( PropertyType::String ); registerProperty( "position", "static" ).setType( PropertyType::String ); + 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 ); @@ -516,6 +519,9 @@ void StyleSheetSpecification::registerDefaultProperties() { registerShorthand( "border-bottom", { "border-bottom-width", "border-bottom-style", "border-bottom-color" }, "border-side" ); + registerShorthand( "list-style", + { "list-style-type", "list-style-position", "list-style-image" }, + "list-style" ); } void StyleSheetSpecification::registerNodeSelector( const std::string& name, @@ -1135,6 +1141,40 @@ void StyleSheetSpecification::registerDefaultShorthandParsers() { return properties; }; + + mShorthandParsers["list-style"] = []( const ShorthandDefinition* shorthand, + std::string value ) -> std::vector { + value = String::trim( value ); + if ( value.empty() ) + return {}; + std::vector properties; + const std::vector& propNames = shorthand->getProperties(); + if ( propNames.empty() ) + return {}; + auto tokens = String::split( value, " ", "", "(" ); + int typePos = getIndexEndingWith( propNames, "-type" ); + int posPos = getIndexEndingWith( propNames, "-position" ); + int imagePos = getIndexEndingWith( propNames, "-image" ); + for ( auto& tok : tokens ) { + String::trimInPlace( tok ); + if ( tok == "inside" || tok == "outside" ) { + if ( posPos != -1 ) + properties.emplace_back( StyleSheetProperty( propNames[posPos], tok ) ); + } else if ( String::startsWith( tok, "url(" ) ) { + if ( imagePos != -1 ) + properties.emplace_back( StyleSheetProperty( propNames[imagePos], tok ) ); + } else if ( tok == "none" ) { + if ( typePos != -1 ) + properties.emplace_back( StyleSheetProperty( propNames[typePos], tok ) ); + if ( imagePos != -1 ) + properties.emplace_back( StyleSheetProperty( propNames[imagePos], tok ) ); + } else { + if ( typePos != -1 ) + properties.emplace_back( StyleSheetProperty( propNames[typePos], tok ) ); + } + } + return properties; + }; } }}} // namespace EE::UI::CSS diff --git a/src/eepp/ui/uihtmllistitem.cpp b/src/eepp/ui/uihtmllistitem.cpp new file mode 100644 index 000000000..914981bdf --- /dev/null +++ b/src/eepp/ui/uihtmllistitem.cpp @@ -0,0 +1,198 @@ +#include +#include +#include +#include + +namespace EE { namespace UI { + +UIHTMLListItem* UIHTMLListItem::New() { + return eeNew( UIHTMLListItem, () ); +} + +UIHTMLListItem::UIHTMLListItem() : UIRichText( "li" ) {} + +void UIHTMLListItem::setListStyleType( CSSListStyleType type ) { + if ( mListStyleType != type ) { + mListStyleType = type; + invalidateList(); + } +} + +void UIHTMLListItem::setListStylePosition( CSSListStylePosition pos ) { + if ( mListStylePosition != pos ) { + mListStylePosition = pos; + invalidateList(); + } +} + +Uint32 UIHTMLListItem::getType() const { + return UI_TYPE_HTML_LIST_ITEM; +} + +bool UIHTMLListItem::isType( const Uint32& type ) const { + return UIHTMLListItem::getType() == type ? true : UIRichText::isType( type ); +} + +void UIHTMLListItem::draw() { + UIRichText::draw(); + if ( mVisible && 0.f != mAlpha ) { + const FontStyleConfig& style = mRichText.getFontStyleConfig(); + Float fontSize = style.CharacterSize; + Float offset = 0.25f * fontSize; + Float lineHeight = + style.Font ? style.Font->getLineSpacing( (unsigned int)fontSize ) : fontSize; + Float lineTop = mScreenPos.y + mPaddingPx.Top; + + if ( mListStyleType == CSSListStyleType::Disc ) { + Float radius = fontSize * 0.22f; + Float markerX = std::floor( mScreenPos.x + mPaddingPx.Left - radius * 2.f - offset ); + Float markerY = std::floor( lineTop + ( lineHeight - radius * 2.f ) * 0.5f + radius ); + Primitives p; + p.setColor( style.FontColor ); + p.setFillMode( PrimitiveFillMode::DRAW_FILL ); + p.drawCircle( { markerX, markerY }, radius ); + } else if ( mListStyleType == CSSListStyleType::Circle ) { + Float radius = fontSize * 0.2f; + Float lineWidth = fontSize * 0.04f; + Float markerX = std::floor( mScreenPos.x + mPaddingPx.Left - radius * 2.f - offset ); + Float markerY = std::floor( lineTop + ( lineHeight - radius * 2.f ) * 0.5f + radius ); + Primitives p; + p.setColor( style.FontColor ); + p.setFillMode( PrimitiveFillMode::DRAW_LINE ); + p.setLineWidth( lineWidth ); + p.drawCircle( { markerX, markerY }, radius ); + } else if ( mListStyleType == CSSListStyleType::Square ) { + Float size = fontSize * 0.38f; + Float markerX = std::floor( mScreenPos.x + mPaddingPx.Left - size - fontSize * 0.5 ); + Float markerY = std::floor( lineTop + ( lineHeight - size ) * 0.5f ); + Primitives p; + p.setColor( style.FontColor ); + p.setFillMode( PrimitiveFillMode::DRAW_FILL ); + p.drawRectangle( Rectf( markerX, markerY, markerX + size, markerY + size ) ); + } else if ( mListMarkerText && !mListMarkerText->getString().empty() ) { + Float markerX = + mScreenPos.x + mPaddingPx.Left - mListMarkerText->getTextWidth() - offset; + mListMarkerText->draw( markerX, lineTop, Vector2f::One, 0.f, getBlendMode() ); + } + } +} + +bool UIHTMLListItem::applyProperty( const StyleSheetProperty& attribute ) { + if ( !checkPropertyDefinition( attribute ) ) + return false; + + switch ( attribute.getPropertyDefinition()->getPropertyId() ) { + case PropertyId::ListStyleType: + setListStyleType( CSSListStyleTypeHelper::fromString( attribute.value() ) ); + return true; + case PropertyId::ListStylePosition: + setListStylePosition( CSSListStylePositionHelper::fromString( attribute.value() ) ); + return true; + default: + return UIRichText::applyProperty( attribute ); + } +} + +std::string UIHTMLListItem::getPropertyString( const PropertyDefinition* propertyDef, + const Uint32& propertyIndex ) const { + if ( NULL == propertyDef ) + return ""; + + switch ( propertyDef->getPropertyId() ) { + case PropertyId::ListStyleType: + return CSSListStyleTypeHelper::toString( mListStyleType ); + case PropertyId::ListStylePosition: + return CSSListStylePositionHelper::toString( mListStylePosition ); + default: + return UIRichText::getPropertyString( propertyDef, propertyIndex ); + } +} + +std::vector UIHTMLListItem::getPropertiesImplemented() const { + auto props = UIRichText::getPropertiesImplemented(); + props.push_back( PropertyId::ListStyleType ); + props.push_back( PropertyId::ListStylePosition ); + return props; +} + +void UIHTMLListItem::invalidateList() { + if ( mListStyleType == CSSListStyleType::None || mListStyleType == CSSListStyleType::Disc || + mListStyleType == CSSListStyleType::Circle || + mListStyleType == CSSListStyleType::Square ) { + mListMarkerText.reset(); + } else { + String::View markerStr = getListMarkerString(); + if ( !markerStr.empty() ) { + if ( !mListMarkerText ) + mListMarkerText = std::make_unique(); + mListMarkerText->setString( markerStr ); + mListMarkerText->setStyleConfig( mRichText.getFontStyleConfig() ); + } else { + mListMarkerText.reset(); + } + } + invalidateDraw(); +} + +int UIHTMLListItem::countPrecedingLiSiblings() const { + int count = 0; + Node* prev = getPrevNode(); + while ( prev ) { + if ( prev->isWidget() && prev->asType()->getElementTag() == "li" ) + count++; + prev = prev->getPrevNode(); + } + return count; +} + +String::View UIHTMLListItem::getListMarkerString() const { + static String sBuf; + + switch ( mListStyleType ) { + case CSSListStyleType::None: + case CSSListStyleType::Disc: + case CSSListStyleType::Circle: + case CSSListStyleType::Square: + return {}; + case CSSListStyleType::Decimal: { + int idx = countPrecedingLiSiblings() + 1; + sBuf = String( String::toString( idx ) + ". " ); + return sBuf.view(); + } + case CSSListStyleType::LowerAlpha: { + int idx = countPrecedingLiSiblings(); + char c = 'a' + ( idx % 26 ); + sBuf = String( 1, (String::StringBaseType)c ) + ". "; + return sBuf.view(); + } + case CSSListStyleType::UpperAlpha: { + int idx = countPrecedingLiSiblings(); + char c = 'A' + ( idx % 26 ); + sBuf = String( 1, (String::StringBaseType)c ) + ". "; + return sBuf.view(); + } + case CSSListStyleType::LowerRoman: { + static const char* numerals[] = { "i", "ii", "iii", "iv", "v", "vi", + "vii", "viii", "ix", "x", "xi", "xii" }; + int idx = countPrecedingLiSiblings(); + if ( idx < 12 ) + sBuf = String( numerals[idx] ) + ". "; + else + sBuf = String( String::toString( idx + 1 ) + ". " ); + return sBuf.view(); + } + case CSSListStyleType::UpperRoman: { + static const char* numerals[] = { "I", "II", "III", "IV", "V", "VI", + "VII", "VIII", "IX", "X", "XI", "XII" }; + int idx = countPrecedingLiSiblings(); + if ( idx < 12 ) + sBuf = String( numerals[idx] ) + ". "; + else + sBuf = String( String::toString( idx + 1 ) + ". " ); + return sBuf.view(); + } + } + return {}; +} + +}} // namespace EE::UI diff --git a/src/eepp/ui/uiwidgetcreator.cpp b/src/eepp/ui/uiwidgetcreator.cpp index 202c6ba6a..b0afb6221 100644 --- a/src/eepp/ui/uiwidgetcreator.cpp +++ b/src/eepp/ui/uiwidgetcreator.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -158,7 +159,7 @@ void UIWidgetCreator::createBaseWidgetList() { registeredWidget["hr"] = UIRichText::NewHr; registeredWidget["ul"] = [] { return UILinearLayout::NewVerticalWidthMatchParent( "ul" ); }; registeredWidget["ol"] = [] { return UILinearLayout::NewVerticalWidthMatchParent( "ol" ); }; - registeredWidget["li"] = UIRichText::NewListItem; + registeredWidget["li"] = UIHTMLListItem::New; registeredWidget["pre"] = UIRichText::NewPre; registeredWidget["img"] = [] { auto img = UIImage::NewWithTag( "img" ); diff --git a/src/tests/unit_tests/uihtml_tests.cpp b/src/tests/unit_tests/uihtml_tests.cpp index 5b5443393..997d0629b 100644 --- a/src/tests/unit_tests/uihtml_tests.cpp +++ b/src/tests/unit_tests/uihtml_tests.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -570,3 +571,157 @@ UTEST( UILayout, marginAuto ) { Engine::destroySingleton(); } + +UTEST( UILayout, listStyleTypeDecimal ) { + init_ui_test(); + auto* sceneNode = SceneManager::instance()->getUISceneNode(); + sceneNode->loadLayoutFromString( R"html( + +
    +
  1. First item
  2. +
  3. Second item
  4. +
  5. Third item
  6. +
+ + )html" ); + + sceneNode->updateDirtyLayouts(); + + const auto* propDef = + StyleSheetSpecification::instance()->getProperty( "list-style-type" ); + ASSERT_TRUE( propDef != nullptr ); + + auto* li1 = sceneNode->getRoot()->find( "li1" )->asType(); + auto* li2 = sceneNode->getRoot()->find( "li2" )->asType(); + auto* li3 = sceneNode->getRoot()->find( "li3" )->asType(); + + ASSERT_TRUE( li1 != nullptr ); + ASSERT_TRUE( li2 != nullptr ); + ASSERT_TRUE( li3 != nullptr ); + + EXPECT_TRUE( li1->getPropertyString( propDef ) == "decimal" ); + EXPECT_TRUE( li2->getPropertyString( propDef ) == "decimal" ); + EXPECT_TRUE( li3->getPropertyString( propDef ) == "decimal" ); + + Engine::destroySingleton(); +} + +UTEST( UILayout, listStyleTypeDisc ) { + init_ui_test(); + auto* sceneNode = SceneManager::instance()->getUISceneNode(); + sceneNode->loadLayoutFromString( R"html( + +
    +
  • Bullet item
  • +
+ + )html" ); + + sceneNode->updateDirtyLayouts(); + + const auto* propDef = + StyleSheetSpecification::instance()->getProperty( "list-style-type" ); + ASSERT_TRUE( propDef != nullptr ); + + auto* li1 = sceneNode->getRoot()->find( "li1" )->asType(); + ASSERT_TRUE( li1 != nullptr ); + + EXPECT_TRUE( li1->getPropertyString( propDef ) == "disc" ); + + Engine::destroySingleton(); +} + +UTEST( UILayout, listStyleShorthand ) { + init_ui_test(); + auto* sceneNode = SceneManager::instance()->getUISceneNode(); + sceneNode->loadLayoutFromString( R"html( + +
    +
  1. First
  2. +
  3. Second
  4. +
  5. Third
  6. +
+
    +
  • Bullet
  • +
  • Square
  • +
  • Circle
  • +
+ + )html" ); + + sceneNode->updateDirtyLayouts(); + + const auto* typeDef = + StyleSheetSpecification::instance()->getProperty( "list-style-type" ); + const auto* posDef = + StyleSheetSpecification::instance()->getProperty( "list-style-position" ); + + for ( const char* id : { "li1", "li2", "li3", "li4", "li5", "li6" } ) { + auto* li = sceneNode->getRoot()->find( id )->asType(); + ASSERT_TRUE( li != nullptr ); + EXPECT_TRUE( li->isType( UI_TYPE_HTML_LIST_ITEM ) ); + } + + EXPECT_TRUE( sceneNode->getRoot()->find( "li1" )->asType()->getPropertyString( typeDef ) == "decimal" ); + EXPECT_TRUE( sceneNode->getRoot()->find( "li1" )->asType()->getPropertyString( posDef ) == "outside" ); + + EXPECT_TRUE( sceneNode->getRoot()->find( "li2" )->asType()->getPropertyString( typeDef ) == "lower-alpha" ); + EXPECT_TRUE( sceneNode->getRoot()->find( "li2" )->asType()->getPropertyString( posDef ) == "inside" ); + + EXPECT_TRUE( sceneNode->getRoot()->find( "li3" )->asType()->getPropertyString( typeDef ) == "none" ); + + EXPECT_TRUE( sceneNode->getRoot()->find( "li4" )->asType()->getPropertyString( typeDef ) == "disc" ); + EXPECT_TRUE( sceneNode->getRoot()->find( "li5" )->asType()->getPropertyString( typeDef ) == "square" ); + EXPECT_TRUE( sceneNode->getRoot()->find( "li5" )->asType()->getPropertyString( posDef ) == "outside" ); + EXPECT_TRUE( sceneNode->getRoot()->find( "li6" )->asType()->getPropertyString( typeDef ) == "circle" ); + + Engine::destroySingleton(); +} + +UTEST( UILayout, listStyleInheritanceFromUl ) { + init_ui_test(); + auto* sceneNode = SceneManager::instance()->getUISceneNode(); + sceneNode->loadLayoutFromString( R"html( + + + + + +
    +
  1. Coffee
  2. +
+
    +
  • Coffee
  • +
+
    +
  • Coffee
  • +
+
    +
  • Coffee
  • +
+
    +
  1. Coffee
  2. +
+ + + )html" ); + + sceneNode->updateDirtyLayouts(); + + const auto* typeDef = + StyleSheetSpecification::instance()->getProperty( "list-style-type" ); + + EXPECT_TRUE( sceneNode->getRoot()->find( "h1" )->asType()->getPropertyString( typeDef ) == "upper-roman" ); + EXPECT_TRUE( sceneNode->getRoot()->find( "a1" )->asType()->getPropertyString( typeDef ) == "circle" ); + EXPECT_TRUE( sceneNode->getRoot()->find( "b1" )->asType()->getPropertyString( typeDef ) == "disc" ); + EXPECT_TRUE( sceneNode->getRoot()->find( "c1" )->asType()->getPropertyString( typeDef ) == "square" ); + EXPECT_TRUE( sceneNode->getRoot()->find( "d1" )->asType()->getPropertyString( typeDef ) == "decimal" ); + + Engine::destroySingleton(); +}