Simple StyleSheet model WIP.

--HG--
branch : dev-stateful-drawable
This commit is contained in:
Martín Lucas Golini
2018-12-27 02:33:08 -03:00
parent 2468dbe72b
commit d37091f643
8 changed files with 610 additions and 4 deletions

View File

@@ -53,6 +53,8 @@ class NodeAttribute {
std::vector<std::string> names;
};
NodeAttribute();
NodeAttribute( std::string name, std::string value );
std::string getName() const;

View File

@@ -0,0 +1,302 @@
#ifndef EE_UI_CSS_STYLESHEETPARSER
#define EE_UI_CSS_STYLESHEETPARSER
#include <string>
#include <map>
#include <eepp/core/string.hpp>
#include <eepp/system/iostream.hpp>
#include <iostream>
#include <algorithm>
using namespace EE;
using namespace EE::System;
namespace EE { namespace UI { namespace CSS {
// This is not how it should be.
class StyleSheetSelector {
public:
enum SelectorType {
TagName = 1 << 0,
Id = 1 << 1,
Class = 1 << 2,
PseudoClass = 1 << 3
};
enum SpecificityVal {
SpecificityId = 1000000,
SpecificityClass = 100000,
SpecificityTag = 10000,
SpecificityPseudoClass = 0
};
explicit StyleSheetSelector( const std::string& selectorName ) :
name( selectorName ),
specificity(0)
{
auto parts = String::split( name, ' ' );
for ( auto it = parts.begin(); it != parts.end(); ++it ) {
parseSelector( *it );
}
}
Uint32 getRequiredFlags() {
Uint32 flags = 0;
if ( hasTagName() )
flags |= TagName;
if ( hasId() )
flags |= Id;
if ( hasClasses() )
flags |= Class;
if ( hasPseudoClass() )
flags |= PseudoClass;
return flags;
}
const std::string& getName() const { return name; };
const std::string& getTagName() const { return tagName; }
const std::string getId() const { return id; }
const std::vector<std::string> getClasses() const { return classes; }
const std::string& getPseudoClass() const;
bool hasTagName() { return !tagName.empty(); }
bool hasId() { return !id.empty(); }
bool hasClasses() { return !classes.empty(); }
bool hasClass( std::string cls ) { return std::find(classes.begin(), classes.end(), cls) != classes.end(); }
bool hasPseudoClass() { return !pseudoClass.empty(); }
Uint32 getSpecificity() { return specificity; }
protected:
std::string name;
std::string tagName;
std::string id;
std::vector<std::string> classes;
std::string pseudoClass;
Uint32 specificity;
void parseSelector( const std::string& selector ) {
auto selPseudo = String::split( selector, ':' );
if ( !selPseudo.empty() ) {
std::string rselector( selPseudo[0] );
if ( !rselector.empty() ) {
if ( rselector[0] == '.' ) {
classes.push_back( rselector.substr(1) );
specificity += SpecificityClass;
} else if ( selector[0] == '#' ) {
id = rselector.substr(1);
specificity += SpecificityId;
} else {
tagName = rselector;
specificity += SpecificityTag;
}
}
if ( selPseudo.size() > 1 ) {
pseudoClass = selPseudo[1];
specificity += SpecificityPseudoClass;
}
}
}
};
class StyleSheetProperty {
public:
StyleSheetProperty()
{}
explicit StyleSheetProperty( const std::string& name, const std::string& value ) :
name( String::trim( name ) ),
value( String::trim( value ) )
{}
std::string name;
std::string value;
};
typedef std::map<std::string, StyleSheetProperty> StyleSheetProperties;
class StyleSheetSelectorParser {
public:
StyleSheetSelectorParser(){}
explicit StyleSheetSelectorParser( std::string name )
{
std::vector<std::string> sels = String::split( name, ',' );
for ( std::vector<std::string>::iterator it = sels.begin(); it != sels.end(); ++it )
{
std::string cur = String::trim( *it );
selectors.push_back( StyleSheetSelector( cur ) );
}
}
std::vector<StyleSheetSelector> selectors;
};
class StyleSheetPropertiesParser {
public:
typedef std::map<std::string, StyleSheetProperty> PropertiesDictionary;
StyleSheetPropertiesParser(){}
explicit StyleSheetPropertiesParser( const std::string& propsstr ) {
std::vector<std::string> props = String::split( propsstr, ';' );
if ( !props.empty() ) {
parse( propsstr );
}
};
void print();
PropertiesDictionary properties;
protected:
enum ReadState {
ReadingPropertyName,
ReadingPropertyValue,
ReadingValueUrl,
ReadingComment
};
ReadState prevRs;
void parse( std::string propsstr );
int readPropertyName( ReadState& rs, std::size_t pos, std::string& buffer, const std::string& str );
int readPropertyValue( ReadState& rs, std::size_t pos, std::string& buffer, const std::string& str );
int readComment( ReadState& rs, std::size_t pos, std::string& buffer, const std::string& str );
int readValueUrl( ReadState& rs, std::size_t pos, std::string& buffer, const std::string& str );
};
class StyleSheetNode {
public:
explicit StyleSheetNode( const std::string& selector, const StyleSheetProperties& properties ) :
selector( selector ),
properties( properties )
{}
void print() {
std::cout << selector.getName() << " {" << std::endl;
for ( StyleSheetProperties::iterator it = properties.begin(); it != properties.end(); ++it ) {
StyleSheetProperty& prop = it->second;
std::cout << "\t" << prop.name << ": " << prop.value << ";" << std::endl;
}
std::cout << "}" << std::endl;
}
StyleSheetSelector selector;
StyleSheetProperties properties;
};
class StyleSheet {
public:
StyleSheet() {}
void addNode( StyleSheetNode node ) {
nodes.push_back( node );
}
StyleSheetProperties find( const std::string& tagName = "", const std::string& id = "", const std::vector<std::string>& classes = {}, const std::string& pseudoClass = "" ) {
StyleSheetProperties propertiesSelected;
Uint32 lastSpecificity = -1;
for ( auto it = nodes.begin(); it != nodes.end(); ++it ) {
StyleSheetNode& node = *it;
StyleSheetSelector& selector = node.selector;
Uint32 flags = 0;
if ( selector.hasTagName() && !tagName.empty() && selector.getTagName() == tagName ) {
flags |= StyleSheetSelector::TagName;
}
if ( selector.hasId() && !id.empty() && selector.getId() == id ) {
flags |= StyleSheetSelector::Id;
}
if ( selector.hasClasses() && !classes.empty() ) {
bool hasClasses = true;
for ( auto cit = classes.begin(); cit != classes.end(); ++cit ) {
if ( !selector.hasClass( *cit ) ) {
hasClasses = false;
break;
}
}
if ( hasClasses ) {
flags |= StyleSheetSelector::Class;
}
}
if ( selector.hasPseudoClass() && !pseudoClass.empty() && selector.getPseudoClass() == pseudoClass ) {
flags |= StyleSheetSelector::PseudoClass;
}
if ( flags == selector.getRequiredFlags() && selector.getSpecificity() > lastSpecificity ) {
propertiesSelected = node.properties;
lastSpecificity = selector.getSpecificity();
}
}
return propertiesSelected;
}
std::vector<StyleSheetNode> nodes;
};
class StyleSheetParser {
public:
StyleSheetParser();
bool loadFromStream( IOStream& stream );
bool loadFromFile( const std::string& file );
void print();
protected:
enum ReadState {
ReadingStyle,
ReadingProperty,
ReadingComment
};
std::string mCSS;
StyleSheet mStyleSheet;
std::vector<std::string> mComments;
bool parse();
int readStyle( ReadState& rs, std::size_t pos, std::string& buffer );
int readComment( ReadState& rs, std::size_t pos, std::string& buffer );
int readProperty( ReadState& rs, std::size_t pos, std::string& buffer );
};
}}}
#endif

