diff --git a/bin/assets/layouts/test.css b/bin/assets/layouts/test.css
index 932ba7615..710e2f6a8 100644
--- a/bin/assets/layouts/test.css
+++ b/bin/assets/layouts/test.css
@@ -151,23 +151,65 @@ Tooltip {
skinColor: #66ff44;
}
-
-#rtest {
+#test_2 > #rtest {
padding: 8dp;
background: #221122;
}
-#rtest #rttv {
- background:#333;
+#test_2 > #rtest #rttv {
+ background: #333;
textSize: 24dp;
borderRadius: 8;
+ padding: 8dp;
+ transition: all 0.125s;
}
-#rtest #rttv:hover {
- background:#666;
+#test_2 > #rtest #rttv:hover {
+ background: #666;
+}
+
+#test_2 > #rtest:hover #rttv {
+ background: #005522;
+}
+
+#test_2 > #rtest:hover #rttv:hover {
+ background: #008822;
+}
+
+#test_2 > #rtest:hover #rttv:pressed {
+ background: lime;
textColor: #333;
}
-#rtest:hover #rttv {
- background:#005522;
+#test_3 {
+ background: #221122;
}
+
+#test_3 > .rooter {
+ background: red;
+}
+
+#test_3 > .rooter .outer {
+ background: blue;
+}
+
+#test_3 > .rooter:hover .outer {
+ background: green;
+}
+
+#test_3 > .rooter:hover .outer:hover {
+ background: yellow;
+}
+
+#test_3 > .rooter .inner {
+ background: #123456;
+}
+
+#test_3 > .rooter:hover .outer:hover .inner {
+ background: #DABA22;
+}
+
+#test_3 > .rooter:hover .outer:hover .inner:hover {
+ background: #CCC;
+}
+
diff --git a/bin/assets/layouts/test.xml b/bin/assets/layouts/test.xml
index de8f282ad..16425bff4 100644
--- a/bin/assets/layouts/test.xml
+++ b/bin/assets/layouts/test.xml
@@ -51,8 +51,16 @@
+
+
+
+
+
+
+
+
diff --git a/include/eepp/graphics/fontsprite.hpp b/include/eepp/graphics/fontsprite.hpp
index ad57bf509..7ae7f9d0e 100644
--- a/include/eepp/graphics/fontsprite.hpp
+++ b/include/eepp/graphics/fontsprite.hpp
@@ -21,13 +21,13 @@ class EE_API FontSprite : public Font {
~FontSprite();
- bool loadFromFile(const std::string& filename, Color key = Color::Magenta, Uint32 firstChar = 32, int spacing = 0);
+ bool loadFromFile(const std::string& filename, Color key = Color::Fuchsia, Uint32 firstChar = 32, int spacing = 0);
- bool loadFromMemory(const void* data, std::size_t sizeInBytes, Color key = Color::Magenta, Uint32 firstChar = 32, int spacing = 0);
+ bool loadFromMemory(const void* data, std::size_t sizeInBytes, Color key = Color::Fuchsia, Uint32 firstChar = 32, int spacing = 0);
- bool loadFromStream( IOStream& stream, Color key = Color::Magenta, Uint32 firstChar = 32, int spacing = 0 );
+ bool loadFromStream( IOStream& stream, Color key = Color::Fuchsia, Uint32 firstChar = 32, int spacing = 0 );
- bool loadFromPack( Pack * pack, std::string filePackPath, Color key = Color::Magenta, Uint32 firstChar = 32, int spacing = 0 );
+ bool loadFromPack( Pack * pack, std::string filePackPath, Color key = Color::Fuchsia, Uint32 firstChar = 32, int spacing = 0 );
const Font::Info& getInfo() const;
diff --git a/include/eepp/system/color.hpp b/include/eepp/system/color.hpp
index 98e38c7f5..4d7d36b25 100755
--- a/include/eepp/system/color.hpp
+++ b/include/eepp/system/color.hpp
@@ -241,22 +241,22 @@ class EE_API Color : public tColor
static bool isColorString( std::string str );
static const Color Transparent;
- static const Color White;
static const Color Black;
- static const Color Red;
- static const Color Green;
- static const Color Blue;
- static const Color Yellow;
- static const Color Cyan;
- static const Color Magenta;
static const Color Silver;
static const Color Gray;
+ static const Color White;
static const Color Maroon;
- static const Color Olive;
- static const Color OfficeGreen;
+ static const Color Red;
static const Color Purple;
- static const Color Teal;
+ static const Color Fuchsia;
+ static const Color Green;
+ static const Color Lime;
+ static const Color Olive;
+ static const Color Yellow;
static const Color Navy;
+ static const Color Blue;
+ static const Color Teal;
+ static const Color Aqua;
};
typedef Color ColorA;
diff --git a/include/eepp/ui/css/stylesheetselector.hpp b/include/eepp/ui/css/stylesheetselector.hpp
index 1ee04a27b..d2f519abe 100644
--- a/include/eepp/ui/css/stylesheetselector.hpp
+++ b/include/eepp/ui/css/stylesheetselector.hpp
@@ -27,6 +27,8 @@ class EE_API StyleSheetSelector {
bool hasPseudoClass(const std::string& cls) const;
bool hasPseudoClasses() const;
+
+ std::vector getRelatedElements( StyleSheetElement * element, const bool& applyPseudo = true ) const;
protected:
std::string mName;
std::string mPseudoClass;
diff --git a/include/eepp/ui/uistyle.hpp b/include/eepp/ui/uistyle.hpp
index 737fa18f4..8848ce7ba 100644
--- a/include/eepp/ui/uistyle.hpp
+++ b/include/eepp/ui/uistyle.hpp
@@ -7,6 +7,8 @@
#include
#include
#include
+#include
+#include
using namespace EE::Scene;
@@ -76,12 +78,26 @@ class EE_API UIStyle : public UIState {
CSS::StyleSheetProperties mProperties;
std::vector mTransitionAttributes;
TransitionsMap mTransitions;
+ std::set mRelatedWidgets;
+ std::set mSubscribedWidgets;
void tryApplyStyle( const CSS::StyleSheetStyle& style );
void updateState();
void parseTransitions();
+
+ void subscribeNonCacheableStyles();
+
+ void unsubscribeNonCacheableStyles();
+
+ void subscribeRelated( UIWidget * widget );
+
+ void unsubscribeRelated( UIWidget * widget );
+
+ void removeFromSubscribedWidgets( UIWidget * widget );
+
+ void removeRelatedWidgets();
};
}}
diff --git a/include/eepp/ui/uiwidget.hpp b/include/eepp/ui/uiwidget.hpp
index d99a9f64a..defd2de76 100644
--- a/include/eepp/ui/uiwidget.hpp
+++ b/include/eepp/ui/uiwidget.hpp
@@ -191,6 +191,8 @@ class EE_API UIWidget : public UINode, public CSS::StyleSheetElement {
virtual void onThemeLoaded();
+ virtual void onParentChange();
+
void updateAnchors( const Vector2f & SizeChange );
void alignAgainstLayout();
diff --git a/src/eepp/graphics/fontsprite.cpp b/src/eepp/graphics/fontsprite.cpp
index 022079d28..98e5b5077 100644
--- a/src/eepp/graphics/fontsprite.cpp
+++ b/src/eepp/graphics/fontsprite.cpp
@@ -130,7 +130,7 @@ bool FontSprite::loadFromStream( IOStream& stream, Color key, Uint32 firstChar,
xPosToRead = charSpacing;
}
- img.createMaskFromColor( Color::Magenta, 0 );
+ img.createMaskFromColor( Color::Fuchsia, 0 );
Uint32 texId = TextureFactory::instance()->loadFromPixels( img.getPixelsPtr(), img.getWidth(), img.getHeight(), img.getChannels() );
diff --git a/src/eepp/system/color.cpp b/src/eepp/system/color.cpp
index 95d3e1234..0741667af 100644
--- a/src/eepp/system/color.cpp
+++ b/src/eepp/system/color.cpp
@@ -7,23 +7,25 @@
namespace EE { namespace System {
-const Color Color::Transparent = Color(0,0,0,0);
-const Color Color::White = Color(255,255,255);
-const Color Color::Black = Color(0,0,0);
-const Color Color::Red = Color(255,0,0);
-const Color Color::Green = Color(0,255,0);
-const Color Color::Blue = Color(0,0,255);
-const Color Color::Yellow = Color(255,255,0);
-const Color Color::Cyan = Color(0,255,255);
-const Color Color::Magenta = Color(255,0,255);
-const Color Color::Silver = Color(192,0,192);
-const Color Color::Gray = Color(128,128,128);
-const Color Color::Maroon = Color(128,0,0);
-const Color Color::Olive = Color(128,128,0);
-const Color Color::OfficeGreen = Color(0,128,0);
-const Color Color::Purple = Color(128,0,128);
-const Color Color::Teal = Color(0,128,128);
-const Color Color::Navy = Color(0,0,128);
+// @TODO: Support all CSS3 color keywords.
+// Reference: https://www.w3.org/TR/2018/REC-css-color-3-20180619/
+const Color Color::Transparent = Color(0x00000000);
+const Color Color::Black = Color(0x000000FF);
+const Color Color::Silver = Color(0xC0C0C0FF);
+const Color Color::Gray = Color(0x808080FF);
+const Color Color::White = Color(0xFFFFFFFF);
+const Color Color::Maroon = Color(0x800000FF);
+const Color Color::Red = Color(0xFF0000FF);
+const Color Color::Purple = Color(0x800080FF);
+const Color Color::Fuchsia = Color(0xFF00FFFF);
+const Color Color::Green = Color(0x008000FF);
+const Color Color::Lime = Color(0x00FF00FF);
+const Color Color::Olive = Color(0x808000FF);
+const Color Color::Yellow = Color(0xFFFF00FF);
+const Color Color::Navy = Color(0x000080FF);
+const Color Color::Blue = Color(0x0000FFFF);
+const Color Color::Teal = Color(0x008080FF);
+const Color Color::Aqua = Color(0x00FFFFFF);
RGB::RGB() : tRGB()
{
@@ -303,22 +305,22 @@ Color Color::fromString( std::string str ) {
} else if ( size >= 3 && isalpha( str[0] ) && isalpha( str[1] ) && isalpha( str[2] ) ) {
String::toLowerInPlace( str );
if ( "transparent" == str ) return Color::Transparent;
- else if ( "white" == str ) return Color::White;
else if ( "black" == str ) return Color::Black;
- else if ( "red" == str ) return Color::Red;
- else if ( "green" == str ) return Color::Green;
- else if ( "blue" == str ) return Color::Blue;
- else if ( "yellow" == str ) return Color::Yellow;
- else if ( "cyan" == str ) return Color::Cyan;
- else if ( "magenta" == str ) return Color::Magenta;
else if ( "silver" == str ) return Color::Silver;
else if ( "gray" == str ) return Color::Gray;
+ else if ( "white" == str ) return Color::White;
else if ( "maroon" == str ) return Color::Maroon;
- else if ( "olive" == str ) return Color::Olive;
- else if ( "officegreen" == str ) return Color::OfficeGreen;
+ else if ( "red" == str ) return Color::Red;
else if ( "purple" == str ) return Color::Purple;
- else if ( "teal" == str ) return Color::Teal;
+ else if ( "fuchsia" == str ) return Color::Fuchsia;
+ else if ( "green" == str ) return Color::Green;
+ else if ( "lime" == str ) return Color::Lime;
+ else if ( "olive" == str ) return Color::Olive;
+ else if ( "yellow" == str ) return Color::Yellow;
else if ( "navy" == str ) return Color::Navy;
+ else if ( "blue" == str ) return Color::Blue;
+ else if ( "teal" == str ) return Color::Teal;
+ else if ( "aqua" == str ) return Color::Aqua;
}
if ( size < 6 ) {
@@ -343,22 +345,22 @@ bool Color::isColorString( std::string str ) {
String::toLowerInPlace( str );
if ( "transparent" == str ) return true;
- else if ( "white" == str ) return true;
else if ( "black" == str ) return true;
- else if ( "red" == str ) return true;
- else if ( "green" == str ) return true;
- else if ( "blue" == str ) return true;
- else if ( "yellow" == str ) return true;
- else if ( "cyan" == str ) return true;
- else if ( "magenta" == str ) return true;
else if ( "silver" == str ) return true;
else if ( "gray" == str ) return true;
+ else if ( "white" == str ) return true;
else if ( "maroon" == str ) return true;
- else if ( "olive" == str ) return true;
- else if ( "officegreen" == str ) return true;
+ else if ( "red" == str ) return true;
else if ( "purple" == str ) return true;
- else if ( "teal" == str ) return true;
+ else if ( "fuchsia" == str ) return true;
+ else if ( "green" == str ) return true;
+ else if ( "lime" == str ) return true;
+ else if ( "olive" == str ) return true;
+ else if ( "yellow" == str ) return true;
else if ( "navy" == str ) return true;
+ else if ( "blue" == str ) return true;
+ else if ( "teal" == str ) return true;
+ else if ( "aqua" == str ) return true;
return false;
}
diff --git a/src/eepp/ui/css/stylesheetselector.cpp b/src/eepp/ui/css/stylesheetselector.cpp
index 8bfbedbf2..95585b6a8 100644
--- a/src/eepp/ui/css/stylesheetselector.cpp
+++ b/src/eepp/ui/css/stylesheetselector.cpp
@@ -206,4 +206,113 @@ bool StyleSheetSelector::select( StyleSheetElement * element, const bool& applyP
return true;
}
+std::vector StyleSheetSelector::getRelatedElements( StyleSheetElement * element, const bool& applyPseudo ) const {
+ static std::vector EMPTY_ELEMENTS;
+ std::vector elements;
+ if ( mSelectorRules.empty() )
+ return elements;
+
+ StyleSheetElement * curElement = element;
+
+ for ( size_t i = 0; i < mSelectorRules.size(); i++ ) {
+ const StyleSheetSelectorRule& selectorRule = mSelectorRules[i];
+
+ switch ( selectorRule.getPatternMatch() ) {
+ case StyleSheetSelectorRule::ANY:
+ {
+ if ( !selectorRule.matches( curElement, applyPseudo ) )
+ return EMPTY_ELEMENTS;
+
+ break; // continue evaluating
+ }
+ case StyleSheetSelectorRule::DESCENDANT:
+ {
+ bool foundDescendant = false;
+
+ curElement = curElement->getStyleSheetParentElement();
+
+ while ( NULL != curElement && !foundDescendant ) {
+ if ( selectorRule.matches( curElement, applyPseudo ) ) {
+ foundDescendant = true;
+ } else {
+ curElement = curElement->getStyleSheetParentElement();
+ }
+ }
+
+ if ( !foundDescendant )
+ return EMPTY_ELEMENTS;
+
+ if ( 0 != i && ( selectorRule.hasPseudoClasses() || selectorRule.hasStructuralPseudoClasses() ) ) {
+ elements.push_back( curElement );
+ }
+
+ break; // continue evaluating
+ }
+ case StyleSheetSelectorRule::CHILD:
+ {
+ curElement = curElement->getStyleSheetParentElement();
+
+ if ( NULL == curElement || !selectorRule.matches( curElement, applyPseudo ) )
+ return EMPTY_ELEMENTS;
+
+ if ( 0 != i && ( selectorRule.hasPseudoClasses() || selectorRule.hasStructuralPseudoClasses() ) ) {
+ elements.push_back( curElement );
+ }
+
+ break; // continue evaluating
+ }
+ case StyleSheetSelectorRule::DIRECT_SIBLING:
+ {
+ curElement = curElement->getStyleSheetPreviousSiblingElement();
+
+ if ( NULL == curElement || !selectorRule.matches( curElement, applyPseudo ) )
+ return EMPTY_ELEMENTS;
+
+ if ( 0 != i && ( selectorRule.hasPseudoClasses() || selectorRule.hasStructuralPseudoClasses() ) ) {
+ elements.push_back( curElement );
+ }
+
+ break; // continue evaluating
+ }
+ case StyleSheetSelectorRule::SIBLING:
+ {
+ bool foundSibling = false;
+ StyleSheetElement * prevSibling = curElement->getStyleSheetPreviousSiblingElement();
+ StyleSheetElement * nextSibling = curElement->getStyleSheetNextSiblingElement();
+
+ while ( NULL != prevSibling && !foundSibling ) {
+ if ( selectorRule.matches( prevSibling, applyPseudo ) ) {
+ foundSibling = true;
+ curElement = prevSibling;
+ } else {
+ prevSibling = prevSibling->getStyleSheetPreviousSiblingElement();
+ }
+ }
+
+ if ( !foundSibling ) {
+ while ( NULL != nextSibling && !foundSibling ) {
+ if ( selectorRule.matches( nextSibling, applyPseudo ) ) {
+ foundSibling = true;
+ curElement = nextSibling;
+ } else {
+ nextSibling = nextSibling->getStyleSheetNextSiblingElement();
+ }
+ }
+ }
+
+ if ( !foundSibling )
+ return EMPTY_ELEMENTS;
+
+ if ( 0 != i && ( selectorRule.hasPseudoClasses() || selectorRule.hasStructuralPseudoClasses() ) ) {
+ elements.push_back( curElement );
+ }
+
+ break; // continue evaluating
+ }
+ }
+ }
+
+ return elements;
+}
+
}}}
diff --git a/src/eepp/ui/css/stylesheetselectorrule.cpp b/src/eepp/ui/css/stylesheetselectorrule.cpp
index 584b3705e..0a63392ef 100644
--- a/src/eepp/ui/css/stylesheetselectorrule.cpp
+++ b/src/eepp/ui/css/stylesheetselectorrule.cpp
@@ -242,8 +242,8 @@ bool StyleSheetSelectorRule::matches( StyleSheetElement * element, const bool& a
if ( !mClasses.empty() && !element->getStyleSheetClasses().empty() ) {
bool hasClasses = true;
- for ( auto cit = element->getStyleSheetClasses().begin(); cit != element->getStyleSheetClasses().end(); ++cit ) {
- if ( !hasClass( *cit ) ) {
+ for ( const auto& cls : element->getStyleSheetClasses() ) {
+ if ( !hasClass( cls ) ) {
hasClasses = false;
break;
}
@@ -259,8 +259,8 @@ bool StyleSheetSelectorRule::matches( StyleSheetElement * element, const bool& a
bool hasPseudoClasses = false;
const std::vector& elPseudoClasses = element->getStyleSheetPseudoClasses();
- for ( auto cit = elPseudoClasses.begin(); cit != elPseudoClasses.end(); ++cit ) {
- if ( hasPseudoClass( *cit ) ) {
+ for ( const auto& cls : elPseudoClasses ) {
+ if ( hasPseudoClass( cls ) ) {
hasPseudoClasses = true;
break;
}
diff --git a/src/eepp/ui/uistyle.cpp b/src/eepp/ui/uistyle.cpp
index 4a3eebd61..4421bb901 100644
--- a/src/eepp/ui/uistyle.cpp
+++ b/src/eepp/ui/uistyle.cpp
@@ -18,8 +18,10 @@ UIStyle::UIStyle( UIWidget * widget ) :
load();
}
-UIStyle::~UIStyle()
-{}
+UIStyle::~UIStyle() {
+ removeRelatedWidgets();
+ unsubscribeNonCacheableStyles();
+}
bool UIStyle::stateExists( const EE::Uint32& ) const {
return true;
@@ -44,6 +46,8 @@ void UIStyle::addStyleSheetProperty( const StyleSheetProperty& attribute ) {
}
void UIStyle::load() {
+ unsubscribeNonCacheableStyles();
+
mCacheableStyles.clear();
mNoncacheableStyles.clear();
@@ -64,6 +68,8 @@ void UIStyle::load() {
mNoncacheableStyles.push_back( style );
}
}
+
+ subscribeNonCacheableStyles();
}
}
}
@@ -92,6 +98,14 @@ UIStyle::TransitionInfo UIStyle::getTransition( const std::string& propertyName
return TransitionInfo();
}
+void UIStyle::subscribeRelated( UIWidget * widget ) {
+ mRelatedWidgets.insert( widget );
+}
+
+void UIStyle::unsubscribeRelated( UIWidget * widget ) {
+ mRelatedWidgets.erase( widget );
+}
+
void UIStyle::tryApplyStyle( const StyleSheetStyle& style ) {
if ( style.getSelector().select( mWidget ) ) {
for ( const auto& prop : style.getProperties() ) {
@@ -134,6 +148,12 @@ void UIStyle::onStateChange() {
}
mWidget->endAttributesTransaction();
+
+ for ( auto& related : mRelatedWidgets ) {
+ if ( NULL != related->getUIStyle() ) {
+ related->getUIStyle()->onStateChange();
+ }
+ }
}
}
@@ -189,6 +209,48 @@ void UIStyle::updateState() {
onStateChange();
}
+void UIStyle::subscribeNonCacheableStyles() {
+ for ( auto& style : mNoncacheableStyles ) {
+ std::vector elements = style.getSelector().getRelatedElements( mWidget, false );
+
+ if ( !elements.empty() ) {
+ for ( auto& element : elements ) {
+ UIWidget * widget = dynamic_cast( element );
+
+ if ( NULL != widget && NULL != widget->getUIStyle() ) {
+ widget->getUIStyle()->subscribeRelated( mWidget );
+
+ mSubscribedWidgets.insert( widget );
+ }
+ }
+ }
+ }
+}
+
+void UIStyle::unsubscribeNonCacheableStyles() {
+ for ( auto& widget : mSubscribedWidgets ) {
+ if ( NULL != widget->getUIStyle() ) {
+ widget->getUIStyle()->unsubscribeRelated( mWidget );
+ }
+ }
+
+ mSubscribedWidgets.clear();
+}
+
+void UIStyle::removeFromSubscribedWidgets( UIWidget * widget ) {
+ mSubscribedWidgets.erase( widget );
+}
+
+void UIStyle::removeRelatedWidgets() {
+ for ( auto& widget : mRelatedWidgets ) {
+ if ( NULL != widget->getUIStyle() ) {
+ widget->getUIStyle()->removeFromSubscribedWidgets( mWidget );
+ }
+ }
+
+ mRelatedWidgets.clear();
+}
+
void UIStyle::parseTransitions() {
std::vector properties;
std::vector