From dbde511d9965e80316a37f4f7710a1db08cf14f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Mon, 6 Apr 2026 02:39:14 -0300 Subject: [PATCH] Fix UIRichText children sizing, take into account padding. --- .ecode/project_build.json | 2 +- src/eepp/ui/uirichtext.cpp | 12 +++-- src/examples/ui_html/ui_html.cpp | 80 +++++++++++++++++++++++++++++-- src/tests/unit_tests/richtext.cpp | 41 ++++++++++++++++ 4 files changed, 125 insertions(+), 10 deletions(-) diff --git a/.ecode/project_build.json b/.ecode/project_build.json index cc8afc1fa..f354fe70f 100644 --- a/.ecode/project_build.json +++ b/.ecode/project_build.json @@ -377,7 +377,7 @@ "working_dir": "${project_root}/bin" }, { - "args": "", + "args": "-c system --hn-dark", "command": "${project_root}/bin/eepp-ui-html-debug", "name": "eepp-ui-html-debug", "working_dir": "${project_root}/bin" diff --git a/src/eepp/ui/uirichtext.cpp b/src/eepp/ui/uirichtext.cpp index 785570bed..73f4c6649 100644 --- a/src/eepp/ui/uirichtext.cpp +++ b/src/eepp/ui/uirichtext.cpp @@ -478,11 +478,12 @@ void UIRichText::rebuildRichText( RichText& richText, IntrinsicMode mode ) { Float maxWidth = mSize.getWidth() - mPaddingPx.Left - mPaddingPx.Right; if ( maxWidth < 0 ) maxWidth = 0; - + Float mw = 0.f; if ( !mMaxWidthEq.empty() ) { mw = getMaxSizePx().getWidth() - mPaddingPx.Left - mPaddingPx.Right; - if ( mw < 0 ) mw = 0.f; + if ( mw < 0 ) + mw = 0.f; } if ( mWidthPolicy == SizePolicy::WrapContent || mode != IntrinsicMode::None ) { @@ -521,7 +522,9 @@ void UIRichText::rebuildRichText( RichText& richText, IntrinsicMode mode ) { if ( mode == IntrinsicMode::None ) { if ( widget->getLayoutWidthPolicy() == SizePolicy::MatchParent ) { if ( mSize.getWidth() != 0 ) { - widget->setPixelsSize( mSize.getWidth() - margin.Left - margin.Right, + widget->setPixelsSize( eemax( 0.f, mSize.getWidth() - mPaddingPx.Left - + mPaddingPx.Right - margin.Left - + margin.Right ), widget->getPixelsSize().getHeight() ); } else { onAutoSizeChild( widget ); @@ -661,7 +664,8 @@ void UIRichText::positionChildren() { pos.y += widget->getPrevNode()->getPixelsSize().getHeight(); } widget->setPixelsPosition( pos ); - widget->setPixelsSize( { mSize.getWidth(), 0 } ); + widget->setPixelsSize( + { eemax( 0.f, mSize.getWidth() - mPaddingPx.Left - mPaddingPx.Right ), 0 } ); } else { curCharIdx += 1; const auto* span = getNextCustomSpan(); diff --git a/src/examples/ui_html/ui_html.cpp b/src/examples/ui_html/ui_html.cpp index 87d96cb03..fe88b886e 100644 --- a/src/examples/ui_html/ui_html.cpp +++ b/src/examples/ui_html/ui_html.cpp @@ -1,6 +1,33 @@ #include -EE_MAIN_FUNC int main( int, char** ) { +#include +#include + +EE_MAIN_FUNC int main( int argc, char** argv ) { + args::ArgumentParser parser( "eepp HTML Example" ); + args::HelpFlag help( parser, "help", "Display this help menu", { 'h', "help" } ); + args::Flag hnDark( parser, "hn-dark", + "Force a custom CSS style for Hacker News site to be dark.", { "hn-dark" } ); + args::ValueFlag prefersColorScheme( + parser, "prefers-color-scheme", + "Set the preferred color scheme (\"light\", \"dark\" or \"system\")", + { 'c', "prefers-color-scheme" } ); + + try { + parser.ParseCLI( Sys::parseArguments( argc, argv ) ); + } catch ( const args::Help& ) { + std::cout << parser; + return EXIT_SUCCESS; + } catch ( const args::ParseError& e ) { + std::cerr << e.what() << std::endl; + std::cerr << parser; + return EXIT_FAILURE; + } catch ( args::ValidationError& e ) { + std::cerr << e.what() << std::endl; + std::cerr << parser; + return EXIT_FAILURE; + } + UIApplication app( { 1280, 720, "eepp - UI HTML Example" } ); Log::instance()->setLogLevelThreshold( LogLevel::Debug ); @@ -19,7 +46,12 @@ EE_MAIN_FUNC int main( int, char** ) { ui->getUIIconThemeManager()->setCurrentTheme( IconManager::init( "icons", remixIconFont, noniconsFont, codIconFont ) ); - ui->setColorSchemePreference( ColorSchemeExtPreference::Light ); + ui->setColorSchemePreference( + !prefersColorScheme.Get().empty() + ? ColorSchemePreferences::fromStringExt( prefersColorScheme.Get() ) + : ColorSchemeExtPreference::Light ); + + bool useHNDark = hnDark.Get(); ui->loadLayoutFromString( R"xml( @@ -48,11 +80,11 @@ EE_MAIN_FUNC int main( int, char** ) { fwdBtn->setEnabled( historyIndex < static_cast( history.size() ) - 1 ); }; - const auto loadDocumentData = [ui, mainContainer, urlBar, &app, - scrollView]( URI url, std::string& data ) { + const auto loadDocumentData = [ui, mainContainer, urlBar, &app, scrollView, + useHNDark]( URI url, std::string& data ) { if ( data.empty() ) return; - ui->ensureMainThread( [url, data, mainContainer, urlBar, ui, &app, scrollView] { + ui->ensureMainThread( [url, data, mainContainer, urlBar, ui, &app, scrollView, useHNDark] { mainContainer->closeAllChildren(); ui->getStyleSheet().removeAllWithoutMarker( app.getStyleSheetDefaultMarker() ); ui->setURIFromURL( url ); @@ -61,6 +93,44 @@ EE_MAIN_FUNC int main( int, char** ) { scrollView->getVerticalScrollBar()->setValue( 0 ); ui->loadLayoutFromString( HTMLFormatter::HTMLtoXML( data ), mainContainer, hash ); urlBar->setText( urlStr ); + + if ( useHNDark && url.getAuthority() == "news.ycombinator.com" ) { + static const std::string_view HN_DARK = R"css( + body * { + color: #dcdccc !important; + } + body, + #hnmain, + .pagetop { + background-color: #404040 !important; + } + body > center > table > tbody > tr:first-child * { + background-color: #505050 !important; + } + body > center > table > tbody > tr:first-child * a:hover { + background: #404040 !important; + } + body code, body pre, body input, body textarea { + background: #505050 !important; + } + body a { + color: #7F9F7F !important; + } + body .subtext a { + color: #dcdccc !important; + } + body a:visited, body a:visited span { + color: #CC9393 !important; + } + body a:hover, body a:hover span { + background: #505050 !important; + } + )css"; + + StyleSheetParser parser; + if ( parser.loadFromString( HN_DARK ) ) + ui->getStyleSheet().combineStyleSheet( parser.getStyleSheet() ); + } } ); }; diff --git a/src/tests/unit_tests/richtext.cpp b/src/tests/unit_tests/richtext.cpp index b9da69ab6..61d3ff169 100644 --- a/src/tests/unit_tests/richtext.cpp +++ b/src/tests/unit_tests/richtext.cpp @@ -1111,6 +1111,47 @@ UTEST( UIRichText, MinMaxWidthChildren ) { Engine::destroySingleton(); } +UTEST( UIRichText, MatchParentChildPadding ) { + Engine::instance()->createWindow( WindowSettings( 800, 600, "RichText MatchParent Child Padding 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->loaded() ); + FontFamily::loadFromRegular( font ); + + UI::UISceneNode* sceneNode = UI::UISceneNode::New(); + UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager(); + themeManager->setDefaultFont( font ); + + String xml = R"xml( + + + + + + )xml"; + + sceneNode->loadLayoutFromString( xml ); + UI::UIRichText* rtParent = sceneNode->find( "rt_parent" ); + UI::UIWidget* childWidget = sceneNode->find( "child_widget" ); + ASSERT_TRUE( rtParent != nullptr ); + ASSERT_TRUE( childWidget != nullptr ); + + sceneNode->update( Time::Zero ); + + Float parentWidth = rtParent->getSize().getWidth(); + Float childWidth = childWidget->getSize().getWidth(); + Float expectedChildWidth = parentWidth - PixelDensity::dpToPx( 20 ); + + EXPECT_EQ( childWidth, expectedChildWidth ); + + eeDelete( sceneNode ); + Engine::destroySingleton(); +} + UTEST( UILayout, MinMaxWidthChildren ) { Engine::instance()->createWindow( WindowSettings( 800, 600, "Layout Min/Max Width Children Test", WindowStyle::Default, WindowBackend::Default,