View File

@@ -16,12 +16,15 @@ class EE_API UIStyle : public UIState {
virtual ~UIStyle();
bool stateExists( const Uint32& State );
bool stateExists( const Uint32& state );
void addAttribute( int state, NodeAttribute attribute );
protected:
std::map<int, std::map<std::string, NodeAttribute> > states;;
typedef std::map<std::string, NodeAttribute> AttributesMap;
std::map<int, AttributesMap> mStates;
};
}}
#endif

View File

@@ -729,6 +729,7 @@ function build_eepp( build_name )
"src/eepp/scene/actions/*.cpp",
"src/eepp/ui/*.cpp",
"src/eepp/ui/actions/*.cpp",
"src/eepp/ui/css/*.cpp",
"src/eepp/ui/tools/*.cpp",
"src/eepp/physics/*.cpp",
"src/eepp/physics/constraints/*.cpp",

View File

@@ -293,6 +293,7 @@
../../include/eepp/thirdparty/PlusCallback/callback.hpp
../../include/eepp/ui/base.hpp
../../include/eepp/ui.hpp
../../include/eepp/ui/css/stylesheetparser.hpp
../../include/eepp/ui/marginmove/scale.hpp
../../include/eepp/ui/tools/textureatlaseditor.hpp
../../include/eepp/ui/uicheckbox.hpp
@@ -692,6 +693,7 @@
../../src/eepp/system/translator.cpp
../../src/eepp/system/virtualfilesystem.cpp
../../src/eepp/system/zip.cpp
../../src/eepp/ui/css/stylesheetparser.cpp
../../src/eepp/ui/tools/textureatlaseditor.cpp
../../src/eepp/ui/tools/textureatlasnew.cpp
../../src/eepp/ui/tools/textureatlasnew.hpp

