|
|
@@ -32,19 +32,13 @@
|
|
|
#include "../../Include/RmlUi/Core/Profiling.h"
|
|
|
#include "../../Include/RmlUi/Core/PropertyDefinition.h"
|
|
|
#include "../../Include/RmlUi/Core/StyleSheetSpecification.h"
|
|
|
-#include "../../Include/RmlUi/Core/Utilities.h"
|
|
|
#include "ElementDefinition.h"
|
|
|
+#include "ElementStyle.h"
|
|
|
#include "StyleSheetNode.h"
|
|
|
#include <algorithm>
|
|
|
|
|
|
namespace Rml {
|
|
|
|
|
|
-// Sorts style nodes based on specificity.
|
|
|
-inline static bool StyleSheetNodeSort(const StyleSheetNode* lhs, const StyleSheetNode* rhs)
|
|
|
-{
|
|
|
- return lhs->GetSpecificity() < rhs->GetSpecificity();
|
|
|
-}
|
|
|
-
|
|
|
StyleSheet::StyleSheet()
|
|
|
{
|
|
|
root = MakeUnique<StyleSheetNode>();
|
|
|
@@ -105,7 +99,7 @@ void StyleSheet::MergeStyleSheet(const StyleSheet& other_sheet)
|
|
|
void StyleSheet::BuildNodeIndex()
|
|
|
{
|
|
|
RMLUI_ZoneScoped;
|
|
|
- styled_node_index.clear();
|
|
|
+ styled_node_index = {};
|
|
|
root->BuildIndex(styled_node_index);
|
|
|
root->SetStructurallyVolatileRecursive(false);
|
|
|
}
|
|
|
@@ -170,89 +164,75 @@ const Sprite* StyleSheet::GetSprite(const String& name) const
|
|
|
return spritesheet_list.GetSprite(name);
|
|
|
}
|
|
|
|
|
|
-size_t StyleSheet::NodeHash(const String& tag, const String& id)
|
|
|
-{
|
|
|
- size_t seed = 0;
|
|
|
- if (!tag.empty())
|
|
|
- seed = Hash<String>()(tag);
|
|
|
- if(!id.empty())
|
|
|
- Utilities::HashCombine(seed, id);
|
|
|
- return seed;
|
|
|
-}
|
|
|
-
|
|
|
// Returns the compiled element definition for a given element hierarchy.
|
|
|
SharedPtr<const ElementDefinition> StyleSheet::GetElementDefinition(const Element* element) const
|
|
|
{
|
|
|
RMLUI_ASSERT_NONRECURSIVE;
|
|
|
|
|
|
- // See if there are any styles defined for this element.
|
|
|
// Using static to avoid allocations. Make sure we don't call this function recursively.
|
|
|
static Vector< const StyleSheetNode* > applicable_nodes;
|
|
|
applicable_nodes.clear();
|
|
|
|
|
|
- const String& tag = element->GetTagName();
|
|
|
- const String& id = element->GetId();
|
|
|
-
|
|
|
- // The styled_node_index is hashed with the tag and id of the RCSS rule. However, we must also check
|
|
|
- // the rules which don't have them defined, because they apply regardless of tag and id.
|
|
|
- Array<size_t, 4> node_hash;
|
|
|
- int num_hashes = 2;
|
|
|
-
|
|
|
- node_hash[0] = 0;
|
|
|
- node_hash[1] = NodeHash(tag, String());
|
|
|
-
|
|
|
- // If we don't have an id, we can safely skip nodes that define an id. Otherwise, we also check the id nodes.
|
|
|
- if (!id.empty())
|
|
|
- {
|
|
|
- num_hashes = 4;
|
|
|
- node_hash[2] = NodeHash(String(), id);
|
|
|
- node_hash[3] = NodeHash(tag, id);
|
|
|
- }
|
|
|
-
|
|
|
- // The hashes are keys into a set of applicable nodes (given tag and id).
|
|
|
- for (int i = 0; i < num_hashes; i++)
|
|
|
- {
|
|
|
- auto it_nodes = styled_node_index.find(node_hash[i]);
|
|
|
- if (it_nodes != styled_node_index.end())
|
|
|
+ auto AddApplicableNodes = [element](const StyleSheetIndex::NodeIndex& node_index, const String& key) {
|
|
|
+ auto it_nodes = node_index.find(Hash<String>()(key));
|
|
|
+ if (it_nodes != node_index.end())
|
|
|
{
|
|
|
- const NodeList& nodes = it_nodes->second;
|
|
|
+ const StyleSheetIndex::NodeList& nodes = it_nodes->second;
|
|
|
|
|
|
- // Now see if we satisfy all of the requirements not yet tested: classes, pseudo classes, structural selectors,
|
|
|
- // and the full requirements of parent nodes. What this involves is traversing the style nodes backwards,
|
|
|
- // trying to match nodes in the element's hierarchy to nodes in the style hierarchy.
|
|
|
for (const StyleSheetNode* node : nodes)
|
|
|
{
|
|
|
- if (node->IsApplicable(element, true))
|
|
|
- {
|
|
|
+ // We found a node that has at least one requirement matching the element. Now see if we satisfy the remaining requirements of the
|
|
|
+ // node, including all ancestor nodes. What this involves is traversing the style nodes backwards, trying to match nodes in the
|
|
|
+ // element's hierarchy to nodes in the style hierarchy.
|
|
|
+ if (node->IsApplicable(element))
|
|
|
applicable_nodes.push_back(node);
|
|
|
- }
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
+ };
|
|
|
+
|
|
|
+ // See if there are any styles defined for this element.
|
|
|
+ const String& tag = element->GetTagName();
|
|
|
+ const String& id = element->GetId();
|
|
|
+ const StringList& class_names = element->GetStyle()->GetClassNameList();
|
|
|
+
|
|
|
+ // First, look up the indexed requirements.
|
|
|
+ if (!id.empty())
|
|
|
+ AddApplicableNodes(styled_node_index.ids, id);
|
|
|
|
|
|
- std::sort(applicable_nodes.begin(), applicable_nodes.end(), StyleSheetNodeSort);
|
|
|
+ for (const String& name : class_names)
|
|
|
+ AddApplicableNodes(styled_node_index.classes, name);
|
|
|
+
|
|
|
+ AddApplicableNodes(styled_node_index.tags, tag);
|
|
|
+
|
|
|
+ // Also check all remaining nodes that don't contain any indexed requirements.
|
|
|
+ for (const StyleSheetNode* node : styled_node_index.other)
|
|
|
+ {
|
|
|
+ if (node->IsApplicable(element))
|
|
|
+ applicable_nodes.push_back(node);
|
|
|
+ }
|
|
|
|
|
|
// If this element definition won't actually store any information, don't bother with it.
|
|
|
if (applicable_nodes.empty())
|
|
|
return nullptr;
|
|
|
|
|
|
- // Check if this puppy has already been cached in the node index.
|
|
|
- size_t seed = 0;
|
|
|
- for (const StyleSheetNode* node : applicable_nodes)
|
|
|
- Utilities::HashCombine(seed, node);
|
|
|
+ // Sort the applicable nodes by specificity first, then by pointer value in case we have duplicate specificities.
|
|
|
+ std::sort(applicable_nodes.begin(), applicable_nodes.end(), [](const StyleSheetNode* a, const StyleSheetNode* b) {
|
|
|
+ const int a_specificity = a->GetSpecificity();
|
|
|
+ const int b_specificity = b->GetSpecificity();
|
|
|
+ if (a_specificity == b_specificity)
|
|
|
+ return a < b;
|
|
|
+ return a_specificity < b_specificity;
|
|
|
+ });
|
|
|
|
|
|
- auto cache_iterator = node_cache.find(seed);
|
|
|
- if (cache_iterator != node_cache.end())
|
|
|
+ // Check if this puppy has already been cached in the node index.
|
|
|
+ SharedPtr<const ElementDefinition>& definition = node_cache[applicable_nodes];
|
|
|
+ if (!definition)
|
|
|
{
|
|
|
- SharedPtr<const ElementDefinition>& definition = (*cache_iterator).second;
|
|
|
- return definition;
|
|
|
+ // Otherwise, create a new definition and add it to our cache.
|
|
|
+ definition = MakeShared<const ElementDefinition>(applicable_nodes);
|
|
|
}
|
|
|
|
|
|
- // Create the new definition and add it to our cache.
|
|
|
- auto new_definition = MakeShared<const ElementDefinition>(applicable_nodes);
|
|
|
- node_cache[seed] = new_definition;
|
|
|
-
|
|
|
- return new_definition;
|
|
|
+ return definition;
|
|
|
}
|
|
|
|
|
|
} // namespace Rml
|