From 0f8d930e80b738353ade2c2dbb75bf6660afff15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Mon, 11 May 2026 21:22:49 -0300 Subject: [PATCH] Add support for `--var: var(--other-var)`. Reset global definition for body and html elements when new styles are loaded (required when these elements have only variable definitions). --- include/eepp/ui/css/elementdefinition.hpp | 2 + src/eepp/ui/css/elementdefinition.cpp | 62 +++++- src/eepp/ui/uiscenenode.cpp | 8 + .../unit_tests/uicss_inheritance_tests.cpp | 182 ++++++++++++++---- 4 files changed, 219 insertions(+), 35 deletions(-) diff --git a/include/eepp/ui/css/elementdefinition.hpp b/include/eepp/ui/css/elementdefinition.hpp index 32c274ee1..a79419c5b 100644 --- a/include/eepp/ui/css/elementdefinition.hpp +++ b/include/eepp/ui/css/elementdefinition.hpp @@ -40,6 +40,8 @@ class EE_API ElementDefinition : NonCopyable { bool mStructurallyVolatile; void findVariables( const CSS::StyleSheetStyle* style ); + + void resolveVariables(); }; }}} // namespace EE::UI::CSS diff --git a/src/eepp/ui/css/elementdefinition.cpp b/src/eepp/ui/css/elementdefinition.cpp index eda4a15b7..4b9f65bcf 100644 --- a/src/eepp/ui/css/elementdefinition.cpp +++ b/src/eepp/ui/css/elementdefinition.cpp @@ -1,3 +1,4 @@ +#include #include namespace EE { namespace UI { namespace CSS { @@ -70,6 +71,8 @@ void ElementDefinition::refresh() { findVariables( styleSheetStyle ); } + resolveVariables(); + for ( auto& property : mProperties ) mPropertyIds.insert( property.first ); } @@ -78,11 +81,68 @@ void ElementDefinition::findVariables( const StyleSheetStyle* style ) { for ( const auto& vars : style->getVariables() ) { const StyleSheetVariable& variable = vars.second; const auto& it = mVariables.find( variable.getNameHash() ); - if ( it == mVariables.end() || variable.getSpecificity() >= it->second.getSpecificity() ) { mVariables[variable.getNameHash()] = variable; } } } +static void resolveVarValue( std::string& value, const StyleSheetVariables& variables, + UnorderedSet& visited, int depth = 0 ) { + static constexpr int maxDepth = 32; + if ( depth > maxDepth ) + return; + + std::string::size_type tokenStart = 0; + + while ( true ) { + tokenStart = value.find( "var(", tokenStart ); + if ( tokenStart == std::string::npos ) + return; + + std::string::size_type tokenEnd = String::findCloseBracket( value, tokenStart, '(', ')' ); + if ( tokenEnd == std::string::npos ) + return; + + std::string varDef( value, tokenStart, tokenEnd + 1 - tokenStart ); + System::FunctionString functionType = System::FunctionString::parse( varDef ); + + if ( functionType.getParameters().empty() ) { + tokenStart = tokenEnd; + continue; + } + + bool resolved = false; + for ( auto& param : functionType.getParameters() ) { + if ( String::startsWith( param, "--" ) ) { + auto it = variables.find( String::hash( param ) ); + if ( it != variables.end() && + visited.find( it->second.getNameHash() ) == visited.end() ) { + visited.insert( it->second.getNameHash() ); + std::string deepValue( it->second.getValue() ); + resolveVarValue( deepValue, variables, visited, depth + 1 ); + String::replaceAll( value, varDef, deepValue ); + resolved = true; + break; + } + } + } + + if ( resolved ) + tokenStart = 0; + else + tokenStart = tokenEnd; + } +} + +void ElementDefinition::resolveVariables() { + static UnorderedSet visited; + for ( auto& varPair : mVariables ) { + std::string value( varPair.second.getValue() ); + visited.clear(); + resolveVarValue( value, mVariables, visited ); + varPair.second.setValue( value ); + } +} + }}} // namespace EE::UI::CSS diff --git a/src/eepp/ui/uiscenenode.cpp b/src/eepp/ui/uiscenenode.cpp index a4169c07f..ac8d46d4f 100644 --- a/src/eepp/ui/uiscenenode.cpp +++ b/src/eepp/ui/uiscenenode.cpp @@ -422,6 +422,14 @@ void UISceneNode::updateStyleSheet( bool forceReloadStyle ) { if ( mRoot && mRoot->getUIStyle() ) mRoot->getUIStyle()->resetGlobalDefinition(); + auto bodies = mRoot->findAllByType( UI_TYPE_HTML_BODY ); + for ( auto body : bodies ) + body->asType()->getUIStyle()->resetGlobalDefinition(); + + auto htmls = mRoot->findAllByType( UI_TYPE_HTML_HTML ); + for ( auto html : htmls ) + html->asType()->getUIStyle()->resetGlobalDefinition(); + if ( mRoot && mediaChanged ) mRoot->reportStyleStateChangeRecursive( false, false ); diff --git a/src/tests/unit_tests/uicss_inheritance_tests.cpp b/src/tests/unit_tests/uicss_inheritance_tests.cpp index 8355d9241..780d7e4de 100644 --- a/src/tests/unit_tests/uicss_inheritance_tests.cpp +++ b/src/tests/unit_tests/uicss_inheritance_tests.cpp @@ -1,6 +1,7 @@ #include "utest.hpp" #include #include +#include #include #include #include @@ -354,8 +355,7 @@ UTEST( CSSUnits, ExChWithFont ) { WindowBackend::Default, 32 ), UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash() ), 1.f ); - Graphics::Font* font = - app.getUI()->getUIThemeManager()->getDefaultFont(); + Graphics::Font* font = app.getUI()->getUIThemeManager()->getDefaultFont(); EXPECT_TRUE( font != nullptr ); Float elFontSize = 24.f; @@ -374,49 +374,163 @@ UTEST( CSSUnits, ExChWithFont ) { EXPECT_NE( fallback, resultCh ); } -UTEST( CSSUnits, Integration ) { - for ( Float scale : { 1.f, 1.5f, 2.f } ) { - UTEST_PRINT_STEP( String::format( "SCALE %.1f", scale ).c_str() ); - UIApplication app( WindowSettings( 800, 600, "eepp - CSS Units Ex/Ch Integration Test", - WindowStyle::Default, WindowBackend::Default, 32 ), - UIApplication::Settings( - Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), scale ) ); +UTEST( CSSVariables, VariableReferencesSimple ) { + UIApplication app( + WindowSettings( 800, 600, "eepp - CSS Var Ref Test 1", WindowStyle::Default, + WindowBackend::Default, 32 ), + UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash() ), 1 ); - std::string xml = R"( + std::string xml = R"( -
x
-
0
+
Test text
- )"; + )"; - UIWidget* root = app.getUI()->loadLayoutFromString( xml ); - EXPECT_TRUE( root != nullptr ); + UIWidget* root = app.getUI()->loadLayoutFromString( xml ); + EXPECT_TRUE( root != nullptr ); - UIRichText* exbox = root->querySelector( "#exbox" )->asType(); - EXPECT_TRUE( exbox != nullptr ); - EXPECT_GT( exbox->getPixelsSize().getWidth(), 0.f ); - EXPECT_GT( exbox->getPixelsSize().getHeight(), 0.f ); + UIRichText* div = root->querySelector( "#testdiv" )->asType(); + EXPECT_TRUE( div != nullptr ); - UIRichText* chbox = root->querySelector( "#chbox" )->asType(); - EXPECT_TRUE( chbox != nullptr ); - EXPECT_GT( chbox->getPixelsSize().getWidth(), 0.f ); - EXPECT_GT( chbox->getPixelsSize().getHeight(), 0.f ); - } + EXPECT_TRUE( Color( "#FF0000" ) == div->getFontColor() ); +} + +UTEST( CSSVariables, VariableReferencesChain ) { + UIApplication app( + WindowSettings( 800, 600, "eepp - CSS Var Ref Test 2", WindowStyle::Default, + WindowBackend::Default, 32 ), + UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash() ), 1 ); + + std::string xml = R"( + + + + + +
Test text
+ + + )"; + + UIWidget* root = app.getUI()->loadLayoutFromString( xml ); + EXPECT_TRUE( root != nullptr ); + + UIRichText* div = root->querySelector( "#testdiv" )->asType(); + EXPECT_TRUE( div != nullptr ); + + EXPECT_TRUE( Color( "#00FF00" ) == div->getFontColor() ); +} + +UTEST( CSSVariables, VariableReferencesWithPadding ) { + UIApplication app( + WindowSettings( 800, 600, "eepp - CSS Var Ref Test 3", WindowStyle::Default, + WindowBackend::Default, 32 ), + UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash() ), 1 ); + + std::string xml = R"( + + + + + +
Test text
+ + + )"; + + UIWidget* root = app.getUI()->loadLayoutFromString( xml ); + EXPECT_TRUE( root != nullptr ); + + UIWidget* div = root->querySelector( "#testdiv" ); + EXPECT_TRUE( div != nullptr ); + + Float padding = PixelDensity::dpToPx( div->getPadding().Left ); + EXPECT_NEAR( 20.f, padding, 1.f ); +} + +UTEST( CSSVariables, VariableReferencesMultiple ) { + UIApplication app( + WindowSettings( 800, 600, "eepp - CSS Var Ref Test 4", WindowStyle::Default, + WindowBackend::Default, 32 ), + UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash() ), 1 ); + + std::string xml = R"( + + + + + +
Test text
+ + + )"; + + UIWidget* root = app.getUI()->loadLayoutFromString( xml ); + EXPECT_TRUE( root != nullptr ); + + UIRichText* div = root->querySelector( "#testdiv" )->asType(); + EXPECT_TRUE( div != nullptr ); + + EXPECT_TRUE( Color( "#0000FF" ) == div->getFontColor() ); +} + +UTEST( CSSVariables, VariableReferencesCircular ) { + UIApplication app( + WindowSettings( 800, 600, "eepp - CSS Var Ref Test 5", WindowStyle::Default, + WindowBackend::Default, 32 ), + UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash() ), 1 ); + + std::string xml = R"( + + + + + +
Test text
+ + + )"; + + UIWidget* root = app.getUI()->loadLayoutFromString( xml ); + EXPECT_TRUE( root != nullptr ); + + UIRichText* div = root->querySelector( "#testdiv" )->asType(); + EXPECT_TRUE( div != nullptr ); }