View File

@@ -28,6 +28,9 @@ const std::vector<std::string>& NodeAttribute::Info::getNames() const {
return names;
}
NodeAttribute::NodeAttribute()
{}
NodeAttribute::NodeAttribute( std::string name, std::string value ) :
mName( String::toLower( name ) ),
mValue( value )

View File

@@ -0,0 +1,289 @@
#include <eepp/ui/css/stylesheetparser.hpp>
#include <eepp/system/iostreamfile.hpp>
namespace EE { namespace UI { namespace CSS {
void StyleSheetPropertiesParser::parse( std::string propsstr ) {
ReadState rs = ReadingPropertyName;
prevRs = rs;
std::size_t pos = 0;
std::string buffer;
while ( pos < propsstr.size() ) {
switch (rs) {
case ReadingPropertyName:
{
pos = readPropertyName(rs, pos, buffer, propsstr);
break;
}
case ReadingPropertyValue:
{
pos = readPropertyValue(rs, pos, buffer, propsstr);
break;
}
case ReadingComment:
{
pos = readComment(rs, pos, buffer, propsstr);
}
default:
break;
}
}
}
int StyleSheetPropertiesParser::readPropertyName(StyleSheetPropertiesParser::ReadState & rs, std::size_t pos, std::string & buffer, const std::string& str) {
prevRs = rs;
buffer.clear();
while ( pos < str.size() ) {
if ( str[pos] == '/' && str.size() > pos + 1 && str[pos+1] == '*' ) {
rs = ReadingComment;
return pos;
}
if ( str[pos] == ':' ) {
rs = ReadingPropertyValue;
return pos + 1;
}
if ( str[pos] != '\n' && str[pos] != '\t' )
buffer += str[pos];
pos++;
}
return pos;
}
int StyleSheetPropertiesParser::readPropertyValue(StyleSheetPropertiesParser::ReadState & rs, std::size_t pos, std::string & buffer, const std::string& str) {
std::string propName( buffer );
buffer.clear();
prevRs = rs;
while ( pos < str.size() ) {
if ( str[pos] == '/' && str.size() > pos + 1 && str[pos+1] == '*' ) {
rs = ReadingComment;
return pos;
}
if ( buffer.size() == 4 && buffer.substr(0,4) == "url(" ) {
rs = ReadingValueUrl;
pos = readValueUrl( rs, pos, buffer, str );
}
if ( str[pos] == ';' ) {
rs = ReadingPropertyName;
properties[ propName ] = StyleSheetProperty( propName, buffer );
return pos + 1;
}
if ( str[pos] != '\n' && str[pos] != '\t' )
buffer += str[pos];
pos++;
if ( pos == str.size() ) {
rs = ReadingPropertyName;
properties[ propName ] = StyleSheetProperty( propName, buffer );
return pos + 1;
}
}
return pos;
}
int StyleSheetPropertiesParser::readComment(StyleSheetPropertiesParser::ReadState & rs, std::size_t pos, std::string & buffer, const std::string & str) {
buffer.clear();
while ( pos < str.size() ) {
if ( str[pos] == '*' && str.size() > pos + 1 && str[pos+1] == '/' ) {
rs = prevRs;
return pos + 2;
}
buffer += str[pos];
pos++;
}
return pos;
}
int StyleSheetPropertiesParser::readValueUrl(StyleSheetPropertiesParser::ReadState & rs, std::size_t pos, std::string & buffer, const std::string& str) {
bool quoted = false;
while ( pos < str.size() ) {
if ( !quoted && str[pos] == '"' ) {
buffer += str[pos];
quoted = true;
} else if ( !quoted ) {
if ( str[pos] != '\n' && str[pos] != '\t' )
buffer += str[pos];
if ( str[pos] == ')' ) {
rs = ReadingPropertyValue;
return pos + 1;
}
}
else {
buffer += str[pos];
if ( quoted && str[pos] == '"' ) {
quoted = false;
}
}
pos++;
}
return pos;
}
StyleSheetParser::StyleSheetParser() {
}
bool StyleSheetParser::loadFromFile( const std::string& file ) {
IOStreamFile stream( file );
return loadFromStream( stream );
}
void StyleSheetParser::print() {
for ( auto it = mStyleSheet.nodes.begin(); it != mStyleSheet.nodes.end(); ++it ) {
StyleSheetNode& style = *it;
style.print();
}
std::cout << "Comments: " << std::endl;
for ( std::size_t i = 0; i < mComments.size(); i++ ) {
std::cout << mComments[i] << std::endl;
}
}
bool StyleSheetParser::parse() {
ReadState rs = ReadingStyle;
std::size_t pos = 0;
std::string buffer;
while ( pos < mCSS.size() ) {
switch (rs) {
case ReadingStyle:
{
pos = readStyle(rs, pos, buffer);
break;
}
case ReadingComment:
{
pos = readComment(rs, pos, buffer);
break;
}
case ReadingProperty:
{
pos = readProperty(rs, pos, buffer);
break;
}
default:
break;
}
}
return true;
}
int StyleSheetParser::readStyle(ReadState& rs, std::size_t pos, std::string& buffer) {
buffer.clear();
while ( pos < mCSS.size() ) {
if ( mCSS[pos] == '/' && mCSS.size() > pos + 1 && mCSS[pos+1] == '*' ) {
rs = ReadingComment;
return pos;
}
if ( mCSS[pos] == '{' ) {
rs = ReadingProperty;
return pos + 1;
}
if ( mCSS[pos] != '\n' && mCSS[pos] != '\t' )
buffer += mCSS[pos];
pos++;
}
return pos;
}
int StyleSheetParser::readComment(ReadState& rs, std::size_t pos, std::string& buffer) {
buffer.clear();
while ( pos < mCSS.size() ) {
if ( mCSS[pos] == '*' && mCSS.size() > pos + 1 && mCSS[pos+1] == '/' ) {
rs = ReadingStyle;
buffer += mCSS[pos];
buffer += mCSS[pos+1];
mComments.push_back( buffer );
return pos + 2;
}
buffer += mCSS[pos];
pos++;
}
return pos;
}
int StyleSheetParser::readProperty( ReadState& rs, std::size_t pos, std::string& buffer ) {
std::string selectorName( buffer );
buffer.clear();
while ( pos < mCSS.size() ) {
if ( mCSS[pos] == '{' ) {
pos++;
continue;
} else if ( mCSS[pos] == '}' ) {
selectorName = String::trim( selectorName );
StyleSheetSelectorParser selectorParse( selectorName );
StyleSheetPropertiesParser propertiesParse( buffer );
if ( !selectorParse.selectors.empty() ) {
for ( auto it = selectorParse.selectors.begin(); it != selectorParse.selectors.end(); ++it ) {
StyleSheetNode node( selectorName, propertiesParse.properties );
mStyleSheet.addNode( node );
}
}
rs = ReadingStyle;
return pos + 1;
}
if ( mCSS[pos] != '\n' && mCSS[pos] != '\t' )
buffer += mCSS[pos];
pos++;
}
return pos;
}
bool StyleSheetParser::loadFromStream( IOStream& stream ) {
mCSS.resize( stream.getSize(), '\0' );
stream.read( &mCSS[0], stream.getSize() );
return parse();
}
}}}

View File

@@ -14,8 +14,12 @@ UIStyle::~UIStyle()
{
}
bool UIStyle::stateExists( const EE::Uint32 & State ) {
bool UIStyle::stateExists( const EE::Uint32 & ) {
return false;
}
void UIStyle::addAttribute(int state, NodeAttribute attribute) {
mStates[ state ][ attribute.getName() ] = attribute;
}
}}