diff --git a/bin/unit_tests/assets/html/ss64_deferred_cascade.css b/bin/unit_tests/assets/html/ss64_deferred_cascade.css new file mode 100644 index 000000000..8fdbd76a7 --- /dev/null +++ b/bin/unit_tests/assets/html/ss64_deferred_cascade.css @@ -0,0 +1,11 @@ +h1 { + font-size: 1.5rem; +} + +li a[href="../bash/"] { + background-color: #F8F9FA; +} + +#external-css-loaded { + height: 37px; +} diff --git a/include/eepp/ui/css/stylesheet.hpp b/include/eepp/ui/css/stylesheet.hpp index 2f17015cc..6ddd978bb 100644 --- a/include/eepp/ui/css/stylesheet.hpp +++ b/include/eepp/ui/css/stylesheet.hpp @@ -11,6 +11,8 @@ namespace EE { namespace UI { namespace CSS { class EE_API StyleSheet { public: + using SourceOrder = Uint32; + StyleSheet(); void clear(); @@ -23,6 +25,10 @@ class EE_API StyleSheet { void combineStyleSheet( const StyleSheet& styleSheet ); + void combineStyleSheet( const StyleSheet& styleSheet, SourceOrder sourceOrder ); + + SourceOrder reserveSourceOrder(); + std::shared_ptr getElementStyles( UIWidget* element, const bool& applyPseudo = false ) const; @@ -78,6 +84,18 @@ class EE_API StyleSheet { StyleSheet& operator=( const StyleSheet& other ); protected: + struct SourcePosition { + SourceOrder styleSheet{ 0 }; + Uint32 rule{ 0 }; + + bool operator<( const SourcePosition& other ) const { + if ( styleSheet != other.styleSheet ) + return styleSheet < other.styleSheet; + return rule < other.rule; + } + }; + static_assert( sizeof( SourcePosition ) == sizeof( Uint64 ) ); + Uint64 mVersion{ 1 }; Uint32 mMarker{ 0 }; std::vector> mNodes; @@ -87,8 +105,8 @@ class EE_API StyleSheet { UnorderedMap mClassNodeIndex; // Candidate buckets are independent, so retain insertion order explicitly for equal // specificity. - UnorderedMap mStyleSourceOrder; - size_t mNextStyleSourceOrder{ 0 }; + UnorderedMap mStyleSourceOrder; + SourceOrder mNextStyleSourceOrder{ 0 }; MediaQueryList::vector mMediaQueryList; KeyframesDefinitionMap mKeyframesMap; using ElementDefinitionCache = UnorderedMap>; @@ -99,6 +117,9 @@ class EE_API StyleSheet { void addMediaQueryList( MediaQueryList::ptr list ); bool addStyleToNodeIndex( StyleSheetStyle* style ); + + void addStyle( std::shared_ptr node, SourceOrder sourceOrder, + Uint32 ruleOrder ); }; }}} // namespace EE::UI::CSS diff --git a/include/eepp/ui/uiscenenode.hpp b/include/eepp/ui/uiscenenode.hpp index 0d7454952..65229c3dd 100644 --- a/include/eepp/ui/uiscenenode.hpp +++ b/include/eepp/ui/uiscenenode.hpp @@ -407,9 +407,11 @@ class EE_API UISceneNode : public SceneNode { * @param forceReloadStyle If true, forces immediate style reload (default: true). * @param baseURI If the resource was loaded from an URI, pass the URI in order to solve * relative paths in CSS + * @param sourceOrder Reserved document order for a stylesheet loaded asynchronously. */ void combineStyleSheet( const CSS::StyleSheet& styleSheet, bool forceReloadStyle = true, - URI baseURI = {} ); + URI baseURI = {}, + std::optional sourceOrder = {} ); /** * @brief Combines an inline stylesheet with the existing one. @@ -421,9 +423,11 @@ class EE_API UISceneNode : public SceneNode { * @param marker Marker to associate with the new styles. * @param baseURI If the resource was loaded from an URI, pass the URI in order to solve * relative paths in CSS + * @param sourceOrder Reserved document order for a stylesheet loaded asynchronously. */ void combineStyleSheet( const std::string& inlineStyleSheet, bool forceReloadStyle = true, - const Uint32& marker = 0, URI baseURI = {} ); + const Uint32& marker = 0, URI baseURI = {}, + std::optional sourceOrder = {} ); /** * @brief Gets the reference to the current stylesheet. @@ -1161,8 +1165,9 @@ class EE_API UISceneNode : public SceneNode { * * @param uri URI to load * @param defer Defer some specific time the CSS load (0 to just load it asynchronously) + * @param sourceOrder Document order reserved when the stylesheet link was encountered. */ - void loadCSS( URI uri, std::optional