mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-06-04 20:46:29 +03:00
Minor fix in UIHTMLTable layouting.
Add *very* basic HTML test. And *very* basic HTML demo. This is more than basic, it's just that I need something to quickly test stuff.
This commit is contained in:
@@ -81,7 +81,7 @@ Uint32 Text::stringToStyleFlag( const std::string& str ) {
|
||||
flags |= Text::Bold;
|
||||
else if ( "italic" == cur )
|
||||
flags |= Text::Italic;
|
||||
else if ( "strikethrough" == cur )
|
||||
else if ( "strikethrough" == cur || "line-through" == cur )
|
||||
flags |= Text::StrikeThrough;
|
||||
else if ( "shadowed" == cur || "shadow" == cur )
|
||||
flags |= Text::Shadow;
|
||||
|
||||
@@ -112,7 +112,7 @@ SyntaxColorScheme::Style parseStyle(
|
||||
style.style |= Text::Italic;
|
||||
else if ( "underline" == val || "underlined" == val )
|
||||
style.style |= Text::Underlined;
|
||||
else if ( "strikethrough" == val )
|
||||
else if ( "strikethrough" == val || "line-through" == val )
|
||||
style.style |= Text::StrikeThrough;
|
||||
else if ( "shadow" == val )
|
||||
style.style |= Text::Shadow;
|
||||
|
||||
@@ -98,6 +98,7 @@ void UIHTMLTable::updateLayout() {
|
||||
for ( Uint32 i = 0; i < end - start; ++i ) {
|
||||
UIHTMLTableCell* cell = mCells[start + i];
|
||||
cell->setLayoutWidthPolicy( SizePolicy::WrapContent );
|
||||
cell->mSize.x = mSize.x;
|
||||
cell->updateLayout();
|
||||
Uint32 cellColspan = cell->getColspan();
|
||||
if ( cellColspan == 1 ) {
|
||||
@@ -216,7 +217,8 @@ void UIHTMLTable::updateLayout() {
|
||||
mRows[rowCount - 1]->setPixelsPosition( mPaddingPx.Left, 0 );
|
||||
|
||||
if ( mHeightPolicy == SizePolicy::WrapContent ) {
|
||||
setInternalPixelsHeight( headHeight + bodyHeight + footerHeight + mPaddingPx.Bottom );
|
||||
setInternalPixelsHeight( mPaddingPx.Top + headHeight + bodyHeight + footerHeight +
|
||||
mPaddingPx.Bottom );
|
||||
}
|
||||
|
||||
mPacking = false;
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
#include <eepp/ui/uitooltip.hpp>
|
||||
#include <eepp/ui/uiwidgetcreator.hpp>
|
||||
#include <eepp/ui/uiwindow.hpp>
|
||||
#include <eepp/window/engine.hpp>
|
||||
#include <eepp/window/window.hpp>
|
||||
|
||||
#define PUGIXML_HEADER_ONLY
|
||||
#include <pugixml/pugixml.hpp>
|
||||
|
||||
@@ -318,7 +320,9 @@ std::vector<UIWidget*> UISceneNode::loadNode( pugi::xml_node node, Node* parent,
|
||||
} else if ( String::iequals( widget.name(), "link" ) ) {
|
||||
auto type = widget.attribute( "type" );
|
||||
auto href = widget.attribute( "href" );
|
||||
if ( !type.empty() && !href.empty() && String::iequals( type.value(), "text/css" ) ) {
|
||||
auto rel = widget.attribute( "rel" );
|
||||
if ( !href.empty() && ( String::iequals( type.value(), "text/css" ) ||
|
||||
String::iequals( rel.value(), "stylesheet" ) ) ) {
|
||||
loadCSS( href.as_string() );
|
||||
}
|
||||
}
|
||||
@@ -1087,29 +1091,46 @@ void UISceneNode::loadFontFaces( const StyleSheetStyleVector& styles ) {
|
||||
}
|
||||
}
|
||||
|
||||
void UISceneNode::loadCSS( URI uri ) {
|
||||
std::string scheme = uri.getScheme();
|
||||
if ( !mURI.empty() && scheme.empty() ) {
|
||||
std::string pathStart = mURI.getPath();
|
||||
FileSystem::dirAddSlashAtEnd( pathStart );
|
||||
std::string pathEnd = pathStart + uri.getPath();
|
||||
uri = mURI;
|
||||
uri.setPath( pathEnd );
|
||||
}
|
||||
URI UISceneNode::solveRelativePath( URI uri ) {
|
||||
if ( mURI.empty() )
|
||||
return uri;
|
||||
|
||||
if ( "file" == scheme || ( scheme.empty() && FileSystem::fileExists( uri.getPath() ) ) ) {
|
||||
if ( mURI.getScheme().empty() )
|
||||
uri.setScheme( "file" );
|
||||
|
||||
if ( uri.getPath().empty() || uri.getPath().back() != '/' )
|
||||
uri.setPath( mURI.getPath() + uri.getPath() );
|
||||
|
||||
if ( uri.getScheme().empty() )
|
||||
uri.setScheme( mURI.getScheme() );
|
||||
|
||||
if ( uri.getAuthority().empty() )
|
||||
uri.setAuthority( mURI.getAuthority() );
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
void UISceneNode::loadCSS( URI uri ) {
|
||||
uri = solveRelativePath( uri );
|
||||
std::string url = uri.toString();
|
||||
Log::debug( "UISceneNode::loadCSS: %s", url );
|
||||
|
||||
if ( "file" == uri.getScheme() ||
|
||||
( uri.getScheme().empty() && FileSystem::fileExists( uri.getPath() ) ) ) {
|
||||
std::string filePath( uri.getPath() );
|
||||
std::string css;
|
||||
if ( FileSystem::fileExists( filePath ) && FileSystem::fileGet( filePath, css ) ) {
|
||||
combineStyleSheet( css, true, String::hash( uri.toString() ) );
|
||||
combineStyleSheet( css, true, String::hash( url ) );
|
||||
Log::debug( "UISceneNode::loadCSS: Loaded - %s", url );
|
||||
}
|
||||
} else if ( "http" == scheme || "https" == scheme ) {
|
||||
} else if ( "http" == uri.getScheme() || "https" == uri.getScheme() ) {
|
||||
Http::getAsync(
|
||||
[this, uri]( const Http&, Http::Request&, Http::Response& response ) {
|
||||
[this, url]( const Http&, Http::Request&, Http::Response& response ) {
|
||||
if ( !response.getBody().empty() ) {
|
||||
std::string css( response.getBody() );
|
||||
runOnMainThread( [css = std::move( css ), uri = std::move( uri ), this] {
|
||||
combineStyleSheet( css, true, String::hash( uri.toString() ) );
|
||||
runOnMainThread( [css = std::move( css ), url = std::move( url ), this] {
|
||||
combineStyleSheet( css, true, String::hash( url ) );
|
||||
Log::debug( "UISceneNode::loadCSS: Loaded - %s", url );
|
||||
} );
|
||||
}
|
||||
},
|
||||
@@ -1118,8 +1139,9 @@ void UISceneNode::loadCSS( URI uri ) {
|
||||
IOStream* stream = VFS::instance()->getFileFromPath( uri.getPath() );
|
||||
CSS::StyleSheetParser parser;
|
||||
if ( parser.loadFromStream( *stream ) ) {
|
||||
parser.getStyleSheet().setMarker( String::hash( uri.toString() ) );
|
||||
parser.getStyleSheet().setMarker( String::hash( url ) );
|
||||
combineStyleSheet( parser.getStyleSheet() );
|
||||
Log::debug( "UISceneNode::loadCSS: Loaded - %s", url );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1242,4 +1264,10 @@ void UISceneNode::setURI( const URI& uri ) {
|
||||
mURI = uri;
|
||||
}
|
||||
|
||||
void UISceneNode::openURL( URI uri ) {
|
||||
if ( mURLInterceptorCb && mURLInterceptorCb( uri ) )
|
||||
return;
|
||||
Engine::instance()->openURI( uri.toString() );
|
||||
}
|
||||
|
||||
}} // namespace EE::UI
|
||||
|
||||
@@ -341,7 +341,8 @@ bool UIScrollView::isTouchOverAllowedChildren() {
|
||||
bool ret = mViewType == ScrollViewType::Outside
|
||||
? !mVScroll->isMouseOverMeOrChildren() && !mHScroll->isMouseOverMeOrChildren()
|
||||
: true;
|
||||
return isMouseOverMeOrChildren() && mScrollView->isMouseOverMeOrChildren() && ret;
|
||||
return isMouseOverMeOrChildren() && mScrollView && mScrollView->isMouseOverMeOrChildren() &&
|
||||
ret;
|
||||
}
|
||||
|
||||
std::string UIScrollView::getPropertyString( const PropertyDefinition* propertyDef,
|
||||
@@ -437,7 +438,7 @@ bool UIScrollView::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
Uint32 UIScrollView::onMessage( const NodeMessage* Msg ) {
|
||||
switch ( Msg->getMsg() ) {
|
||||
case NodeMessage::MouseUp: {
|
||||
if ( mVScroll->isEnabled() && 0 != mScrollView->getSize().getHeight() &&
|
||||
if ( mScrollView && mVScroll->isEnabled() && 0 != mScrollView->getSize().getHeight() &&
|
||||
isTouchOverAllowedChildren() && Msg->getSender()->isUINode() &&
|
||||
!Msg->getSender()->asType<UINode>()->isScrollable() ) {
|
||||
if ( Msg->getFlags() & EE_BUTTON_WUMASK ) {
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <eepp/ui/uitextspan.hpp>
|
||||
#include <eepp/ui/uithememanager.hpp>
|
||||
#include <eepp/ui/uiwidgetcreator.hpp>
|
||||
#include <eepp/window/engine.hpp>
|
||||
|
||||
#define PUGIXML_HEADER_ONLY
|
||||
#include <pugixml/pugixml.hpp>
|
||||
@@ -574,7 +573,7 @@ Uint32 UIAnchorSpan::onMessage( const NodeMessage* Msg ) {
|
||||
switch ( Msg->getMsg() ) {
|
||||
case NodeMessage::MouseClick: {
|
||||
if ( !mHref.empty() && ( Msg->getFlags() & EE_BUTTON_LMASK ) )
|
||||
Engine::instance()->openURI( mHref );
|
||||
getUISceneNode()->openURL( mHref );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -610,7 +609,7 @@ const std::string& UIAnchorSpan::getHref() const {
|
||||
Uint32 UIAnchorSpan::onKeyDown( const KeyEvent& event ) {
|
||||
if ( event.getKeyCode() == KEY_KP_ENTER || event.getKeyCode() == KEY_RETURN ) {
|
||||
if ( !mHref.empty() ) {
|
||||
Engine::instance()->openURI( mHref );
|
||||
getUISceneNode()->openURL( mHref );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <eepp/ui/uitextview.hpp>
|
||||
#include <eepp/ui/uithememanager.hpp>
|
||||
#include <eepp/window/clipboard.hpp>
|
||||
#include <eepp/window/engine.hpp>
|
||||
|
||||
#define PUGIXML_HEADER_ONLY
|
||||
#include <pugixml/pugixml.hpp>
|
||||
|
||||
@@ -985,7 +985,7 @@ UIAnchor::UIAnchor( const std::string& tag ) : UITextView( tag ) {
|
||||
onClick(
|
||||
[this]( const MouseEvent* ) {
|
||||
if ( !mHref.empty() )
|
||||
Engine::instance()->openURI( mHref );
|
||||
getUISceneNode()->openURL( mHref );
|
||||
},
|
||||
EE_BUTTON_LEFT );
|
||||
}
|
||||
@@ -1019,7 +1019,7 @@ const std::string& UIAnchor::getHref() const {
|
||||
Uint32 UIAnchor::onKeyDown( const KeyEvent& event ) {
|
||||
if ( event.getKeyCode() == KEY_KP_ENTER || event.getKeyCode() == KEY_RETURN ) {
|
||||
if ( !mHref.empty() ) {
|
||||
Engine::instance()->openURI( mHref );
|
||||
getUISceneNode()->openURL( mHref );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,16 +166,12 @@ void UIWidgetCreator::createBaseWidgetList() {
|
||||
};
|
||||
registeredWidget["center"] = [] {
|
||||
auto center = UIRichText::NewWithTag( "center" );
|
||||
center->setLayoutWidthPolicy( SizePolicy::WrapContent );
|
||||
// center->setLayoutWidthPolicy( SizePolicy::WrapContent );
|
||||
return center;
|
||||
};
|
||||
registeredWidget["html"] = [] {
|
||||
return UILinearLayout::NewVerticalWidthMatchParent( "html" );
|
||||
};
|
||||
registeredWidget["html"] = [] { return UIRichText::NewWithTag( "html" ); };
|
||||
registeredWidget["head"] = [] { return UIWidget::NewWithTag( "head" ); };
|
||||
registeredWidget["body"] = [] {
|
||||
return UILinearLayout::NewVerticalWidthMatchParent( "body" );
|
||||
};
|
||||
registeredWidget["body"] = [] { return UIRichText::NewWithTag( "body" ); };
|
||||
registeredWidget["table"] = UIHTMLTable::New;
|
||||
registeredWidget["tr"] = UIHTMLTableRow::New;
|
||||
registeredWidget["thead"] = UIHTMLTableHead::New;
|
||||
|
||||
93
src/examples/ui_html/ui_html.cpp
Normal file
93
src/examples/ui_html/ui_html.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
#include <eepp/ee.hpp>
|
||||
|
||||
EE_MAIN_FUNC int main( int, char** ) {
|
||||
UIApplication app( { 1280, 720, "eepp - UI HTML Example" } );
|
||||
|
||||
Log::instance()->setLogLevelThreshold( LogLevel::Debug );
|
||||
Log::instance()->setLogToStdOut( true );
|
||||
Log::instance()->setLiveWrite( true );
|
||||
|
||||
auto win = app.getWindow();
|
||||
auto ui = app.getUI();
|
||||
|
||||
ui->setColorSchemePreference( ColorSchemeExtPreference::Light );
|
||||
|
||||
ui->loadLayoutFromString( R"xml(
|
||||
<vbox layout_width="match_parent" layout_height="match_parent">
|
||||
<hbox layout_width="match_parent" layout_height="wrap_content">
|
||||
<TextInput id="url_bar" layout_width="0" layout_weight="1"
|
||||
hint="@string(enter_address, Enter Address)" />
|
||||
</hbox>
|
||||
<ScrollView id="html_view" layout_width="match_parent" layout_height="0" layout_weight="1">
|
||||
<vbox layout_width="match_parent" layout_height="wrap_content" id="html_doc"></vbox>
|
||||
</ScrollView>
|
||||
</vbox>
|
||||
)xml" );
|
||||
|
||||
auto urlBar = ui->find( "url_bar" )->asType<UITextInput>();
|
||||
auto mainContainer = ui->find( "html_doc" );
|
||||
|
||||
const auto loadDocument = [&]( URI url ) {
|
||||
static String::HashType prevURL = 0;
|
||||
std::string data;
|
||||
if ( !url.getScheme().empty() ) {
|
||||
if ( url.getScheme() == "https" || url.getScheme() == "http" ) {
|
||||
auto response = Http::get( url, Seconds( 5 ) );
|
||||
data = response.getBody();
|
||||
} else if ( url.getScheme() == "file" ) {
|
||||
FileSystem::fileGet( url.getPath(), data );
|
||||
}
|
||||
} else if ( !url.getPath().empty() && url.getPath().front() == '/' ) {
|
||||
FileSystem::fileGet( url.getPath(), data );
|
||||
}
|
||||
|
||||
if ( !data.empty() ) {
|
||||
if ( url.getPath().empty() || url.getPath().back() != '/' ) {
|
||||
if ( url.getScheme() == "file" &&
|
||||
!FileSystem::fileExtension( url.getPath() ).empty() ) {
|
||||
url.setPath( FileSystem::fileRemoveFileName( url.getPath() ) );
|
||||
}
|
||||
url.setPath( url.getPath() + "/" );
|
||||
}
|
||||
mainContainer->closeAllChildren();
|
||||
if ( prevURL )
|
||||
ui->getStyleSheet().removeAllWithMarker( prevURL );
|
||||
ui->setURI( url );
|
||||
auto hash = String::hash( url.toString() );
|
||||
ui->loadLayoutFromString( data, mainContainer, hash );
|
||||
prevURL = hash;
|
||||
}
|
||||
};
|
||||
|
||||
urlBar->on( Event::OnPressEnter,
|
||||
[&]( auto event ) { loadDocument( urlBar->getText().toUtf8() ); } );
|
||||
|
||||
ui->setURLInterceptorCb( [&]( URI uri ) {
|
||||
loadDocument( ui->solveRelativePath( uri ) );
|
||||
return true;
|
||||
} );
|
||||
|
||||
win->getInput()->pushCallback( [&loadDocument]( InputEvent* event ) {
|
||||
switch ( event->Type ) {
|
||||
case InputEvent::FileDropped: {
|
||||
std::string file( event->file.file );
|
||||
loadDocument( "file://" + file );
|
||||
break;
|
||||
}
|
||||
case InputEvent::TextDropped: {
|
||||
loadDocument( event->textdrop.text );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} );
|
||||
|
||||
app.getUI()->on( Event::KeyUp, [&app]( const Event* event ) {
|
||||
if ( event->asKeyEvent()->getKeyCode() == KEY_F11 ) {
|
||||
UIWidgetInspector::create( app.getUI() );
|
||||
}
|
||||
} );
|
||||
|
||||
return app.run();
|
||||
}
|
||||
90
src/tests/unit_tests/uihtml_tests.cpp
Normal file
90
src/tests/unit_tests/uihtml_tests.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
// #include "compareimages.hpp"
|
||||
#include "utest.h"
|
||||
|
||||
#include <eepp/graphics/fontfamily.hpp>
|
||||
#include <eepp/graphics/fonttruetype.hpp>
|
||||
#include <eepp/scene/scenemanager.hpp>
|
||||
#include <eepp/system/filesystem.hpp>
|
||||
#include <eepp/system/sys.hpp>
|
||||
#include <eepp/ui/tools/uiwidgetinspector.hpp>
|
||||
#include <eepp/ui/uihtmltable.hpp>
|
||||
#include <eepp/ui/uiscenenode.hpp>
|
||||
#include <eepp/ui/uitextspan.hpp>
|
||||
#include <eepp/ui/uithememanager.hpp>
|
||||
#include <eepp/window/engine.hpp>
|
||||
#include <eepp/window/input.hpp>
|
||||
|
||||
using namespace EE;
|
||||
using namespace EE::Graphics;
|
||||
using namespace EE::Window;
|
||||
using namespace EE::Scene;
|
||||
using namespace EE::UI;
|
||||
using namespace EE::UI::Tools;
|
||||
|
||||
UTEST( UIHTMLTable, complexLayout ) {
|
||||
auto win = Engine::instance()->createWindow(
|
||||
WindowSettings( 1024, 650, "HTML Tables Test", WindowStyle::Default, WindowBackend::Default,
|
||||
32, {}, 1, false, true ),
|
||||
ContextSettings( false, ContextSettings::FrameRateLimitScreenRefreshRate, 4 ) );
|
||||
FileSystem::changeWorkingDirectory( Sys::getProcessPath() );
|
||||
|
||||
#ifdef EE_DEBUG
|
||||
Log::instance()->setLiveWrite( true );
|
||||
Log::instance()->setLogToStdOut( true );
|
||||
#endif
|
||||
|
||||
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( Sys::getProcessPath() + "assets/html/" );
|
||||
sceneNode->loadLayoutFromFile( "assets/html/hn_thread_test.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( votelinks->getPixelsSize().getWidth(), 0 );
|
||||
EXPECT_GT( votelinks->getPixelsSize().getHeight(), 0 );
|
||||
|
||||
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_LT( totalTds, mainTotal );
|
||||
|
||||
// compareImages( utest_state, utest_result, win, "eepp-uihtmltable-complex-layout", "html" );
|
||||
|
||||
Engine::destroySingleton();
|
||||
}
|
||||
Reference in New Issue
Block a user