diff --git a/README.md b/README.md index 579cfe5da..23dd5c0de 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ framework heavily focused on the development of rich graphical user interfaces. [![macOS status](https://img.shields.io/github/actions/workflow/status/SpartanJ/eepp/eepp-macos-build-check.yml?branch=develop&label=macOS)](https://github.com/SpartanJ/eepp/actions?query=workflow%3AmacOS) [![iOS status](https://img.shields.io/github/actions/workflow/status/SpartanJ/eepp/eepp-ios-build-check.yml?branch=develop&label=iOS)](https://github.com/SpartanJ/eepp/actions?query=workflow%3AiOS) [![Android status](https://img.shields.io/github/actions/workflow/status/SpartanJ/eepp/eepp-android-build-check.yml?branch=develop&label=Android)](https://github.com/SpartanJ/eepp/actions?query=workflow%3AAndroid) +[![emscripten status](https://img.shields.io/github/actions/workflow/status/SpartanJ/eepp/eepp-emscripten-build-check.yml?branch=develop&label=emscripten)](https://github.com/SpartanJ/eepp/actions?query=workflow%3Aemscripten) ## Features diff --git a/bin/assets/ui/breeze.css b/bin/assets/ui/breeze.css index 0e03ea909..93cad5519 100644 --- a/bin/assets/ui/breeze.css +++ b/bin/assets/ui/breeze.css @@ -56,14 +56,11 @@ TabWidget { } MarkdownView { + color: var(--font); background-color: var(--list-back); padding: 4dp; } -MarkdownView body { - color: var(--font); -} - MarkdownView p, MarkdownView ol, MarkdownView ul, diff --git a/include/eepp/ui/tools/htmlformatter.hpp b/include/eepp/ui/tools/htmlformatter.hpp index f3760d933..76ff4baab 100644 --- a/include/eepp/ui/tools/htmlformatter.hpp +++ b/include/eepp/ui/tools/htmlformatter.hpp @@ -9,6 +9,9 @@ namespace EE { namespace UI { namespace Tools { class EE_API HTMLFormatter { public: static std::string HTMLtoXML( const std::string& layoutString ); + + /** Converts HTML to XML, serializing only the children of the parsed body element. */ + static std::string HTMLBodyToXML( const std::string& layoutString ); }; }}} // namespace EE::UI::Tools diff --git a/premake4.lua b/premake4.lua index 534fc22a4..387cf30d0 100644 --- a/premake4.lua +++ b/premake4.lua @@ -654,7 +654,7 @@ function build_link_configuration( package_name, use_ee_icon ) add_cross_config_links() configuration "emscripten" - linkoptions { "-s TOTAL_MEMORY=536870912 -s ALLOW_MEMORY_GROWTH=1 -s USE_SDL=2" } + linkoptions { "-s TOTAL_MEMORY=536870912 -s ALLOW_MEMORY_GROWTH=1 -s USE_SDL=2 -s ENVIRONMENT=worker,web" } buildoptions { "-s USE_SDL=2" } buildoptions { "-s USE_PTHREADS=1" } linkoptions { "-s USE_PTHREADS=1 -sPTHREAD_POOL_SIZE=8" } diff --git a/premake5.lua b/premake5.lua index 788cfc0bc..7eb71be33 100644 --- a/premake5.lua +++ b/premake5.lua @@ -372,7 +372,7 @@ function build_base_configuration( package_name ) buildoptions { "/utf-8" } filter "system:emscripten" - buildoptions { "-O3 -s USE_SDL=2 -s PRECISE_F32=1 -s ENVIRONMENT=worker,web" } + buildoptions { "-O3 -s USE_SDL=2" } buildoptions { "-s USE_PTHREADS=1" } filter {} @@ -412,7 +412,7 @@ function build_base_cpp_configuration( package_name ) symbols "On" filter "system:emscripten" - buildoptions { "-O3 -s USE_SDL=2 -s PRECISE_F32=1 -s ENVIRONMENT=worker,web" } + buildoptions { "-O3 -s USE_SDL=2" } buildoptions { "-s USE_PTHREADS=1" } filter {} @@ -603,8 +603,8 @@ function build_link_configuration( package_name, use_ee_icon ) if package_name ~= "eepp" and package_name ~= "eepp-static" then targetextension ".html" end - linkoptions { "-O3 -s TOTAL_MEMORY=536870912 -s ALLOW_MEMORY_GROWTH=1 -s USE_SDL=2" } - buildoptions { "-O3 -s USE_SDL=2 -s PRECISE_F32=1 -s ENVIRONMENT=worker,web" } + linkoptions { "-O3 -s TOTAL_MEMORY=536870912 -s ALLOW_MEMORY_GROWTH=1 -s USE_SDL=2 -s ENVIRONMENT=worker,web" } + buildoptions { "-O3 -s USE_SDL=2" } buildoptions { "-s USE_PTHREADS=1" } linkoptions { "-s USE_PTHREADS=1 -sPTHREAD_POOL_SIZE=8" } diff --git a/src/eepp/ui/tools/htmlformatter.cpp b/src/eepp/ui/tools/htmlformatter.cpp index 13ca0653a..c3f669685 100644 --- a/src/eepp/ui/tools/htmlformatter.cpp +++ b/src/eepp/ui/tools/htmlformatter.cpp @@ -156,7 +156,28 @@ static void serializeGumboNodeToXML( GumboNode* node, std::string& out ) { } } -std::string HTMLFormatter::HTMLtoXML( const std::string& layoutString ) { +static GumboNode* findElement( GumboNode* node, GumboTag tag ) { + if ( !node ) + return nullptr; + if ( node->type == GUMBO_NODE_ELEMENT && node->v.element.tag == tag ) + return node; + + GumboVector* children = nullptr; + if ( node->type == GUMBO_NODE_DOCUMENT ) + children = &node->v.document.children; + else if ( node->type == GUMBO_NODE_ELEMENT ) + children = &node->v.element.children; + if ( !children ) + return nullptr; + + for ( unsigned int i = 0; i < children->length; ++i ) { + if ( auto* element = findElement( static_cast( children->data[i] ), tag ) ) + return element; + } + return nullptr; +} + +static std::string htmlToXML( const std::string& layoutString, bool bodyChildrenOnly ) { if ( layoutString.empty() ) return ""; @@ -174,7 +195,15 @@ std::string HTMLFormatter::HTMLtoXML( const std::string& layoutString ) { // 2. Serialize the AST into strict XML std::string strict_xml; - serializeGumboNodeToXML( output->root, strict_xml ); + if ( bodyChildrenOnly ) { + if ( auto* body = findElement( output->root, GUMBO_TAG_BODY ) ) { + GumboVector* children = &body->v.element.children; + for ( unsigned int i = 0; i < children->length; ++i ) + serializeGumboNodeToXML( static_cast( children->data[i] ), strict_xml ); + } + } else { + serializeGumboNodeToXML( output->root, strict_xml ); + } // 3. Cleanup Gumbo's memory gumbo_destroy_output( &kGumboDefaultOptions, output ); @@ -182,4 +211,12 @@ std::string HTMLFormatter::HTMLtoXML( const std::string& layoutString ) { return strict_xml; } +std::string HTMLFormatter::HTMLtoXML( const std::string& layoutString ) { + return htmlToXML( layoutString, false ); +} + +std::string HTMLFormatter::HTMLBodyToXML( const std::string& layoutString ) { + return htmlToXML( layoutString, true ); +} + }}} // namespace EE::UI::Tools diff --git a/src/eepp/ui/uimarkdownview.cpp b/src/eepp/ui/uimarkdownview.cpp index 56e5f4b95..70b9458b6 100644 --- a/src/eepp/ui/uimarkdownview.cpp +++ b/src/eepp/ui/uimarkdownview.cpp @@ -17,6 +17,7 @@ UIMarkdownView* UIMarkdownView::New() { UIMarkdownView::UIMarkdownView() : UILinearLayout( "markdownview", UIOrientation::Vertical ) { mWidthPolicy = SizePolicy::MatchParent; mHeightPolicy = SizePolicy::WrapContent; + getUISceneNode()->loadHTMLBaseCSS(); } Uint32 UIMarkdownView::getType() const { @@ -29,7 +30,7 @@ bool UIMarkdownView::isType( const Uint32& type ) const { void UIMarkdownView::loadFromString( std::string_view markdown ) { closeAllChildren(); - auto xhtml = Tools::HTMLFormatter::HTMLtoXML( Markdown::toXHTML( markdown ) ); + auto xhtml = Tools::HTMLFormatter::HTMLBodyToXML( Markdown::toXHTML( markdown ) ); getUISceneNode()->loadLayoutFromString( xhtml, this ); } diff --git a/src/tests/unit_tests/resource_prerequisite_tests.cpp b/src/tests/unit_tests/resource_prerequisite_tests.cpp index 58acf7a45..fa9f82020 100644 --- a/src/tests/unit_tests/resource_prerequisite_tests.cpp +++ b/src/tests/unit_tests/resource_prerequisite_tests.cpp @@ -635,6 +635,35 @@ UTEST( ResourcePrerequisites, fontServiceFollowsScopeOwnershipAndDetachesRetaine Engine::destroySingleton(); } +UTEST( ResourcePrerequisites, distinctFallbackResourcesRemainInFallbackChain ) { + EE::Window::Window* window = createLifecycleTestWindow( "Multiple font fallback test" ); + ResourceScopePtr scope = ResourceScope::New(); + const std::string fontsPath = Sys::getProcessPath() + "assets/fonts/"; + FontTrueTypePtr baseFont = + FontTrueType::New( "base-font", fontsPath + "NotoNaskhArabic-Regular.ttf", *scope ); + FontTrueTypePtr cjkFallback = + FontTrueType::New( "fallback-font", fontsPath + "NotoSansKR-Regular.ttf", *scope ); + FontTrueTypePtr userFallback = + FontTrueType::New( "user-fallback-font", fontsPath + "NotoSansHebrew-Regular.ttf", *scope ); + ASSERT_TRUE( baseFont && baseFont->loaded() ); + ASSERT_TRUE( cjkFallback && cjkFallback->loaded() ); + ASSERT_TRUE( userFallback && userFallback->loaded() ); + + FontService& fontService = scope->getFontService(); + EXPECT_TRUE( fontService.addFallbackFont( cjkFallback ) ); + EXPECT_TRUE( fontService.addFallbackFont( userFallback ) ); + ASSERT_EQ( (size_t)2, fontService.getFallbackFonts().size() ); + EXPECT_EQ( cjkFallback.get(), fontService.getFallbackFonts()[0].get() ); + EXPECT_EQ( userFallback.get(), fontService.getFallbackFonts()[1].get() ); + + baseFont.reset(); + cjkFallback.reset(); + userFallback.reset(); + scope.reset(); + window->display( false ); + Engine::destroySingleton(); +} + UTEST( ResourcePrerequisites, fontFactoriesPublishIntoExplicitScope ) { EE::Window::Window* window = createLifecycleTestWindow( "Scoped font factories test" ); auto scope = ResourceScope::New(); diff --git a/src/tests/unit_tests/uihtml_tests.cpp b/src/tests/unit_tests/uihtml_tests.cpp index 480d1ef21..f47def9ac 100644 --- a/src/tests/unit_tests/uihtml_tests.cpp +++ b/src/tests/unit_tests/uihtml_tests.cpp @@ -2769,6 +2769,29 @@ UTEST( UIHTML, StyleSheetTraversalBoundaries ) { Engine::destroySingleton(); } +UTEST( UIHTML, MarkdownViewLoadsBodyChildrenIntoNativeTree ) { + init_ui_test(); + auto* sceneNode = SceneManager::instance()->getUISceneNode(); + StyleSheetParser parser; + ASSERT_TRUE( parser.loadFromString( std::string_view{ "MarkdownView p { color: red; }" } ) ); + sceneNode->setStyleSheet( parser.getStyleSheet() ); + + auto* markdownView = UIMarkdownView::New(); + markdownView->setParent( sceneNode->getRoot() ); + markdownView->loadFromString( "Paragraph" ); + sceneNode->update( Seconds( 1 ) ); + + auto* paragraph = markdownView->findByTag( "p" ); + ASSERT_TRUE( paragraph != nullptr ); + ASSERT_TRUE( paragraph->isType( UI_TYPE_RICHTEXT ) ); + EXPECT_TRUE( markdownView->findByType( UI_TYPE_HTML_HTML ) == nullptr ); + EXPECT_TRUE( markdownView->findByType( UI_TYPE_HTML_BODY ) == nullptr ); + EXPECT_TRUE( paragraph->getStyleSheetParentElement() == markdownView ); + EXPECT_TRUE( Color::Red == paragraph->asType()->getFontColor() ); + + Engine::destroySingleton(); +} + UTEST( UIHTML, StyleSheetSiblingCombinators ) { Engine::instance()->createWindow( WindowSettings( 1024, 768, "CSS Sibling Test", WindowStyle::Default, WindowBackend::Default, diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index 458f36de9..c28160671 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -4537,7 +4537,10 @@ void App::init( InitParameters& params ) { defaultResourceScope().getFontService().addFallbackFont( mFallbackFont ); if ( mConfig.ui.fallbackFont != "fonts/DroidSansFallbackFull.ttf" ) { - mUserFallbackFont = loadFont( "fallback-font", mConfig.ui.fallbackFont ); + // Keep the user fallback under a distinct resource key. Publishing it as + // "fallback-font" would replace the built-in CJK font in the default scope and remove + // that font from FontService's fallback chain. + mUserFallbackFont = loadFont( "user-fallback-font", mConfig.ui.fallbackFont ); if ( mUserFallbackFont ) defaultResourceScope().getFontService().addFallbackFont( mUserFallbackFont ); }