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