mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-05-28 17:16:29 +03:00
Fix hex color string parsing.
UIMarkdownView now uses the HTMLFormatter::HTMLtoXML. Removed formatting hack to force strict XML from HTML, now we should always use HTMLFormatter::HTMLtoXML.
This commit is contained in:
@@ -377,7 +377,7 @@
|
||||
"working_dir": "${project_root}/bin"
|
||||
},
|
||||
{
|
||||
"args": "-d1 file:///home/downloads/files/svn/eepp/bin/unit_tests/assets/html/blog_main_incorrect_widths.html",
|
||||
"args": "-c system --hn-dark",
|
||||
"command": "${project_root}/bin/eepp-ui-html-debug",
|
||||
"name": "eepp-ui-html-debug",
|
||||
"working_dir": "${project_root}/bin"
|
||||
|
||||
2
TODO.md
2
TODO.md
@@ -9,8 +9,6 @@
|
||||
|
||||
* Implement TableView and TreeView properties.
|
||||
|
||||
* Add automatic font-fallback for lang scripts
|
||||
|
||||
* Implement support for setting and creating a model from the XML.
|
||||
|
||||
* Implement support for very simple state-changes from the XML file (ex: onclick="toggleclass(x)").
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace EE { namespace System {
|
||||
namespace {
|
||||
|
||||
template <typename T> inline T _round( T r ) {
|
||||
return ( r > 0.0f ) ? eefloor( r + 0.5f ) : eeceil( r - 0.5f );
|
||||
return ( r > 0.0f ) ? std::floor( r + 0.5f ) : std::ceil( r - 0.5f );
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -504,21 +504,29 @@ Color Color::fromString( std::string str ) {
|
||||
return Color::Transparent;
|
||||
|
||||
if ( str[0] == '#' ) {
|
||||
str = str.substr( 1 );
|
||||
str.erase( 0, 1 );
|
||||
|
||||
size = str.size();
|
||||
|
||||
if ( 0 == size )
|
||||
if ( size != 3 && size != 4 && size != 6 && size != 8 )
|
||||
return Color::Transparent;
|
||||
|
||||
if ( size < 6 ) {
|
||||
for ( std::size_t i = size; i < 6; i++ )
|
||||
str += str[size - 1];
|
||||
// Expand shorthand CSS notation
|
||||
if ( size == 3 || size == 4 ) {
|
||||
std::string expanded;
|
||||
expanded.reserve( size * 2 );
|
||||
|
||||
size = 6;
|
||||
for ( char c : str ) {
|
||||
expanded += c;
|
||||
expanded += c;
|
||||
}
|
||||
|
||||
if ( 6 == size )
|
||||
str = expanded;
|
||||
size = str.size();
|
||||
}
|
||||
|
||||
// Add opaque alpha if omitted
|
||||
if ( size == 6 )
|
||||
str += "FF";
|
||||
|
||||
return Color( std::strtoul( str.c_str(), NULL, 16 ) );
|
||||
@@ -715,8 +723,7 @@ Color Color::fromString( std::string str ) {
|
||||
return Color::Transparent;
|
||||
}
|
||||
|
||||
template <typename StringType>
|
||||
bool Color::isColorStringT( StringType str, bool searchColorNames ) {
|
||||
template <typename StringType> bool Color::isColorStringT( StringType str, bool searchColorNames ) {
|
||||
if ( str.empty() )
|
||||
return false;
|
||||
|
||||
@@ -831,12 +838,18 @@ bool Color::validHexColorString( String::View hexColor ) {
|
||||
}
|
||||
|
||||
bool Color::validHexColorString( std::string_view hexColor ) {
|
||||
if ( hexColor.size() < 2 || hexColor[0] != '#' )
|
||||
if ( hexColor.empty() || hexColor[0] != '#' )
|
||||
return false;
|
||||
|
||||
const size_t len = hexColor.size() - 1;
|
||||
|
||||
if ( len != 3 && len != 4 && len != 6 && len != 8 )
|
||||
return false;
|
||||
|
||||
for ( size_t i = 1; i < hexColor.size(); i++ ) {
|
||||
if ( !( String::isNumber( hexColor[i] ) || ( hexColor[i] >= 'a' && hexColor[i] <= 'f' ) ||
|
||||
( hexColor[i] >= 'A' && hexColor[i] <= 'F' ) ) ) {
|
||||
char c = hexColor[i];
|
||||
|
||||
if ( !( String::isNumber( c ) || ( c >= 'a' && c <= 'f' ) || ( c >= 'A' && c <= 'F' ) ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <eepp/ui/doc/markdownhelper.hpp>
|
||||
#include <eepp/ui/tools/htmlformatter.hpp>
|
||||
#include <eepp/ui/uimarkdownview.hpp>
|
||||
#include <eepp/ui/uiscenenode.hpp>
|
||||
|
||||
@@ -28,8 +29,7 @@ bool UIMarkdownView::isType( const Uint32& type ) const {
|
||||
|
||||
void UIMarkdownView::loadFromString( std::string_view markdown ) {
|
||||
closeAllChildren();
|
||||
auto xhtml = Markdown::toXHTML( markdown );
|
||||
// printf( "%s", xhtml.c_str() );
|
||||
auto xhtml = Tools::HTMLFormatter::HTMLtoXML( Markdown::toXHTML( markdown ) );
|
||||
getUISceneNode()->loadLayoutFromString( xhtml, this );
|
||||
}
|
||||
|
||||
|
||||
@@ -679,6 +679,14 @@ Float UIRichText::getLineHeightPx() const {
|
||||
mLineHeightPxCache = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Temporal hack until we support calc
|
||||
if ( mLineHeightEq.find( "calc(" ) != std::string::npos ||
|
||||
mLineHeightEq.find( "var(" ) != std::string::npos ) {
|
||||
mLineHeightPxCache = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool isUnitless = !mLineHeightEq.empty();
|
||||
for ( char c : mLineHeightEq ) {
|
||||
if ( c != '-' && c != '+' && c != '.' && !String::isNumber( c, false ) ) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include "eepp/ui/uirichtext.hpp"
|
||||
#include <algorithm>
|
||||
#include <eepp/core/string.hpp>
|
||||
#include <eepp/graphics/fontmanager.hpp>
|
||||
@@ -36,9 +35,6 @@ using namespace EE::Network;
|
||||
|
||||
namespace EE { namespace UI {
|
||||
|
||||
static constexpr std::string_view VOIDTAG_REGEX =
|
||||
"(<(?:img|br|hr|input|meta|link)\\b[^>]*?)(?<!/)>";
|
||||
|
||||
UISceneNode* UISceneNode::New( EE::Window::Window* window ) {
|
||||
return eeNew( UISceneNode, ( window ) );
|
||||
}
|
||||
@@ -543,26 +539,14 @@ UIWidget* UISceneNode::loadLayoutFromFile( const std::string& layoutPath, Node*
|
||||
|
||||
UIWidget* UISceneNode::loadLayoutFromString( const char* layoutString, Node* parent,
|
||||
const Uint32& marker ) {
|
||||
RegEx voidTagsRegex( VOIDTAG_REGEX );
|
||||
|
||||
pugi::xml_document doc;
|
||||
pugi::xml_parse_result result;
|
||||
std::string fixedLayout;
|
||||
bool needsReplacements = voidTagsRegex.matches( layoutString );
|
||||
|
||||
if ( needsReplacements ) {
|
||||
fixedLayout = voidTagsRegex.gsub( layoutString, "%1 />" );
|
||||
result =
|
||||
doc.load_string( fixedLayout.c_str(), pugi::parse_default | pugi::parse_ws_pcdata );
|
||||
} else {
|
||||
result = doc.load_string( layoutString, pugi::parse_default | pugi::parse_ws_pcdata );
|
||||
}
|
||||
pugi::xml_parse_result result =
|
||||
doc.load_string( layoutString, pugi::parse_default | pugi::parse_ws_pcdata );
|
||||
|
||||
if ( result ) {
|
||||
return loadLayoutNodes( doc.first_child(), NULL != parent ? parent : this, marker );
|
||||
} else {
|
||||
Log::error( "Couldn't load UI Layout from string: %s",
|
||||
needsReplacements ? fixedLayout.c_str() : layoutString );
|
||||
Log::error( "Couldn't load UI Layout from string: %s", layoutString );
|
||||
Log::error( "Error description: %s", result.description() );
|
||||
Log::error( "Error offset: %d", result.offset );
|
||||
Log::error( "Error context: %s", getErrorContext( result.offset, layoutString ) );
|
||||
@@ -578,28 +562,15 @@ UIWidget* UISceneNode::loadLayoutFromString( const std::string& layoutString, No
|
||||
|
||||
UIWidget* UISceneNode::loadLayoutFromMemory( const void* buffer, Int32 bufferSize, Node* parent,
|
||||
const Uint32& marker ) {
|
||||
RegEx voidTagsRegex( VOIDTAG_REGEX );
|
||||
|
||||
pugi::xml_document doc;
|
||||
pugi::xml_parse_result result;
|
||||
std::string_view layoutString( static_cast<const char*>( buffer ), bufferSize );
|
||||
std::string fixedLayout;
|
||||
bool needsReplacements =
|
||||
voidTagsRegex.matches( static_cast<const char*>( buffer ), 0, nullptr, bufferSize );
|
||||
|
||||
if ( needsReplacements ) {
|
||||
fixedLayout = voidTagsRegex.gsub( layoutString.data(), "%1 />" );
|
||||
result = doc.load_buffer( fixedLayout.c_str(), fixedLayout.size(),
|
||||
pugi::parse_default | pugi::parse_ws_pcdata );
|
||||
} else {
|
||||
result = doc.load_buffer( buffer, bufferSize, pugi::parse_default | pugi::parse_ws_pcdata );
|
||||
}
|
||||
pugi::xml_parse_result result =
|
||||
doc.load_buffer( buffer, bufferSize, pugi::parse_default | pugi::parse_ws_pcdata );
|
||||
|
||||
if ( result ) {
|
||||
return loadLayoutNodes( doc.first_child(), NULL != parent ? parent : this, marker );
|
||||
} else {
|
||||
Log::error( "Couldn't load UI Layout from memory: %s",
|
||||
needsReplacements ? fixedLayout.c_str() : layoutString.data() );
|
||||
Log::error( "Couldn't load UI Layout from memory: %s", layoutString.data() );
|
||||
Log::error( "Error description: %s", result.description() );
|
||||
Log::error( "Error offset: %d", result.offset );
|
||||
Log::error( "Error context: %s",
|
||||
@@ -620,32 +591,17 @@ UIWidget* UISceneNode::loadLayoutFromStream( IOStream& stream, Node* parent,
|
||||
TScopedBuffer<char> scopedBuffer( bufferSize );
|
||||
stream.read( scopedBuffer.get(), scopedBuffer.length() );
|
||||
|
||||
RegEx voidTagsRegex( VOIDTAG_REGEX );
|
||||
|
||||
pugi::xml_document doc;
|
||||
pugi::xml_parse_result result;
|
||||
std::string_view layoutString( scopedBuffer.get(), scopedBuffer.length() );
|
||||
std::string fixedLayout;
|
||||
bool needsReplacements =
|
||||
voidTagsRegex.matches( scopedBuffer.get(), 0, nullptr, scopedBuffer.length() );
|
||||
std::string_view contents;
|
||||
|
||||
if ( needsReplacements ) {
|
||||
fixedLayout = voidTagsRegex.gsub( layoutString.data(), "%1 />" );
|
||||
result = doc.load_buffer( fixedLayout.c_str(), fixedLayout.size(),
|
||||
pugi::parse_default | pugi::parse_ws_pcdata );
|
||||
contents = fixedLayout;
|
||||
} else {
|
||||
result = doc.load_buffer( scopedBuffer.get(), scopedBuffer.length(),
|
||||
pugi::xml_parse_result result = doc.load_buffer( scopedBuffer.get(), scopedBuffer.length(),
|
||||
pugi::parse_default | pugi::parse_ws_pcdata );
|
||||
contents = std::string_view( scopedBuffer.get(), scopedBuffer.length() );
|
||||
}
|
||||
|
||||
if ( result ) {
|
||||
return loadLayoutNodes( doc.first_child(), NULL != parent ? parent : this, marker );
|
||||
} else {
|
||||
Log::error( "Couldn't load UI Layout from stream: %s",
|
||||
needsReplacements ? fixedLayout.c_str() : layoutString.data() );
|
||||
Log::error( "Couldn't load UI Layout from stream: %s", layoutString.data() );
|
||||
Log::error( "Error description: %s", result.description() );
|
||||
Log::error( "Error offset: %d", result.offset );
|
||||
Log::error( "Error context: %s", getErrorContext( result.offset, contents ) );
|
||||
@@ -1531,6 +1487,8 @@ Font* UISceneNode::getFontFromNamesList( std::string_view names, Uint32 fontStyl
|
||||
|
||||
font = fm->getByName( fontFamily );
|
||||
|
||||
// Remove the font style part (ex: `Arial#bold` to `Arial`)
|
||||
// We need this for SystemFontResolver::genericFamilyFromName
|
||||
if ( fontStyle )
|
||||
fontFamily.resize( size );
|
||||
|
||||
@@ -1548,12 +1506,9 @@ Font* UISceneNode::getFontFromNamesList( std::string_view names, Uint32 fontStyl
|
||||
font = fm->getByName( fontFamily );
|
||||
}
|
||||
|
||||
return font != nullptr;
|
||||
},
|
||||
',' );
|
||||
|
||||
if ( font == nullptr && SystemFontResolver::isEnabled() ) {
|
||||
FontWeight weight = ( fontStyle & Text::Bold ) ? FontWeight::Bold : FontWeight::Normal;
|
||||
FontWeight weight =
|
||||
( fontStyle & Text::Bold ) ? FontWeight::Bold : FontWeight::Normal;
|
||||
FontDesc desc = SystemFontResolver::instance()->resolveFromNamesList(
|
||||
std::string{ names }, weight, fontStyle & Text::Italic );
|
||||
if ( !desc.path.empty() ) {
|
||||
@@ -1562,7 +1517,7 @@ Font* UISceneNode::getFontFromNamesList( std::string_view names, Uint32 fontStyl
|
||||
family += "#" + Text::styleFlagToString( fontStyle );
|
||||
|
||||
if ( ( font = fm->getByName( family ) ) )
|
||||
return font;
|
||||
return true;
|
||||
|
||||
FontTrueType* ttf = FontTrueType::New( family, desc.path, desc.faceIndex );
|
||||
if ( ttf && ttf->loaded() ) {
|
||||
@@ -1570,7 +1525,8 @@ Font* UISceneNode::getFontFromNamesList( std::string_view names, Uint32 fontStyl
|
||||
Uint32 weightStyle = fontStyle & ( Text::Bold | Text::Italic );
|
||||
if ( weightStyle ) {
|
||||
Font* regular = fm->getByName( desc.family );
|
||||
if ( regular && regular != font && regular->getType() == FontType::TTF ) {
|
||||
if ( regular && regular != font &&
|
||||
regular->getType() == FontType::TTF ) {
|
||||
auto* regularFT = static_cast<FontTrueType*>( regular );
|
||||
if ( weightStyle == Text::Bold )
|
||||
regularFT->setBoldFont( ttf );
|
||||
@@ -1584,6 +1540,10 @@ Font* UISceneNode::getFontFromNamesList( std::string_view names, Uint32 fontStyl
|
||||
}
|
||||
}
|
||||
|
||||
return font != nullptr;
|
||||
},
|
||||
',' );
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
|
||||
@@ -1118,7 +1118,7 @@ UTEST( UIHTMLBody, maxWidthResizingBug ) {
|
||||
|
||||
std::string htmlContent;
|
||||
FileSystem::fileGet( "assets/html/dwarmstrong/dwarmstrong.html", htmlContent );
|
||||
sceneNode->loadLayoutFromString( htmlContent );
|
||||
sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( htmlContent ) );
|
||||
|
||||
sceneNode->getRoot()->setSize( 1024, 768 );
|
||||
sceneNode->updateDirtyLayouts();
|
||||
|
||||
Reference in New Issue
Block a user