From 10fa8f7411fddc628eba78598be6e15f45efd243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sat, 9 May 2026 02:22:06 -0300 Subject: [PATCH] Fix `ex` and `ch` units. --- include/eepp/ui/css/stylesheetlength.hpp | 10 ++- src/eepp/ui/css/stylesheetlength.cpp | 29 +++++- src/eepp/ui/uinode.cpp | 11 ++- .../unit_tests/uicss_inheritance_tests.cpp | 89 +++++++++++++++++++ 4 files changed, 131 insertions(+), 8 deletions(-) diff --git a/include/eepp/ui/css/stylesheetlength.hpp b/include/eepp/ui/css/stylesheetlength.hpp index 29c040e8f..ee832d5c6 100644 --- a/include/eepp/ui/css/stylesheetlength.hpp +++ b/include/eepp/ui/css/stylesheetlength.hpp @@ -6,6 +6,10 @@ #include #include +namespace EE { namespace Graphics { +class Font; +}} // namespace EE::Graphics + using namespace EE::Math; namespace EE { namespace UI { namespace CSS { @@ -62,10 +66,12 @@ class EE_API StyleSheetLength { const Unit& getUnit() const; Float asPixels( const Float& parentSize, const Sizef& viewSize, const Float& displayDpi, - const Float& elFontSize = 12, const Float& globalFontSize = 12 ) const; + const Float& elFontSize = 12, const Float& globalFontSize = 12, + Graphics::Font* font = nullptr ) const; Float asDp( const Float& parentSize, const Sizef& viewSize, const Float& displayDpi, - const Float& elFontSize = 12, const Float& globalFontSize = 12 ) const; + const Float& elFontSize = 12, const Float& globalFontSize = 12, + Graphics::Font* font = nullptr ) const; bool operator==( const StyleSheetLength& val ) const; diff --git a/src/eepp/ui/css/stylesheetlength.cpp b/src/eepp/ui/css/stylesheetlength.cpp index d12b5fc2d..91ea2834d 100644 --- a/src/eepp/ui/css/stylesheetlength.cpp +++ b/src/eepp/ui/css/stylesheetlength.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -220,7 +221,7 @@ const StyleSheetLength::Unit& StyleSheetLength::getUnit() const { Float StyleSheetLength::asPixels( const Float& parentSize, const Sizef& viewSize, const Float& displayDpi, const Float& elFontSize, - const Float& globalFontSize ) const { + const Float& globalFontSize, Graphics::Font* font ) const { Float ret = 0; // CSS dictates a base 96 DPI for logical pixels. @@ -243,9 +244,29 @@ Float StyleSheetLength::asPixels( const Float& parentSize, const Sizef& viewSize ret = Math::roundUp( PixelDensity::dpToPx( mValue ) ); break; case Unit::Em: - case Unit::Ch: // Using Em for Ch is incorrect but not that incorrect, close enough ret = Math::round( mValue * elFontSize ); break; + case Unit::Ex: + if ( font && elFontSize > 0 ) { + Glyph glyph = + font->getGlyph( 'x', static_cast( elFontSize ), false, false ); + Float xHeight = eemax( 0.f, -glyph.bounds.Top ); + if ( xHeight <= 0 ) + xHeight = elFontSize * 0.5f; + ret = Math::round( mValue * xHeight ); + } else { + ret = Math::round( mValue * elFontSize * 0.5f ); + } + break; + case Unit::Ch: + if ( font && elFontSize > 0 ) { + Glyph glyph = + font->getGlyph( '0', static_cast( elFontSize ), false, false ); + ret = Math::round( mValue * eemax( 0.f, glyph.advance ) ); + } else { + ret = Math::round( mValue * elFontSize * 0.5f ); + } + break; case Unit::Pt: ret = mValue * CSS_DPI / 72.f; break; @@ -290,9 +311,9 @@ Float StyleSheetLength::asPixels( const Float& parentSize, const Sizef& viewSize Float StyleSheetLength::asDp( const Float& parentSize, const Sizef& viewSize, const Float& displayDpi, const Float& elFontSize, - const Float& globalFontSize ) const { + const Float& globalFontSize, Graphics::Font* font ) const { return PixelDensity::pxToDp( - asPixels( parentSize, viewSize, displayDpi, elFontSize, globalFontSize ) ); + asPixels( parentSize, viewSize, displayDpi, elFontSize, globalFontSize, font ) ); } bool StyleSheetLength::operator==( const StyleSheetLength& length ) const { diff --git a/src/eepp/ui/uinode.cpp b/src/eepp/ui/uinode.cpp index 4b18facd5..3e9ac28ec 100644 --- a/src/eepp/ui/uinode.cpp +++ b/src/eepp/ui/uinode.cpp @@ -1796,12 +1796,19 @@ Float UINode::convertLength( const CSS::StyleSheetLength& length, } else { rootFontSize = getAbsoluteFontSize( getUISceneNode()->getRoot() ); } - } else if ( length.getUnit() == StyleSheetLength::Unit::Em && isWidget() ) { + } else if ( ( length.getUnit() == StyleSheetLength::Unit::Em || + length.getUnit() == StyleSheetLength::Unit::Ex || + length.getUnit() == StyleSheetLength::Unit::Ch ) && + isWidget() ) { elFontSize = getAbsoluteFontSize( asConstType() ); } + Graphics::Font* font = nullptr; + if ( getUISceneNode() && getUISceneNode()->getUIThemeManager() ) + font = getUISceneNode()->getUIThemeManager()->getDefaultFont(); + auto ret = length.asPixels( containerLength, getSceneNode()->getPixelsSize(), - getSceneNode()->getDPI(), elFontSize, rootFontSize ); + getSceneNode()->getDPI(), elFontSize, rootFontSize, font ); if ( ( mFlags & UI_HTML_ELEMENT ) && length.getUnit() == StyleSheetLength::Unit::Px ) ret = PixelDensity::dpToPx( ret ); // scale px as if where dp in HTML elements diff --git a/src/tests/unit_tests/uicss_inheritance_tests.cpp b/src/tests/unit_tests/uicss_inheritance_tests.cpp index be710d434..8355d9241 100644 --- a/src/tests/unit_tests/uicss_inheritance_tests.cpp +++ b/src/tests/unit_tests/uicss_inheritance_tests.cpp @@ -1,8 +1,10 @@ #include "utest.hpp" +#include #include #include #include #include +#include #include #include #include @@ -11,12 +13,14 @@ #include #include #include +#include #include using namespace EE; using namespace EE::UI; using namespace EE::UI::CSS; using namespace EE::Scene; +using namespace EE::Graphics; UTEST( CSSInheritance, HtmlXmlLoadingInheritance ) { UIApplication app( @@ -331,3 +335,88 @@ UTEST( CSSInheritance, ExplicitBackgroundColorInherit ) { EXPECT_TRUE( Color( "#00FF00" ) == child->getBackgroundColor() ); } + +UTEST( CSSUnits, ExFallback ) { + StyleSheetLength len( "100ex" ); + Float result = len.asPixels( 0, Sizef::Zero, 96, 16, 16 ); + EXPECT_EQ( Math::round( 100.f * 16.f * 0.5f ), result ); +} + +UTEST( CSSUnits, ChFallback ) { + StyleSheetLength len( "100ch" ); + Float result = len.asPixels( 0, Sizef::Zero, 96, 16, 16 ); + EXPECT_EQ( Math::round( 100.f * 16.f * 0.5f ), result ); +} + +UTEST( CSSUnits, ExChWithFont ) { + UIApplication app( + WindowSettings( 800, 600, "eepp - CSS Units Ex/Ch Test", WindowStyle::Default, + WindowBackend::Default, 32 ), + UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash() ), 1.f ); + + Graphics::Font* font = + app.getUI()->getUIThemeManager()->getDefaultFont(); + EXPECT_TRUE( font != nullptr ); + + Float elFontSize = 24.f; + + Float resultEx = StyleSheetLength( "1ex" ).asPixels( 0, Sizef::Zero, 96, elFontSize, 16, font ); + Float resultCh = StyleSheetLength( "1ch" ).asPixels( 0, Sizef::Zero, 96, elFontSize, 16, font ); + Float resultEm = StyleSheetLength( "1em" ).asPixels( 0, Sizef::Zero, 96, elFontSize, 16, font ); + Float fallback = Math::round( elFontSize * 0.5f ); + + EXPECT_GT( resultEx, 0.f ); + EXPECT_GT( resultCh, 0.f ); + EXPECT_EQ( elFontSize, resultEm ); + EXPECT_LE( resultEx, resultEm ); + EXPECT_LE( resultCh, resultEm ); + EXPECT_NE( fallback, resultEx ); + 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 ) ); + + std::string xml = R"( + + + + + +
x
+
0
+ + + )"; + + 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* chbox = root->querySelector( "#chbox" )->asType(); + EXPECT_TRUE( chbox != nullptr ); + EXPECT_GT( chbox->getPixelsSize().getWidth(), 0.f ); + EXPECT_GT( chbox->getPixelsSize().getHeight(), 0.f ); + } +}