#include "compareimages.hpp" #include "utest.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace EE; using namespace EE::Graphics; using namespace EE::Window; using namespace EE::Scene; using namespace EE::UI; using namespace EE::UI::Tools; static void init_ui_test() { Engine::instance()->createWindow( WindowSettings( 1024, 650, "HTML Tables 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" ); FontFamily::loadFromRegular( font ); UI::UISceneNode* sceneNode = UI::UISceneNode::New(); SceneManager::instance()->add( sceneNode ); UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager(); themeManager->setDefaultFont( font ); } UTEST( UIHTMLTable, complexLayout ) { auto win = Engine::instance()->createWindow( WindowSettings( 1024, 653, "HTML Tables Test", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); UI::UISceneNode* sceneNode = UI::UISceneNode::New(); SceneManager::instance()->add( sceneNode ); UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager(); themeManager->setDefaultFont( font ); sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/" ); std::string html; FileSystem::fileGet( "assets/html/hn_thread_test.html", html ); sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); win->setClearColor( Color::White ); /* while ( win->isRunning() ) */ { win->getInput()->update(); SceneManager::instance()->update(); win->clear(); SceneManager::instance()->draw(); win->display(); } auto hnMain = sceneNode->getRoot()->find( "hnmain" ); auto bigbox = sceneNode->getRoot()->find( "bigbox" ); auto commentTree = sceneNode->getRoot()->findByClass( "comment-tree" ); auto votelinks = sceneNode->getRoot()->findByClass( "votelinks" ); auto commentTd = sceneNode->getRoot()->findByClass( "default" ); auto comment = sceneNode->getRoot()->findByClass( "comment" ); auto commtext = sceneNode->getRoot()->findByClass( "commtext" ); EXPECT_GT( commentTree->getPixelsSize().getWidth(), 0 ); EXPECT_GT( commentTree->getPixelsSize().getHeight(), 0 ); EXPECT_GT( comment->getPixelsSize().getWidth(), 0 ); EXPECT_GT( commtext->getPixelsSize().getWidth(), 0 ); EXPECT_GT( commentTd->getPixelsSize().getWidth(), 0 ); EXPECT_GT( commentTd->getPixelsSize().getHeight(), 0 ); EXPECT_GE( hnMain->getPixelsSize().getHeight(), bigbox->getPixelsSize().getHeight() ); Float totalTds = commentTd->getPixelsSize().getWidth() + votelinks->getPixelsSize().getWidth(); Float mainTotal = hnMain->getPixelsSize().getWidth(); EXPECT_GT( totalTds, 0 ); EXPECT_GT( mainTotal, 0 ); EXPECT_NEAR( totalTds, mainTotal, 0.1 ); compareImages( utest_state, utest_result, win, "eepp-uihtmltable-complex-layout", "html" ); Engine::destroySingleton(); } UTEST( UIHTMLTable, complexLayout2 ) { auto win = Engine::instance()->createWindow( WindowSettings( 1024, 650, "HTML Tables Test 2", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); UI::UISceneNode* sceneNode = UI::UISceneNode::New(); SceneManager::instance()->add( sceneNode ); UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager(); themeManager->setDefaultFont( font ); sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/" ); std::string html; FileSystem::fileGet( "assets/html/hn_threaded_test.html", html ); sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); win->setClearColor( Color::White ); win->getInput()->update(); SceneManager::instance()->update(); win->clear(); SceneManager::instance()->draw(); win->display(); compareImages( utest_state, utest_result, win, "eepp-uihtmltable-complex-layout-2", "html" ); Engine::destroySingleton(); } UTEST( UIRichText, anchorMargins ) { auto win = Engine::instance()->createWindow( WindowSettings( 800, 600, "Anchor Margins Test", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); UI::UISceneNode* sceneNode = UI::UISceneNode::New(); SceneManager::instance()->add( sceneNode ); UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager(); themeManager->setDefaultFont( font ); sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/" ); std::string html; FileSystem::fileGet( "assets/html/anchor_margins.html", html ); sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); win->setClearColor( Color::White ); win->getInput()->update(); SceneManager::instance()->update(); win->clear(); SceneManager::instance()->draw(); win->display(); auto anchors = sceneNode->getRoot()->findAllByTag( "a" ); for ( auto anchor : anchors ) { auto a = anchor->asType(); EXPECT_EQ( anchor->getPixelsSize().getHeight(), a->getFont()->getLineSpacing( a->getFontSize() ) ); } compareImages( utest_state, utest_result, win, "eepp-ui-anchor-margins", "html" ); Engine::destroySingleton(); } UTEST( UIRichText, spanPadding ) { auto win = Engine::instance()->createWindow( WindowSettings( 800, 600, "Span Padding Test", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); UI::UISceneNode* sceneNode = UI::UISceneNode::New(); SceneManager::instance()->add( sceneNode ); UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager(); themeManager->setDefaultFont( font ); sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/" ); std::string html; FileSystem::fileGet( "assets/html/span_padding.html", html ); sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); win->setClearColor( Color::White ); win->getInput()->update(); SceneManager::instance()->update(); win->clear(); SceneManager::instance()->draw(); win->display(); compareImages( utest_state, utest_result, win, "eepp-ui-span-padding", "html" ); Engine::destroySingleton(); } UTEST( UIRichText, anchorPadding ) { auto win = Engine::instance()->createWindow( WindowSettings( 800, 600, "Anchor Span Padding Test", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); UI::UISceneNode* sceneNode = UI::UISceneNode::New(); SceneManager::instance()->add( sceneNode ); UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager(); themeManager->setDefaultFont( font ); sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/" ); std::string html; FileSystem::fileGet( "assets/html/anchor_padding.html", html ); sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); win->setClearColor( Color::White ); win->getInput()->update(); SceneManager::instance()->update(); win->clear(); SceneManager::instance()->draw(); win->display(); compareImages( utest_state, utest_result, win, "eepp-ui-anchor-padding", "html" ); auto anchors = sceneNode->getRoot()->findAllByTag( "a" ); ASSERT_GE( anchors.size(), (size_t)1 ); auto downloadLink = anchors[0]->asType(); EXPECT_NEAR( downloadLink->getPixelsSize().getWidth(), 81.f, 3.f ); EXPECT_NEAR( downloadLink->getPixelsSize().getHeight(), 28.f, 3.f ); Engine::destroySingleton(); } UTEST( UIRichText, anchorPaddingLineHeight ) { auto win = Engine::instance()->createWindow( WindowSettings( 800, 600, "Anchor Padding LineHeight Test", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); UI::UISceneNode* sceneNode = UI::UISceneNode::New(); SceneManager::instance()->add( sceneNode ); UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager(); themeManager->setDefaultFont( font ); sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/" ); std::string html; FileSystem::fileGet( "assets/html/anchor_padding_lineheight.html", html ); sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); win->setClearColor( Color::White ); win->getInput()->update(); SceneManager::instance()->update(); win->clear(); SceneManager::instance()->draw(); win->display(); compareImages( utest_state, utest_result, win, "eepp-ui-anchor-padding-lineheight", "html" ); auto anchors = sceneNode->getRoot()->findAllByTag( "a" ); ASSERT_GE( anchors.size(), (size_t)1 ); auto downloadLink = anchors[0]->asType(); EXPECT_NEAR( downloadLink->getPixelsSize().getWidth(), 81.f, 3.f ); EXPECT_NEAR( downloadLink->getPixelsSize().getHeight(), 28.f, 3.f ); Engine::destroySingleton(); } UTEST( UIHTMLTable, complexLayout3 ) { auto win = Engine::instance()->createWindow( WindowSettings( 1024, 650, "HTML Tables Test 3", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); UI::UISceneNode* sceneNode = UI::UISceneNode::New(); SceneManager::instance()->add( sceneNode ); UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager(); themeManager->setDefaultFont( font ); sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/" ); std::string html; FileSystem::fileGet( "assets/html/hn_frontpage.html", html ); sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); win->setClearColor( Color::White ); win->getInput()->update(); SceneManager::instance()->update(); win->clear(); SceneManager::instance()->draw(); win->display(); compareImages( utest_state, utest_result, win, "eepp-uihtmltable-complex-layout-3", "html" ); Engine::destroySingleton(); } UTEST( UIHTMLTable, nestedPerformance ) { Engine::instance()->createWindow( WindowSettings( 1024, 650, "HTML Tables 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 != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); UI::UISceneNode* sceneNode = UI::UISceneNode::New(); SceneManager::instance()->add( sceneNode ); UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager(); themeManager->setDefaultFont( font ); // Create nested tables. UIHTMLTable* rootTable = UIHTMLTable::New(); rootTable->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::WrapContent ); rootTable->setParent( sceneNode->getRoot() ); UIHTMLTable* currentTable = rootTable; for ( int i = 0; i < 10; ++i ) { UIHTMLTableRow* row = UIHTMLTableRow::New(); row->setParent( currentTable ); UIHTMLTableCell* cell = UIHTMLTableCell::New( "td" ); cell->setParent( row ); UIHTMLTable* childTable = UIHTMLTable::New(); childTable->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::WrapContent ); childTable->setParent( cell ); currentTable = childTable; } UIHTMLTableRow* row = UIHTMLTableRow::New(); row->setParent( currentTable ); UIHTMLTableCell* cell = UIHTMLTableCell::New( "td" ); cell->setParent( row ); UITextSpan* span = UITextSpan::New(); span->setParent( cell ); span->setText( "Deeply nested text" ); Clock clock; sceneNode->updateDirtyLayouts(); Log::info( "Time for nested layout (10 levels): %.2f ms", clock.getElapsedTime().asMilliseconds() ); Engine::destroySingleton(); } UTEST( UIHTMLTable, specifiedWidth ) { Engine::instance()->createWindow( WindowSettings( 1024, 650, "HTML Tables 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 != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); UI::UISceneNode* sceneNode = UI::UISceneNode::New(); SceneManager::instance()->add( sceneNode ); UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager(); themeManager->setDefaultFont( font ); sceneNode->loadLayoutFromString( R"(
C1 C2
)" ); sceneNode->updateDirtyLayouts(); auto c1 = sceneNode->getRoot()->find( "c1" ); auto c2 = sceneNode->getRoot()->find( "c2" ); ASSERT_TRUE( c1 != nullptr ); ASSERT_TRUE( c2 != nullptr ); // Cell 2 should be at least 200px. EXPECT_GE( c2->getPixelsSize().getWidth(), 200.f ); // Total width should be 500px (minus padding if any, but default is 0). EXPECT_NEAR( c1->getPixelsSize().getWidth() + c2->getPixelsSize().getWidth(), 500.f, 1.f ); Engine::destroySingleton(); } UTEST( UIHTMLTable, nestedSpecifiedWidth ) { Engine::instance()->createWindow( WindowSettings( 1024, 650, "HTML Tables 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 != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); UI::UISceneNode* sceneNode = UI::UISceneNode::New(); SceneManager::instance()->add( sceneNode ); UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager(); themeManager->setDefaultFont( font ); sceneNode->loadLayoutFromString( R"(
Flexible text content that should take the rest of the space
)" ); sceneNode->updateDirtyLayouts(); auto c1 = sceneNode->getRoot()->find( "c1" ); auto c2 = sceneNode->getRoot()->find( "c2" ); ASSERT_TRUE( c1 != nullptr ); ASSERT_TRUE( c2 != nullptr ); // Cell 1 should be exactly 50px because it's rigid (only contains fixed-width image) // and Cell 2 is flexible. EXPECT_NEAR( c1->getPixelsSize().getWidth(), 50.f, 1.f ); EXPECT_NEAR( c2->getPixelsSize().getWidth(), 450.f, 1.f ); Engine::destroySingleton(); } UTEST( UIHTMLInput, sizeAttribute ) { init_ui_test(); auto* sceneNode = SceneManager::instance()->getUISceneNode(); sceneNode->loadLayoutFromString( R"html( )html" ); auto c1 = sceneNode->getRoot()->find( "i1" )->asType(); auto c2 = sceneNode->getRoot()->find( "i2" )->asType(); auto c3 = sceneNode->getRoot()->find( "i3" )->asType(); auto cp = sceneNode->getRoot()->find( "i_pwd" )->asType(); auto cm = sceneNode->getRoot()->find( "i_mode_pwd" )->asType(); auto cc = sceneNode->getRoot()->find( "i_chk" )->asType(); ASSERT_TRUE( c1 != nullptr ); ASSERT_TRUE( c2 != nullptr ); ASSERT_TRUE( c3 != nullptr ); ASSERT_TRUE( cp != nullptr ); ASSERT_TRUE( cm != nullptr ); ASSERT_TRUE( cc != nullptr ); auto i1 = c1->getChildWidget()->asType(); auto i2 = c2->getChildWidget()->asType(); auto i3 = c3->getChildWidget()->asType(); ASSERT_TRUE( i1 != nullptr ); ASSERT_TRUE( i2 != nullptr ); ASSERT_TRUE( i3 != nullptr ); EXPECT_EQ( i1->getHtmlSize(), 10u ); EXPECT_EQ( i2->getHtmlSize(), 20u ); EXPECT_EQ( i3->getHtmlSize(), 20u ); EXPECT_GT( i2->getPixelsSize().getWidth(), i1->getPixelsSize().getWidth() ); EXPECT_NEAR( i2->getPixelsSize().getWidth(), i3->getPixelsSize().getWidth(), 1.f ); EXPECT_TRUE( cp->getChildWidget()->isType( UI_TYPE_TEXTINPUT ) ); EXPECT_TRUE( cp->getChildWidget()->asType()->getMode() == UITextInput::TextInputMode::Password ); EXPECT_TRUE( cp->getChildWidget()->asType()->getMode() == UITextInput::TextInputMode::Password ); EXPECT_TRUE( cm->getChildWidget()->asType()->getMode() == UITextInput::TextInputMode::Password ); EXPECT_TRUE( cc->getChildWidget()->isType( UI_TYPE_CHECKBOX ) ); Engine::destroySingleton(); } UTEST( UIHTMLTextArea, rowsColsAttribute ) { init_ui_test(); auto* scene = SceneManager::instance()->getUISceneNode(); auto* c1_raw = scene->loadLayoutFromString( R"html( )html" ); ASSERT_TRUE( c1_raw != nullptr ); auto* t1 = c1_raw->find( "t1" )->asType(); auto* t2 = c1_raw->find( "t2" )->asType(); ASSERT_TRUE( t1 != nullptr ); ASSERT_TRUE( t2 != nullptr ); EXPECT_EQ( t1->getRows(), 2u ); EXPECT_EQ( t1->getCols(), 20u ); EXPECT_EQ( t2->getRows(), 4u ); EXPECT_EQ( t2->getCols(), 40u ); EXPECT_GT( t2->getPixelsSize().getWidth(), t1->getPixelsSize().getWidth() ); EXPECT_GT( t2->getPixelsSize().getHeight(), t1->getPixelsSize().getHeight() ); Engine::destroySingleton(); } UTEST( UIHTMLTable, tableLayoutFixed ) { Engine::instance()->createWindow( WindowSettings( 1024, 650, "HTML Tables 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 != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); UI::UISceneNode* sceneNode = UI::UISceneNode::New(); SceneManager::instance()->add( sceneNode ); UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager(); themeManager->setDefaultFont( font ); sceneNode->loadLayoutFromString( R"(
C1 C2 C3
C4 (Should be ignored) C5 C6
)" ); sceneNode->updateDirtyLayouts(); auto c1 = sceneNode->getRoot()->find( "c1" ); auto c2 = sceneNode->getRoot()->find( "c2" ); auto c3 = sceneNode->getRoot()->find( "c3" ); ASSERT_TRUE( c1 != nullptr ); ASSERT_TRUE( c2 != nullptr ); ASSERT_TRUE( c3 != nullptr ); // Total width is 600px. C1=100, C2=200, C3 takes remaining 300px. EXPECT_NEAR( c1->getPixelsSize().getWidth(), 100.f, 1.f ); EXPECT_NEAR( c2->getPixelsSize().getWidth(), 200.f, 1.f ); EXPECT_NEAR( c3->getPixelsSize().getWidth(), 300.f, 1.f ); Engine::destroySingleton(); } UTEST( UIHTMLBody, backgroundColorPropagation ) { Engine::instance()->createWindow( WindowSettings( 1024, 650, "HTML Tables 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 != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); UI::UISceneNode* sceneNode = UI::UISceneNode::New(); SceneManager::instance()->add( sceneNode ); UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager(); themeManager->setDefaultFont( font ); sceneNode->loadLayoutFromString( R"( )" ); sceneNode->updateDirtyLayouts(); auto html_el = sceneNode->getRoot()->find( "html_el" ); auto body_el = sceneNode->getRoot()->find( "body_el" ); ASSERT_TRUE( html_el != nullptr ); ASSERT_TRUE( body_el != nullptr ); // HTML element should have inherited the red background color, and body should be transparent EXPECT_TRUE( html_el->asType()->getBackgroundColor() == Color::Red ); EXPECT_TRUE( body_el->asType()->getBackgroundColor() == Color::Transparent ); Engine::destroySingleton(); } UTEST( UIHTMLBody, maxWidthResizingBug ) { Engine::instance()->createWindow( WindowSettings( 1024, 768, "HTML Resize Bug", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); UI::UISceneNode* sceneNode = UI::UISceneNode::New(); SceneManager::instance()->add( sceneNode ); UI::CSS::StyleSheetParser parser; parser.loadFromFile( "assets/html/dwarmstrong/style.css" ); sceneNode->setStyleSheet( parser.getStyleSheet() ); std::string htmlContent; FileSystem::fileGet( "assets/html/dwarmstrong/dwarmstrong.html", htmlContent ); sceneNode->loadLayoutFromString( htmlContent ); sceneNode->getRoot()->setSize( 1024, 768 ); sceneNode->updateDirtyLayouts(); auto body_el = sceneNode->getRoot()->findByType( UI_TYPE_HTML_BODY )->asType(); ASSERT_TRUE( body_el != nullptr ); Float widthAt1024 = body_el->getPixelsSize().getWidth(); EXPECT_NEAR( widthAt1024, 960.f, 10.f ); // It should be around 960px (minus some margins if any) sceneNode->getRoot()->setSize( 2048, 768 ); sceneNode->updateDirtyLayouts(); Float widthAt2048 = body_el->getPixelsSize().getWidth(); EXPECT_NEAR( widthAt2048, 960.f, 10.f ); // Body should stay 960px even when parent is huge sceneNode->getRoot()->setSize( 1024, 768 ); sceneNode->updateDirtyLayouts(); Float widthAfterResize = body_el->getPixelsSize().getWidth(); EXPECT_NEAR( widthAt1024, widthAfterResize, 1.f ); Engine::destroySingleton(); } UTEST( UILayout, marginAuto ) { Engine::instance()->createWindow( WindowSettings( 1024, 650, "Margin Auto 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 != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); UI::UISceneNode* sceneNode = UI::UISceneNode::New(); SceneManager::instance()->add( sceneNode ); UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager(); themeManager->setDefaultFont( font ); auto* container = sceneNode->loadLayoutFromString( R"( )" ); auto child = sceneNode->getRoot()->find( "child" ); ASSERT_TRUE( child != nullptr ); UIWidget* childWidget = child->asType(); UIWidget* contWidget = container->asType(); contWidget->setSize( 500, 500 ); childWidget->setSize( 100, 100 ); sceneNode->updateDirtyLayouts(); Float expectedMarginX = ( contWidget->getPixelsSize().getWidth() - childWidget->getPixelsSize().getWidth() ) / 2.f; // Margin left/right should be auto computed to expectedMarginX EXPECT_NEAR( childWidget->getLayoutPixelsMargin().Left, expectedMarginX, 1.f ); EXPECT_NEAR( childWidget->getLayoutPixelsMargin().Right, expectedMarginX, 1.f ); EXPECT_NEAR( childWidget->getLayoutPixelsMargin().Top, 0.f, 1.f ); EXPECT_NEAR( childWidget->getLayoutPixelsMargin().Bottom, 0.f, 1.f ); // Resize parent and see if margins re-evaluate automatically contWidget->setSize( 800, 800 ); sceneNode->updateDirtyLayouts(); expectedMarginX = ( contWidget->getPixelsSize().getWidth() - childWidget->getPixelsSize().getWidth() ) / 2.f; EXPECT_NEAR( childWidget->getLayoutPixelsMargin().Left, expectedMarginX, 1.f ); EXPECT_NEAR( childWidget->getLayoutPixelsMargin().Right, expectedMarginX, 1.f ); // Now test resize of child childWidget->setSize( 200, 100 ); sceneNode->updateDirtyLayouts(); expectedMarginX = ( contWidget->getPixelsSize().getWidth() - childWidget->getPixelsSize().getWidth() ) / 2.f; EXPECT_NEAR( childWidget->getLayoutPixelsMargin().Left, expectedMarginX, 1.f ); EXPECT_NEAR( childWidget->getLayoutPixelsMargin().Right, expectedMarginX, 1.f ); Engine::destroySingleton(); } UTEST( UILayout, listStyleTypeDecimal ) { init_ui_test(); auto* sceneNode = SceneManager::instance()->getUISceneNode(); sceneNode->loadLayoutFromString( R"html(
  1. First item
  2. Second item
  3. Third item
)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. Second
  3. Third
  • 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
  • Coffee
  • Coffee
  • Coffee
  1. Coffee
)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(); } UTEST( UIBorder, renderingVariations ) { auto win = Engine::instance()->createWindow( WindowSettings( 1024, 653, "Border Rendering Test", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); UI::UISceneNode* sceneNode = UI::UISceneNode::New(); SceneManager::instance()->add( sceneNode ); UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager(); themeManager->setDefaultFont( font ); sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/" ); std::string html; FileSystem::fileGet( "assets/html/border_tests.html", html ); sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); win->setClearColor( Color::White ); win->getInput()->update(); SceneManager::instance()->update(); win->clear(); SceneManager::instance()->draw(); win->display(); compareImages( utest_state, utest_result, win, "eepp-ui-border-rendering", "html", 4 ); Engine::destroySingleton(); } UTEST( UIBorder, renderingVariations2 ) { auto win = Engine::instance()->createWindow( WindowSettings( 1024, 653, "Border Rendering Test 2", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); UI::UISceneNode* sceneNode = UI::UISceneNode::New(); SceneManager::instance()->add( sceneNode ); UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager(); themeManager->setDefaultFont( font ); sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/" ); std::string html; FileSystem::fileGet( "assets/html/border_tests_2.html", html ); sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); win->setClearColor( Color::White ); win->getInput()->update(); SceneManager::instance()->update(); win->clear(); SceneManager::instance()->draw(); win->display(); compareImages( utest_state, utest_result, win, "eepp-ui-border-rendering-2", "html", 4 ); Engine::destroySingleton(); } static UISceneNode* init_test_inline_block() { FontTrueType* font = nullptr; FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); font = FontTrueType::New( "NotoSans-Regular" ); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); FontFamily::loadFromRegular( font ); UISceneNode* sceneNode = UISceneNode::New(); SceneManager::instance()->add( sceneNode ); SceneManager::instance()->setCurrentUISceneNode( sceneNode ); UIThemeManager* themeManager = sceneNode->getUIThemeManager(); themeManager->setDefaultFont( font ); themeManager->applyDefaultTheme( sceneNode->getRoot() ); return sceneNode; } UTEST( UIHTML, InlineBlock ) { Engine::instance()->createWindow( WindowSettings( 1024, 768, "Inline Block Test", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); UISceneNode* sceneNode = init_test_inline_block(); const std::string html = R"html( )html"; sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); auto ul = sceneNode->getRoot()->findByTag( "ul" ); ASSERT_TRUE( ul != nullptr ); // Force layout update sceneNode->update( Seconds( 1 ) ); auto lis = ul->findAllByTag( "li" ); EXPECT_EQ( lis.size(), (size_t)6 ); for ( auto li : lis ) { EXPECT_EQ( li->asType()->getDisplay(), CSSDisplay::InlineBlock ); EXPECT_EQ( li->getLayoutWidthPolicy(), SizePolicy::WrapContent ); EXPECT_GT( li->getPixelsSize().getWidth(), 0 ); EXPECT_LT( li->getPixelsSize().getWidth(), ul->getPixelsSize().getWidth() ); EXPECT_GT( li->getPixelsSize().getHeight(), 0 ); } // Check if they are on the same line (inline-block) if ( lis.size() >= 2 ) { EXPECT_EQ( lis[0]->getPixelsPosition().y, lis[1]->getPixelsPosition().y ); EXPECT_LT( lis[0]->getPixelsPosition().x, lis[1]->getPixelsPosition().x ); } Engine::destroySingleton(); } UTEST( UIHTML, BlockList ) { Engine::instance()->createWindow( WindowSettings( 1024, 768, "Block List Test", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); UISceneNode* sceneNode = init_test_inline_block(); const std::string html = R"html(
  • Item 1
  • Item 2
)html"; sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); sceneNode->update( Seconds( 1 ) ); auto ul = sceneNode->getRoot()->findByTag( "ul" ); ASSERT_TRUE( ul != nullptr ); EXPECT_GT( ul->getPixelsSize().getWidth(), 0 ); auto lis = ul->findAllByTag( "li" ); EXPECT_EQ( lis.size(), (size_t)2 ); for ( auto li : lis ) { EXPECT_EQ( li->asType()->getDisplay(), CSSDisplay::ListItem ); EXPECT_GT( li->getChildCount(), (size_t)0 ); EXPECT_TRUE( li->asType()->getRichTextPtr()->getFontStyleConfig().Font != nullptr ); EXPECT_GT( li->asType()->getRichTextPtr()->getFontStyleConfig().CharacterSize, 0 ); EXPECT_GT( li->asType()->getRichTextPtr()->getSize().getWidth(), 0 ); EXPECT_GT( li->asType()->getRichTextPtr()->getSize().getHeight(), 0 ); EXPECT_GT( li->getPixelsSize().getWidth(), 0 ); EXPECT_GT( li->getPixelsSize().getHeight(), 0 ); } // They should be one above the other (block) EXPECT_LT( lis[0]->getPixelsPosition().y, lis[1]->getPixelsPosition().y ); EXPECT_EQ( lis[0]->getPixelsPosition().x, lis[1]->getPixelsPosition().x ); Engine::destroySingleton(); } UTEST( UIHTML, InlineList ) { Engine::instance()->createWindow( WindowSettings( 1024, 768, "Inline List Test", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); UISceneNode* sceneNode = init_test_inline_block(); const std::string html = R"html(
  • Item 1
  • Item 2
)html"; sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); sceneNode->update( Seconds( 1 ) ); auto ul = sceneNode->getRoot()->findByTag( "ul" ); ASSERT_TRUE( ul != nullptr ); EXPECT_GT( ul->getPixelsSize().getWidth(), 0 ); auto lis = ul->findAllByTag( "li" ); EXPECT_EQ( lis.size(), (size_t)2 ); for ( auto li : lis ) { EXPECT_EQ( li->asType()->getDisplay(), CSSDisplay::Inline ); EXPECT_EQ( li->getLayoutWidthPolicy(), SizePolicy::WrapContent ); EXPECT_GT( li->getPixelsSize().getWidth(), 0 ); EXPECT_LT( li->getPixelsSize().getWidth(), ul->getPixelsSize().getWidth() ); } // They should be on the same line (inline) EXPECT_EQ( lis[0]->getPixelsPosition().y, lis[1]->getPixelsPosition().y ); EXPECT_LT( lis[0]->getPixelsPosition().x, lis[1]->getPixelsPosition().x ); Engine::destroySingleton(); } UTEST( UIHTML, InlineBlockExplicitWidth ) { Engine::instance()->createWindow( WindowSettings( 1024, 768, "Inline Block Explicit Width Test", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); UISceneNode* sceneNode = init_test_inline_block(); const std::string html = R"html(
)html"; sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); sceneNode->update( Seconds( 1 ) ); auto d1 = sceneNode->getRoot()->find( "d1" )->asType(); auto d2 = sceneNode->getRoot()->find( "d2" )->asType(); ASSERT_TRUE( d1 != nullptr && d2 != nullptr ); // They should NOT be on the same line because 150 + 150 > 200 EXPECT_LT( d1->getPixelsPosition().y, d2->getPixelsPosition().y ); EXPECT_EQ( d1->getPixelsPosition().x, d2->getPixelsPosition().x ); Engine::destroySingleton(); } UTEST( UIHTML, InlineBlockMixedContent ) { Engine::instance()->createWindow( WindowSettings( 1024, 653, "Inline Block Mixed Content Test", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); UISceneNode* sceneNode = init_test_inline_block(); const std::string html = R"html(
Some text
more text
)html"; sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); sceneNode->update( Seconds( 1 ) ); auto ib = sceneNode->getRoot()->find( "ib" )->asType(); ASSERT_TRUE( ib != nullptr ); // The inline-block should have a non-zero position and be somewhat centered vertically if it // follows text flow EXPECT_GT( ib->getPixelsPosition().x, 0 ); EXPECT_GT( ib->getPixelsSize().getWidth(), 0 ); Engine::destroySingleton(); } UTEST( UIHTML, InlineBlockWrapIssue ) { Engine::instance()->createWindow( WindowSettings( 1024, 653, "Inline Block Wrap Issue Test", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); UI::UISceneNode* sceneNode = init_test_inline_block(); std::string html; FileSystem::fileGet( "assets/html/inline_block_wrap.html", html ); sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); sceneNode->update( Seconds( 1 ) ); auto h2 = sceneNode->getRoot()->find( "h2-wrap" ); ASSERT_TRUE( h2 != nullptr ); auto rt = h2->asType()->getRichTextPtr(); EXPECT_EQ( (size_t)1, rt->getLines().size() ); Engine::destroySingleton(); } UTEST( UIHTML, InlineBlockBrowserTest ) { Engine::instance()->createWindow( WindowSettings( 1024, 653, "Inline Block Browser Test", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); UI::UISceneNode* sceneNode = init_test_inline_block(); std::string html; FileSystem::fileGet( "assets/html/is_inline_block.html", html ); sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); sceneNode->update( Seconds( 1 ) ); auto ib = sceneNode->getRoot()->find( "ib" )->asType(); auto t1 = sceneNode->getRoot()->find( "t1" )->asType(); auto t2 = sceneNode->getRoot()->find( "t2" )->asType(); ASSERT_TRUE( ib != nullptr && t1 != nullptr && t2 != nullptr ); // If it drops to the next line: EXPECT_GT( ib->getPixelsPosition().y, t1->getPixelsPosition().y ); // And t2 should be AFTER ib (either horizontally or vertically) EXPECT_GE( t2->getPixelsPosition().y, ib->getPixelsPosition().y + ib->getPixelsSize().getHeight() ); if ( t2->getPixelsPosition().y == ib->getPixelsPosition().y ) { EXPECT_GE( t2->getPixelsPosition().x, ib->getPixelsPosition().x + ib->getPixelsSize().getWidth() ); } EXPECT_EQ( ib->getPixelsPosition().x, t2->getPixelsPosition().x ); Engine::destroySingleton(); } UTEST( UIHTML, HeightExpansion ) { Engine::instance()->createWindow( WindowSettings( 1024, 653, "Height Expansion Test", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); UI::UISceneNode* sceneNode = init_test_inline_block(); sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/ensoft/" ); std::string html; FileSystem::fileGet( "assets/html/ensoft/ensoft.html", html ); sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); sceneNode->update( Seconds( 1 ) ); // Wait a bit and update again to make sure layouts are computed sceneNode->updateDirtyLayouts(); auto htmlNode = sceneNode->getRoot()->findByType( UI_TYPE_HTML_HTML ); auto bodyNode = sceneNode->getRoot()->findByType( UI_TYPE_HTML_BODY ); ASSERT_TRUE( htmlNode != nullptr ); ASSERT_TRUE( bodyNode != nullptr ); auto htmlWidget = htmlNode->asType(); auto bodyWidget = bodyNode->asType(); EXPECT_GT( htmlWidget->getSize().getHeight(), 0 ); EXPECT_GT( bodyWidget->getSize().getHeight(), 0 ); EXPECT_GE( htmlWidget->getSize().getHeight(), bodyWidget->getSize().getHeight() ); auto barNode = sceneNode->getRoot()->find( "bar" ); ASSERT_TRUE( barNode != nullptr ); auto barWidget = barNode->asType(); auto barHTML = barNode->asType(); EXPECT_EQ( barHTML->getCSSPosition(), CSSPosition::Fixed ); EXPECT_NEAR( barWidget->getPixelsSize().getWidth(), 250.f, 1.f ); auto rootWidget = sceneNode->getRoot(); EXPECT_GT( rootWidget->getPixelsSize().getHeight(), 0 ); EXPECT_NEAR( barWidget->getPixelsSize().getHeight(), rootWidget->getPixelsSize().getHeight(), 1.f ); Engine::destroySingleton(); } UTEST( UIHTML, HeightExpansion_FixedDoesNotExpand ) { Engine::instance()->createWindow( WindowSettings( 1024, 653, "Height Expansion Test", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); UI::UISceneNode* sceneNode = init_test_inline_block(); const std::string html = R"html(
)html"; sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); sceneNode->update( Seconds( 1 ) ); sceneNode->updateDirtyLayouts(); auto bodyNode = sceneNode->getRoot()->findByType( UI_TYPE_HTML_BODY ); ASSERT_TRUE( bodyNode != nullptr ); auto bodyWidget = bodyNode->asType(); // The height should be 100px, not 550px because the fixed div should be ignored. EXPECT_NEAR( bodyWidget->getPixelsSize().getHeight(), 100.f, 1.f ); Engine::destroySingleton(); } UTEST( UIHTML, ContactFormLayout ) { Engine::instance()->createWindow( WindowSettings( 1024, 653, "Contact Form Layout Test", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); UI::UISceneNode* sceneNode = init_test_inline_block(); sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/ensoft/" ); std::string html; FileSystem::fileGet( "assets/html/ensoft/contact.html", html ); sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); sceneNode->update( Seconds( 1 ) ); sceneNode->updateDirtyLayouts(); auto form = sceneNode->getRoot()->find( "form-contact" ); ASSERT_TRUE( form != nullptr ); auto formWidget = form->asType(); EXPECT_GT( formWidget->getPixelsSize().getHeight(), 0 ); auto ul = formWidget->findByTag( "ul" ); ASSERT_TRUE( ul != nullptr ); auto ulWidget = ul->asType(); Float ulHeight = ulWidget->getPixelsSize().getHeight(); EXPECT_GT( ulHeight, 0 ); auto lis = ulWidget->findAllByTag( "li" ); EXPECT_EQ( lis.size(), (size_t)7 ); Float totalLiHeight = 0; int visibleLiCount = 0; for ( auto li : lis ) { Float liH = li->getPixelsSize().getHeight(); if ( liH > 0 ) visibleLiCount++; totalLiHeight += liH; } EXPECT_EQ( visibleLiCount, 6 ); EXPECT_GT( totalLiHeight, 0 ); EXPECT_NEAR( ulHeight, totalLiHeight, 1.f ); auto contactBox = sceneNode->getRoot()->find( "contact-box" ); ASSERT_TRUE( contactBox != nullptr ); auto cbWidget = contactBox->asType(); EXPECT_GT( cbWidget->getPixelsSize().getHeight(), 10 ); auto content = sceneNode->getRoot()->find( "content" ); ASSERT_TRUE( content != nullptr ); auto contentWidget = content->asType(); EXPECT_GT( contentWidget->getPixelsSize().getHeight(), 60 ); auto bodyNode = sceneNode->getRoot()->findByType( UI_TYPE_HTML_BODY ); ASSERT_TRUE( bodyNode != nullptr ); auto bodyWidget = bodyNode->asType(); EXPECT_GT( bodyWidget->getPixelsSize().getHeight(), 0 ); Engine::destroySingleton(); } UTEST( UIBackground, imageAtlasPositioning ) { auto win = Engine::instance()->createWindow( WindowSettings( 1024, 653, "Background Atlas Test", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); UI::UISceneNode* sceneNode = init_test_inline_block(); sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/" ); std::string html; FileSystem::fileGet( "assets/html/background_atlas.html", html ); sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); win->setClearColor( Color::White ); // Verify the atlas image was actually loaded — scan all nodes for a loaded drawable bool foundLoadedImage = false; sceneNode->getRoot()->forEachNode( [&foundLoadedImage]( Node* node ) { if ( foundLoadedImage || !node->isWidget() ) return; auto* bg = node->asType()->getBackground(); if ( !bg ) return; auto* layer = bg->getLayer( 0 ); if ( layer && layer->getDrawable() ) { Sizef sz = layer->getDrawable()->getPixelsSize(); if ( sz.getWidth() == 1024.f && sz.getHeight() == 512.f ) foundLoadedImage = true; } } ); ASSERT_TRUE( foundLoadedImage ); win->getInput()->update(); SceneManager::instance()->update(); win->clear(); SceneManager::instance()->draw(); win->display(); compareImages( utest_state, utest_result, win, "eepp-ui-background-atlas", "html", 4 ); Engine::destroySingleton(); } UTEST( UIBackground, imageAtlasPositioningPixelDensity2 ) { auto win = Engine::instance()->createWindow( WindowSettings( 1024, 653, "Background Atlas Test PD2", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); EE::Graphics::PixelDensity::setPixelDensity( 2.0f ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); UI::UISceneNode* sceneNode = init_test_inline_block(); sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/" ); std::string html; FileSystem::fileGet( "assets/html/background_atlas.html", html ); sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); win->setClearColor( Color::White ); win->getInput()->update(); SceneManager::instance()->update(); win->clear(); SceneManager::instance()->draw(); win->display(); compareImages( utest_state, utest_result, win, "eepp-ui-background-atlas-pd2", "html", 4 ); Engine::destroySingleton(); EE::Graphics::PixelDensity::setPixelDensity( 1.0f ); } UTEST( UIBackground, InlineBlockImageSpans ) { auto win = Engine::instance()->createWindow( WindowSettings( 1024, 653, "inline-block image spans", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); UI::UISceneNode* sceneNode = init_test_inline_block(); sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/" ); std::string html; FileSystem::fileGet( "assets/html/inline_block.html", html ); sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); win->setClearColor( Color::White ); win->getInput()->update(); SceneManager::instance()->update(); win->clear(); SceneManager::instance()->draw(); win->display(); auto anchors = sceneNode->getRoot()->findAllByTag( "a" ); auto spans = sceneNode->getRoot()->querySelectorAll( "a > span" ); EXPECT_GT( anchors.size(), (size_t)0 ); EXPECT_GT( spans.size(), (size_t)0 ); for ( auto anchor : anchors ) { EXPECT_GT( anchor->getPixelsSize().getWidth(), 0 ); EXPECT_GT( anchor->getPixelsSize().getHeight(), 0 ); } for ( auto span : spans ) { EXPECT_GT( span->getPixelsSize().getWidth(), 0 ); EXPECT_GT( span->getPixelsSize().getHeight(), 0 ); } compareImages( utest_state, utest_result, win, "eepp-ui-inline-block-image-spans", "html", 4 ); Engine::destroySingleton(); } UTEST( UIBackground, InlineBlockImageFixedSize ) { auto win = Engine::instance()->createWindow( WindowSettings( 1024, 653, "inline-block image fixed size", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); UI::UISceneNode* sceneNode = init_test_inline_block(); sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/" ); std::string html; FileSystem::fileGet( "assets/html/reddit_header_icons.html", html ); sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); win->setClearColor( Color::White ); win->getInput()->update(); SceneManager::instance()->update(); win->clear(); SceneManager::instance()->draw(); win->display(); auto anchors = sceneNode->getRoot()->findAllByTag( "a" ); EXPECT_GT( anchors.size(), (size_t)0 ); for ( auto anchor : anchors ) { EXPECT_EQ( anchor->getPixelsSize().getWidth(), 15 ); EXPECT_EQ( anchor->getPixelsSize().getHeight(), 12 ); } Engine::destroySingleton(); } UTEST( UIHTML, AnchorsSizing ) { auto win = Engine::instance()->createWindow( WindowSettings( 1024, 653, "anchors sizing", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); UI::UISceneNode* sceneNode = init_test_inline_block(); sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/" ); std::string html; FileSystem::fileGet( "assets/html/lobsters_simple.html", html ); sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); win->setClearColor( Color::White ); win->getInput()->update(); SceneManager::instance()->update(); win->clear(); SceneManager::instance()->draw(); win->display(); auto anchors = sceneNode->getRoot()->findAllByTag( "a" ); EXPECT_GT( anchors.size(), (size_t)0 ); for ( auto anchor : anchors ) { auto a = anchor->asType(); if ( a->getDisplay() == CSSDisplay::None ) continue; EXPECT_GT( a->getPixelsSize().getHeight(), 0 ); if ( !a->getText().empty() && a->getFontStyleConfig().Font ) { String text = a->getText(); text.trim(); if ( !text.empty() ) EXPECT_GE( a->getPixelsSize().getWidth(), Text::getTextWidth( text, a->getFontStyleConfig() ) ); } } Engine::destroySingleton(); } static UISceneNode* createWinAndLoadHTML( std::string winName, std::string htmlPath ) { auto win = Engine::instance()->createWindow( WindowSettings( 1024, 653, winName, WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ), ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); if ( font == nullptr || !font->loaded() ) return nullptr; FontFamily::loadFromRegular( font ); UI::UISceneNode* sceneNode = UI::UISceneNode::New(); SceneManager::instance()->add( sceneNode ); UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager(); themeManager->setDefaultFont( font ); sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/" ); std::string html; FileSystem::fileGet( htmlPath, html ); sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); win->setClearColor( Color::White ); win->getInput()->update(); SceneManager::instance()->update(); win->clear(); SceneManager::instance()->draw(); win->display(); return sceneNode; } UTEST( UIHTML, blockFlow ) { auto sceneNode = createWinAndLoadHTML( "HTML Block Flow", "assets/html/block_flow.html" ); ASSERT_TRUE( sceneNode != nullptr ); auto sections = sceneNode->getRoot()->findAllByClass( "language-section" ); ASSERT_EQ( sections.size(), (size_t)6 ); // Each section is display block so we expect a single section per line // if sections position are not equal it means that some sections are floating Float ref = sections[0]->getPixelsPosition().x; for ( auto section : sections ) EXPECT_EQ( section->getPixelsPosition().x, ref ); Engine::destroySingleton(); } UTEST( UIHTML, blockFlowFloat ) { auto sceneNode = createWinAndLoadHTML( "HTML Block Flow", "assets/html/block_flow_float_left.html" ); ASSERT_TRUE( sceneNode != nullptr ); auto sections = sceneNode->getRoot()->findAllByClass( "language-section" ); ASSERT_EQ( sections.size(), (size_t)6 ); // Each section is display block with float: left and width 48% so we expect two sections // per line, and each odd index should be to the right Float refLeft = sections[0]->getPixelsPosition().x; Float refRight = sections[1]->getPixelsPosition().x; for ( size_t idx = 0; idx < sections.size(); idx++ ) { Float expected = idx % 2 == 0 ? refLeft : refRight; EXPECT_EQ( sections[idx]->getPixelsPosition().x, expected ); } Engine::destroySingleton(); }