Fix ex and ch units.

This commit is contained in:
Martín Lucas Golini
2026-05-09 02:22:06 -03:00
parent f4f30c9979
commit 10fa8f7411
4 changed files with 131 additions and 8 deletions

View File

@@ -6,6 +6,10 @@
#include <eepp/scene/scenenode.hpp>
#include <string>
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;

View File

@@ -1,4 +1,5 @@
#include <eepp/core/string.hpp>
#include <eepp/graphics/font.hpp>
#include <eepp/graphics/pixeldensity.hpp>
#include <eepp/math/math.hpp>
#include <eepp/system/luapattern.hpp>
@@ -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<unsigned int>( 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<unsigned int>( 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 {

View File

@@ -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<UIWidget>() );
}
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

View File

@@ -1,8 +1,10 @@
#include "utest.hpp"
#include <eepp/graphics/font.hpp>
#include <eepp/graphics/fontmanager.hpp>
#include <eepp/scene/node.hpp>
#include <eepp/system/filesystem.hpp>
#include <eepp/ui/css/stylesheet.hpp>
#include <eepp/ui/css/stylesheetlength.hpp>
#include <eepp/ui/css/stylesheetproperty.hpp>
#include <eepp/ui/css/stylesheetspecification.hpp>
#include <eepp/ui/uiapplication.hpp>
@@ -11,12 +13,14 @@
#include <eepp/ui/uistyle.hpp>
#include <eepp/ui/uitextspan.hpp>
#include <eepp/ui/uitextview.hpp>
#include <eepp/ui/uithememanager.hpp>
#include <eepp/ui/uiwidget.hpp>
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"(
<html>
<head>
<style>
.text {
font-size: 20px;
}
#exbox {
width: 10ex;
height: 2ex;
}
#chbox {
width: 10ch;
height: 2ch;
}
</style>
</head>
<body>
<div id="exbox" class="text">x</div>
<div id="chbox" class="text">0</div>
</body>
</html>
)";
UIWidget* root = app.getUI()->loadLayoutFromString( xml );
EXPECT_TRUE( root != nullptr );
UIRichText* exbox = root->querySelector( "#exbox" )->asType<UIRichText>();
EXPECT_TRUE( exbox != nullptr );
EXPECT_GT( exbox->getPixelsSize().getWidth(), 0.f );
EXPECT_GT( exbox->getPixelsSize().getHeight(), 0.f );
UIRichText* chbox = root->querySelector( "#chbox" )->asType<UIRichText>();
EXPECT_TRUE( chbox != nullptr );
EXPECT_GT( chbox->getPixelsSize().getWidth(), 0.f );
EXPECT_GT( chbox->getPixelsSize().getHeight(), 0.f );
}
}