diff --git a/bin/assets/layouts/test.css b/bin/assets/layouts/test.css index a9c8ea8ef..ed025b670 100644 --- a/bin/assets/layouts/test.css +++ b/bin/assets/layouts/test.css @@ -502,6 +502,13 @@ TabWidget { margin-right: 8dp; } +#tm { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAA/UlEQVR42u3ZsQkAMAhFwW/I/iubIQIp4l1vIw8srE46jLWsQAAIAAEgAASAABAAAkAACAABIAAEwKf21bRH8vAAkqRU4AQgAASAABAAAkAACAABIAAEgAAQAAJAAAgAASAABIAAEAACQAAIAAEgAASAABAAAkAACAABIAAEgAAQAAJAAAgAASAABIAAEAACQAAIAAEgAASAABAAAkAACAABIAAEgAAQAAJAAAKwAgEgAASAABAAAkAACAABIAAEgAAQAAJAAAgAASAABIAAEAACQAAIAAEgAASAABAAAkAACAABIAAEgAAQAAJAAAgAASAABIAAEAACQAA8dwB+IAUBZkHfLAAAAABJRU5ErkJggg=="), + url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='90' height='45'%3E%3Cpath d='M10 10h60' stroke='%2300F' stroke-width='5'/%3E%3Cpath d='M10 20h60' stroke='%230F0' stroke-width='5'/%3E%3Cpath d='M10 30h60' stroke='red' stroke-width='5'/%3E%3C/svg%3E"); + background-repeat: none; + background-position: 0 0, 64dp 0dp; +} + @media screen and (max-width: 1024px) { #lvbox { diff --git a/docs/articles/cssspecification.md b/docs/articles/cssspecification.md index e618177e4..6940da29a 100644 --- a/docs/articles/cssspecification.md +++ b/docs/articles/cssspecification.md @@ -280,6 +280,10 @@ Sets if the element skin background should be expanded to the element dimensions Read [background-image](https://developer.mozilla.org/en-US/docs/Web/CSS/background-image) documentation. +* Applicable to: Any element +* Data Type: [resource-path](#resource-path-data-type) +* Default value: `none` + --- ### background-position-x @@ -601,6 +605,10 @@ Same as [background-color](#background-color) but for the foreground. Same as [background-image](#background-image) but for the foreground. +* Applicable to: Any element +* Data Type: [resource-path](#resource-path-data-type) +* Default value: `none` + --- ### foreground-position-x @@ -776,7 +784,7 @@ The mode defines the visibility of the horizontal scroll. Sets an icon to an element that support icons. * Applicable to: EE::UI::UIImage (Image), EE::UI::UIPushButton (PushButton), EE::UI::UITab (Tab) -* Data Type: resource-path (`url()`) +* Data Type: [resource-path](#resource-path-data-type) * Default value: _No value_ --- @@ -1550,35 +1558,9 @@ Sets the space ocuppied by the first view contained by the splitter. Sets the source of a resource in an element that supports source. * Applicable to: EE::UI::UIImage (Image), EE::UI::UISprite (Sprite), EE::UI::UITextureRegion (TextureRegion) -* Data Type: [string](#string-data-type) +* Data Type: [resource-path](#resource-path-data-type) * Default value: _No value_ -Syntax: - -For Image (all the examples are valid). - -For a TextureRegion only the examples with: @textureregion, @drawable, drawable_resource_name -from a texture region resource are valid. - -For a Sprite only the examples with: all the examples are valid except for @9p and http/s resources. - -```CSS -src: file://assets/icon/ee.png; /** relative path to the current working diretory */ -src: "file://assets/icon/ee.png"; -src: https://raw.githubusercontent.com/SpartanJ/eepp/develop/bin/assets/icon/ee.png; -src: "https://raw.githubusercontent.com/SpartanJ/eepp/develop/bin/assets/icon/ee.png"; -src: url(file://assets/icon/ee.png); -src: url(https://raw.githubusercontent.com/SpartanJ/eepp/develop/bin/assets/icon/ee.png); -src: url("file://assets/icon/ee.png"); -src: @image/image_name_already_in_texture_factory; -src: @texture/image_name_already_in_texture_factory; -src: @textureregion/region_name_already_in_any_texture_atlas; -src: @sprite/sprite_name_already_in_any_texture_atlas; /* sprite pattern name **/ -src: @drawable/drawable_name_already_in_any_drawable_manager; /* drawable managers are any holder of image resources. This includes: texture atlases, textures, nine patchs. */ -src: @9p/nine_patch_resource_name_already_in_the_nine_path_manager; -src: drawable_resource_name; /** same as doing: @drawable/drawable_resource_name */ -``` - --- ### tabbar-hide-on-single-tab @@ -2314,6 +2296,45 @@ Read [number](https://developer.mozilla.org/en-US/docs/Web/CSS/number) documenta Read [position](https://developer.mozilla.org/en-US/docs/Web/CSS/position_value) documentation. +--- + +### resource-path (data-type) + +For EE::UI::UIImage (Image), background-image, foreground-image (all the examples are valid). + +For a EE::UI::UITextureRegion (TextureRegion) only the examples with: @textureregion, @drawable, +drawable_resource_name from a texture region resource are valid. + +For a EE::UI::UISprite (Sprite) only the examples with: all the examples are valid except for @9p +and http/s resources. + +Valid resources path: +```CSS +file://assets/icon/ee.png; /** relative path to the current working diretory */ +"file://assets/icon/ee.png"; +https://raw.githubusercontent.com/SpartanJ/eepp/develop/bin/assets/icon/ee.png; +"https://raw.githubusercontent.com/SpartanJ/eepp/develop/bin/assets/icon/ee.png"; +url(file://assets/icon/ee.png); +url(https://raw.githubusercontent.com/SpartanJ/eepp/develop/bin/assets/icon/ee.png); +url("file://assets/icon/ee.png"); +url(data:image/format;base64,data); +url(data:image/format,url-encoded-data); +@image/image_name_already_in_texture_factory; +@texture/image_name_already_in_texture_factory; +@textureregion/region_name_already_in_any_texture_atlas; +@sprite/sprite_name_already_in_any_texture_atlas; /* sprite pattern name **/ +@drawable/drawable_name_already_in_any_drawable_manager; /* drawable managers are any holder of image resources. This includes: texture atlases, textures, nine patchs. */ +@9p/nine_patch_resource_name_already_in_the_nine_path_manager; +drawable_resource_name; /** same as doing: @drawable/drawable_resource_name */ +linear-gradient(from_color, to_color); +linear-gradient(direction, from_color, to_color); /** valid directions are (without quotes): "to bottom", "to left", "to right", "to top". */ +circle(type, color); /** type (optional) can be (without quotes): "fill" or "solid" (filled), or "line" (lined). */ +rectangle(type, color, rotation, radius); /** type (optional) can be (without quotes): "fill" or "solid" (filled), or "line" (lined). rotation (optional) is a number in degress: "0ยบ" (without quotes). radius (optional), must be the last parameter. */ +triangle(type, color, "point_1.x point1.y, point_2.x point2.y, point_3.x point3.y") /** type can be (without quotes): "fill" or "solid" (filled), or "line" (lined). */ +poly(type, color, "point_1.x point1.y, point_2.x point2.y, ...") /** polygon. type (optional) can be (without quotes): "fill" or "solid" (filled), or "line" (lined). */ +``` + + --- ### string (data-type) diff --git a/src/eepp/graphics/drawablesearcher.cpp b/src/eepp/graphics/drawablesearcher.cpp index 96d9d9a69..e4829b3d6 100644 --- a/src/eepp/graphics/drawablesearcher.cpp +++ b/src/eepp/graphics/drawablesearcher.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include @@ -45,6 +47,61 @@ static Drawable* searchByNameInternal( const std::string& name ) { return drawable; } +static Drawable* parseDataURI( const std::string& name ) { + auto hash = MD5::fromString( name ).toHexString(); + Drawable* drawable = TextureFactory::instance()->getByName( hash ); + std::string::size_type formatAndEncSep; + if ( nullptr == drawable && + ( formatAndEncSep = name.find_first_of( ',' ) ) != std::string::npos ) { + std::string decodingType = "urldecode"; + std::string mediaType = name.substr( 0, formatAndEncSep ); + std::string format; + auto parts = String::split( mediaType, ';' ); + if ( parts.empty() ) + return nullptr; + auto formatNamePos = parts[0].find_first_of( '/' ); + if ( formatNamePos + 1 < mediaType.size() ) + format = parts[0].substr( formatNamePos + 1 ); + if ( parts.size() > 1 ) { + for ( size_t i = 1; i < parts.size(); ++i ) { + if ( "base64" == parts[i] ) { + decodingType = parts[i]; + break; + } + } + } + + Uint32 texId = 0; + if ( !format.empty() && + ( Image::isImageExtension( "." + format ) || format == "svg+xml" ) ) { + if ( decodingType == "base64" ) { + int fileStart = formatAndEncSep + 1; + int base64Size = name.size() - fileStart; + int bufSize = Base64::decodeSafeOutLen( base64Size ); + if ( bufSize <= 0 ) + return nullptr; + ScopedBuffer buffer( bufSize ); + int len = Base64::decode( base64Size, &name[fileStart], bufSize, buffer.get() ); + if ( len > 0 ) + texId = TextureFactory::instance()->loadFromMemory( buffer.get(), len ); + } else if ( decodingType == "urldecode" ) { + int fileStart = formatAndEncSep + 1; + std::string decoded( URI::decode( name.substr( fileStart ) ) ); + if ( !decoded.empty() ) + texId = TextureFactory::instance()->loadFromMemory( + (const unsigned char*)decoded.c_str(), decoded.size() ); + } + } + + if ( texId > 0 ) { + Texture* tex = TextureFactory::instance()->getTexture( texId ); + tex->setName( hash ); + drawable = tex; + } + } + return drawable; +} + Drawable* DrawableSearcher::searchByName( const std::string& name, bool firstSearchSprite ) { Drawable* drawable = NULL; @@ -118,6 +175,8 @@ Drawable* DrawableSearcher::searchByName( const std::string& name, bool firstSea } drawable = texture; + } else if ( String::startsWith( name, "data:image/" ) ) { + drawable = parseDataURI( name ); } else { drawable = searchByNameInternal( name ); } diff --git a/src/eepp/ui/css/drawableimageparser.cpp b/src/eepp/ui/css/drawableimageparser.cpp index 3e0e2b558..86536cee0 100644 --- a/src/eepp/ui/css/drawableimageparser.cpp +++ b/src/eepp/ui/css/drawableimageparser.cpp @@ -203,14 +203,18 @@ void DrawableImageParser::registerBaseParsers() { for ( size_t i = 0; i < params.size(); i++ ) { std::string param( String::toLower( params[i] ) ); - if ( Color::isColorString( param ) ) { + if ( param == "solid" || param == "fill" ) { + drawable->setFillMode( DRAW_FILL ); + } else if ( String::startsWith( param, "line" ) ) { + drawable->setFillMode( DRAW_LINE ); + } else if ( Color::isColorString( param ) ) { colors.push_back( Color::fromString( param ) ); } else { std::vector vertex( String::split( param, ',' ) ); if ( vertex.size() == 3 ) { for ( size_t v = 0; v < vertex.size(); v++ ) { - vertex[v] = String::trim( vertex[v] ); + String::trimInPlace( vertex[v] ); std::vector coords( String::split( vertex[v], ' ' ) ); if ( coords.size() == 2 ) { @@ -302,7 +306,7 @@ void DrawableImageParser::registerBaseParsers() { }; mFuncs["url"] = []( const FunctionString& functionType, const Sizef& /*size*/, bool& /*ownIt*/, - UINode * + UINode* /*node*/ ) -> Drawable* { if ( functionType.getParameters().size() < 1 ) { return NULL; diff --git a/src/eepp/ui/css/stylesheetpropertiesparser.cpp b/src/eepp/ui/css/stylesheetpropertiesparser.cpp index 57abed3d3..cf29e2851 100644 --- a/src/eepp/ui/css/stylesheetpropertiesparser.cpp +++ b/src/eepp/ui/css/stylesheetpropertiesparser.cpp @@ -87,13 +87,16 @@ int StyleSheetPropertiesParser::readPropertyValue( StyleSheetPropertiesParser::R mPrevRs = rs; + bool inString = false; + int prevChar = -1; + while ( pos < str.size() ) { if ( str[pos] == '/' && str.size() > pos + 1 && str[pos + 1] == '*' ) { rs = ReadingComment; return pos; } - if ( str[pos] == ';' ) { + if ( str[pos] == ';' && !inString ) { rs = ReadingPropertyName; addProperty( propName, buffer ); @@ -101,6 +104,9 @@ int StyleSheetPropertiesParser::readPropertyValue( StyleSheetPropertiesParser::R return pos + 1; } + if ( str[pos] == '"' && prevChar != '\\' ) + inString = !inString; + if ( str[pos] != '\n' && str[pos] != '\r' && str[pos] != '\t' ) buffer += str[pos]; @@ -113,6 +119,8 @@ int StyleSheetPropertiesParser::readPropertyValue( StyleSheetPropertiesParser::R return pos + 1; } + + prevChar = str[pos - 1]; } return pos; diff --git a/src/tools/ecode/projectdirectorytree.cpp b/src/tools/ecode/projectdirectorytree.cpp index 47971ad64..8c2cd1cee 100644 --- a/src/tools/ecode/projectdirectorytree.cpp +++ b/src/tools/ecode/projectdirectorytree.cpp @@ -167,6 +167,9 @@ void ProjectDirectoryTree::getDirectoryFiles( std::vector& files, FileSystem::dirAddSlashAtEnd( fullpath ); if ( currentDirs.find( fullpath ) == currentDirs.end() ) continue; + if ( std::find( mDirectories.begin(), mDirectories.end(), fullpath ) != + mDirectories.end() ) + continue; mDirectories.push_back( fullpath ); } else { mDirectories.push_back( fullpath );