Browse Source

Replace PropertyNameList with PropertyIdSet (previously DirtyPropertyList)

Michael Ragazzon 6 years ago
parent
commit
d1d72de3f8
35 changed files with 379 additions and 308 deletions
  1. 1 1
      Cmake/FileList.cmake
  2. 0 2
      Include/RmlUi/Controls/DataFormatter.h
  3. 1 1
      Include/RmlUi/Controls/ElementFormControlInput.h
  4. 1 1
      Include/RmlUi/Controls/ElementFormControlTextArea.h
  5. 1 1
      Include/RmlUi/Core/Containers/chobo/flat_set.hpp
  6. 1 0
      Include/RmlUi/Core/Core.h
  7. 1 2
      Include/RmlUi/Core/Element.h
  8. 1 1
      Include/RmlUi/Core/ElementDocument.h
  9. 220 0
      Include/RmlUi/Core/PropertyIdSet.h
  10. 6 5
      Include/RmlUi/Core/PropertySpecification.h
  11. 6 5
      Include/RmlUi/Core/StyleSheetSpecification.h
  12. 1 2
      Include/RmlUi/Core/Types.h
  13. 1 1
      Source/Controls/ElementFormControlInput.cpp
  14. 4 3
      Source/Controls/ElementFormControlTextArea.cpp
  15. 4 3
      Source/Controls/ElementTextSelection.cpp
  16. 1 1
      Source/Controls/ElementTextSelection.h
  17. 1 1
      Source/Controls/InputType.cpp
  18. 1 1
      Source/Controls/InputType.h
  19. 4 3
      Source/Controls/InputTypeText.cpp
  20. 1 1
      Source/Controls/InputTypeText.h
  21. 0 142
      Source/Core/DirtyPropertyList.h
  22. 48 56
      Source/Core/Element.cpp
  23. 2 2
      Source/Core/ElementDefinition.cpp
  24. 1 1
      Source/Core/ElementDefinition.h
  25. 6 6
      Source/Core/ElementDocument.cpp
  26. 3 3
      Source/Core/ElementImage.cpp
  27. 1 1
      Source/Core/ElementImage.h
  28. 23 21
      Source/Core/ElementStyle.cpp
  29. 6 6
      Source/Core/ElementStyle.h
  30. 9 9
      Source/Core/ElementTextDefault.cpp
  31. 1 1
      Source/Core/ElementTextDefault.h
  32. 2 2
      Source/Core/PropertiesIterator.h
  33. 6 6
      Source/Core/PropertyParserAnimation.cpp
  34. 4 5
      Source/Core/PropertySpecification.cpp
  35. 10 12
      Source/Core/StyleSheetSpecification.cpp

+ 1 - 1
Cmake/FileList.cmake

@@ -22,7 +22,6 @@ set(Core_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiledInstancer.h
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiledVertical.h
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiledVerticalInstancer.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/DirtyPropertyList.h
     ${PROJECT_SOURCE_DIR}/Source/Core/DocumentHeader.h
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementAnimation.h
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementBackground.h
@@ -165,6 +164,7 @@ set(Core_PUB_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Property.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/PropertyDefinition.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/PropertyDictionary.h
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/PropertyIdSet.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/PropertyParser.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/PropertySpecification.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/RenderInterface.h

+ 0 - 2
Include/RmlUi/Controls/DataFormatter.h

@@ -36,8 +36,6 @@
 namespace Rml {
 namespace Controls {
 
-class Element;
-
 /**
 	Abstract base class for a data formatter. A data formatter takes raw data
 	and processes it into a final string. They are usually used in conjunction

+ 1 - 1
Include/RmlUi/Controls/ElementFormControlInput.h

@@ -75,7 +75,7 @@ protected:
 	void OnAttributeChange(const Core::ElementAttributes& changed_attributes) override;
 	/// Called when properties on the control are changed.
 	/// @param[in] changed_properties The properties changed on the element.
-	void OnPropertyChange(const Core::PropertyNameList& changed_properties) override;
+	void OnPropertyChange(const Core::PropertyIdSet& changed_properties) override;
 
 	/// If we are the added element, this will pass the call onto our type handler.
 	/// @param[in] child The new member of the hierarchy.

+ 1 - 1
Include/RmlUi/Controls/ElementFormControlTextArea.h

@@ -106,7 +106,7 @@ protected:
 	void OnAttributeChange(const Core::ElementAttributes& changed_attributes) override;
 	/// Called when properties on the control are changed.
 	/// @param[in] changed_properties The properties changed on the element.
-	void OnPropertyChange(const Core::PropertyNameList& changed_properties) override;
+	void OnPropertyChange(const Core::PropertyIdSet& changed_properties) override;
 
 	/// Returns the text content of the element.
 	/// @param[out] content The content of the element.

+ 1 - 1
Include/RmlUi/Core/Containers/chobo/flat_set.hpp

@@ -243,7 +243,7 @@ public:
         return insert(std::move(val));
     }
 
-    iterator erase(iterator pos)
+    iterator erase(const_iterator pos)
     {
         return m_container.erase(pos);
     }

+ 1 - 0
Include/RmlUi/Core/Core.h

@@ -66,6 +66,7 @@
 #include "Property.h"
 #include "PropertyDefinition.h"
 #include "PropertyDictionary.h"
+#include "PropertyIdSet.h"
 #include "PropertyParser.h"
 #include "PropertySpecification.h"
 #include "RenderInterface.h"

+ 1 - 2
Include/RmlUi/Core/Element.h

@@ -62,7 +62,6 @@ class FontFaceHandle;
 class PropertyDictionary;
 class RenderInterface;
 class StyleSheet;
-class DirtyPropertyList;
 struct ElementMeta;
 
 /**
@@ -591,7 +590,7 @@ protected:
 	virtual void OnAttributeChange(const ElementAttributes& changed_attributes);
 	/// Called when properties on the element are changed.
 	/// @param[in] changed_properties The properties changed on the element.
-	virtual void OnPropertyChange(const PropertyNameList& changed_properties);
+	virtual void OnPropertyChange(const PropertyIdSet& changed_properties);
 
 	/// Called when a child node has been added up to two levels below us in the hierarchy.
 	/// @param[in] child The element that has been added. This may be this element.

+ 1 - 1
Include/RmlUi/Core/ElementDocument.h

@@ -125,7 +125,7 @@ public:
 	
 protected:
 	/// Repositions the document if necessary.
-	void OnPropertyChange(const PropertyNameList& changed_properties) override;
+	void OnPropertyChange(const PropertyIdSet& changed_properties) override;
 
 	/// Processes the 'onpropertychange' event, checking for a change in position or size.
 	void ProcessDefaultAction(Event& event) override;

+ 220 - 0
Include/RmlUi/Core/PropertyIdSet.h

@@ -0,0 +1,220 @@
+/*
+ * This source file is part of RmlUi, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://github.com/mikke89/RmlUi
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ * Copyright (c) 2019 The RmlUi Team, and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+#ifndef RMLUICOREPROPERTYIDSET_H
+#define RMLUICOREPROPERTYIDSET_H
+
+#include "Types.h"
+#include "ID.h"
+#include <bitset>
+
+namespace Rml {
+namespace Core {
+
+
+class PropertyIdSetIterator;
+
+
+class RMLUICORE_API PropertyIdSet {
+private:
+	static constexpr size_t N = (size_t)PropertyId::NumDefinedIds;
+	std::bitset<N> defined_ids;
+	SmallOrderedSet<PropertyId> custom_ids;
+
+public:
+	PropertyIdSet() {
+		static_assert((size_t)PropertyId::Invalid == 0, "PropertyIdSet makes an assumption that PropertyId::Invalid is zero.");
+	}
+
+	void Insert(PropertyId id) {
+		if ((size_t)id < N)
+			defined_ids.set((size_t)id);
+		else
+			custom_ids.insert(id);
+	}
+
+	void SetAll() {
+		// We are using PropertyId::Invalid (0) as a flag for all properties set.
+		// However, we set the whole set so that we don't have to do the extra check
+		// for the first bit in Contains().
+		defined_ids.set();
+	}
+	void Clear() {
+		defined_ids.reset();
+		custom_ids.clear();
+	}
+	void Erase(PropertyId id) {
+		if ((size_t)id < N)
+			defined_ids.reset((size_t)id);
+		else
+			custom_ids.erase(id);
+	}
+
+	bool Empty() const {
+		return defined_ids.none() && custom_ids.empty();
+	}
+	bool Contains(PropertyId id) const {
+		if ((size_t)id < N)
+			return defined_ids.test((size_t)id);
+		else if (defined_ids.test(0))
+			return true;
+		else
+			return custom_ids.count(id) == 1;
+	}
+	bool IsAllSet() const {
+		return defined_ids.test(0);
+	}
+
+	size_t Size() const {
+		if(defined_ids.test(0))
+			return N - 1 + custom_ids.size(); // (don't count PropertyId::Invalid)
+		else
+			return defined_ids.count() + custom_ids.size();
+	}
+
+	// Union with another set
+	PropertyIdSet& operator|=(const PropertyIdSet& other)
+	{
+		defined_ids |= other.defined_ids;
+		custom_ids.insert(other.custom_ids.begin(), other.custom_ids.end());
+		return *this;
+	}
+
+	// Intersection with another set
+	PropertyIdSet& operator&=(const PropertyIdSet& other)
+	{
+		defined_ids &= other.defined_ids;
+		if (custom_ids.size() > 0 && other.custom_ids.size() > 0 && !defined_ids.test(0))
+		{
+			for (auto it = custom_ids.begin(); it != custom_ids.end();)
+				if (other.custom_ids.count(*it) == 0)
+					it = custom_ids.erase(it);
+				else
+					++it;
+		}
+		else
+		{
+			custom_ids.clear();
+		}
+		return *this;
+	}
+
+	// Iterator support
+	// @note: Modifying the container invalidates the iterators. Only const_iterators are provided.
+	inline PropertyIdSetIterator begin() const;
+	inline PropertyIdSetIterator end() const;
+
+	// Erases the property id represented by a valid iterator. Invalidates any previous iterators.
+	// @return A new valid iterator pointing to the next element or end().
+	inline PropertyIdSetIterator Erase(const PropertyIdSetIterator& it);
+};
+
+
+
+class RMLUICORE_API PropertyIdSetIterator
+{
+public:
+	using CustomIdsIt = SmallOrderedSet<PropertyId>::const_iterator;
+
+	PropertyIdSetIterator() : container(nullptr), defined_ids_index(0), custom_ids_iterator() {}
+	PropertyIdSetIterator(const PropertyIdSet* container, size_t defined_ids_index, CustomIdsIt custom_ids_iterator)
+		: container(container), defined_ids_index(defined_ids_index), custom_ids_iterator(custom_ids_iterator) 
+	{
+		ProceedToNextValid();
+	}
+	
+	PropertyIdSetIterator& operator++() {
+		if (defined_ids_index < N)
+			++defined_ids_index;
+		else
+			++custom_ids_iterator;
+		ProceedToNextValid();
+		return *this;
+	}
+
+	bool operator==(const PropertyIdSetIterator& other) const {
+		return container == other.container && defined_ids_index == other.defined_ids_index && custom_ids_iterator == other.custom_ids_iterator;
+	}
+	bool operator!=(const PropertyIdSetIterator& other) const { 
+		return !(*this == other); 
+	}
+
+	PropertyId operator*() const { 
+		if (defined_ids_index < N)
+			return static_cast<PropertyId>(defined_ids_index);
+		else
+			return *custom_ids_iterator;
+	}
+
+private:
+
+	inline void ProceedToNextValid()
+	{
+		for (; defined_ids_index < N; ++defined_ids_index)
+		{
+			if (container->Contains( static_cast<PropertyId>(defined_ids_index) ))
+				return;
+		}
+	}
+
+	static constexpr size_t N = (size_t)PropertyId::NumDefinedIds;
+	const PropertyIdSet* container;
+	size_t defined_ids_index;
+	CustomIdsIt custom_ids_iterator;
+
+	friend PropertyIdSetIterator PropertyIdSet::Erase(const PropertyIdSetIterator&);
+};
+
+
+
+PropertyIdSetIterator PropertyIdSet::begin() const {
+	return PropertyIdSetIterator(this, 1, custom_ids.begin());
+}
+
+PropertyIdSetIterator PropertyIdSet::end() const {
+	return PropertyIdSetIterator(this, N, custom_ids.end());
+}
+
+PropertyIdSetIterator PropertyIdSet::Erase(const PropertyIdSetIterator& it_in) {
+	RMLUI_ASSERT(it_in.container == this);
+	PropertyIdSetIterator it = it_in;
+	if (it.defined_ids_index < N)
+	{
+		defined_ids.reset(it.defined_ids_index);
+		++it;
+	}
+	else
+	{
+		it.custom_ids_iterator = custom_ids.erase(it.custom_ids_iterator);
+	}
+	return it;
+}
+
+}
+}
+
+#endif

+ 6 - 5
Include/RmlUi/Core/PropertySpecification.h

@@ -32,6 +32,7 @@
 #include "Header.h"
 #include "Element.h"
 #include "PropertyDefinition.h"
+#include "PropertyIdSet.h"
 
 namespace Rml {
 namespace Core {
@@ -41,7 +42,7 @@ class PropertyDictionary;
 struct ShorthandDefinition;
 
 
-
+// todo: We probably don't need to expose this in a public header
 template <typename ID>
 class IdNameMap {
 	std::vector<String> name_map;  // IDs are indices into the name_map
@@ -175,11 +176,11 @@ public:
 
 	/// Returns the list of the names of all registered property definitions.
 	/// @return The list with stored property names.
-	const PropertyNameList& GetRegisteredProperties() const;
+	const PropertyIdSet& GetRegisteredProperties() const;
 
 	/// Returns the list of the names of all registered inherited property definitions.
 	/// @return The list with stored property names.
-	const PropertyNameList& GetRegisteredInheritedProperties() const;
+	const PropertyIdSet& GetRegisteredInheritedProperties() const;
 
 	/// Registers a shorthand property definition.
 	/// @param[in] shorthand_name The name to register the new shorthand property under.
@@ -223,8 +224,8 @@ private:
 	ShorthandIdNameMap shorthand_map;
 
 	// todo: Do we really need these?
-	PropertyNameList property_names;
-	PropertyNameList inherited_property_names;
+	PropertyIdSet property_names;
+	PropertyIdSet inherited_property_names;
 
 	bool ParsePropertyValues(StringList& values_list, const String& values, bool split_values) const;
 

+ 6 - 5
Include/RmlUi/Core/StyleSheetSpecification.h

@@ -37,7 +37,6 @@ namespace Rml {
 namespace Core {
 
 class PropertyParser;
-class DirtyPropertyList;
 
 /**
 	@author Peter Curry
@@ -77,11 +76,11 @@ public:
 
 	/// Returns the list of the names of all registered property definitions.
 	/// @return The list with stored property names.
-	static const PropertyNameList & GetRegisteredProperties();
+	static const PropertyIdSet & GetRegisteredProperties();
 
 	/// Returns the list of the names of all registered inherited property definitions.
 	/// @return The list with stored property names.
-	static const PropertyNameList & GetRegisteredInheritedProperties();
+	static const PropertyIdSet & GetRegisteredInheritedProperties();
 
 	/// Registers a custom shorthand property definition.
 	/// @param[in] shorthand_name The name to register the new shorthand property under.
@@ -108,9 +107,11 @@ public:
 	static ShorthandId GetShorthandId(const String& shorthand_name);
 	static const String& GetPropertyName(PropertyId id);
 	static const String& GetShorthandName(ShorthandId id);
-	static const DirtyPropertyList& GetRegisteredInheritedPropertyBitList();
+	// todo: Now, this should be equal to GetRegisteredInheritedProperties():
+	static const PropertyIdSet& GetRegisteredInheritedPropertyBitList();
 
-	static std::vector<PropertyId> GetShorthandUnderlyingProperties(ShorthandId id);
+	// Get the underlying property ids associated by a shorthand.
+	static PropertyIdSet GetShorthandUnderlyingProperties(ShorthandId id);
 
 	static const PropertySpecification& GetPropertySpecification();
 

+ 1 - 2
Include/RmlUi/Core/Types.h

@@ -103,13 +103,13 @@ class Variant;
 class Transform;
 class Decorator;
 class FontEffect;
+class PropertyIdSet;
 struct Animation;
 struct Transition;
 struct TransitionList;
 struct Rectangle;
 enum class PropertyId : uint16_t;
 
-
 // Types for external interfaces.
 using FileHandle = uintptr_t;
 using TextureHandle = uintptr_t;
@@ -154,7 +154,6 @@ using ElementAnimationList = std::vector< ElementAnimation >;
 
 using PseudoClassList = SmallUnorderedSet< String >;
 using AttributeNameList = SmallUnorderedSet< String >;
-using PropertyNameList = SmallOrderedSet< PropertyId >;
 using PropertyMap = UnorderedMap< PropertyId, Property >;
 
 using Dictionary = SmallUnorderedMap< String, Variant >;

+ 1 - 1
Source/Controls/ElementFormControlInput.cpp

@@ -148,7 +148,7 @@ void ElementFormControlInput::OnAttributeChange(const Core::ElementAttributes& c
 }
 
 // Called when properties on the element are changed.
-void ElementFormControlInput::OnPropertyChange(const Core::PropertyNameList& changed_properties)
+void ElementFormControlInput::OnPropertyChange(const Core::PropertyIdSet& changed_properties)
 {
 	ElementFormControl::OnPropertyChange(changed_properties);
 

+ 4 - 3
Source/Controls/ElementFormControlTextArea.cpp

@@ -30,6 +30,7 @@
 #include "../../Include/RmlUi/Core/Math.h"
 #include "../../Include/RmlUi/Core/ElementUtilities.h"
 #include "../../Include/RmlUi/Core/ElementText.h"
+#include "../../Include/RmlUi/Core/PropertyIdSet.h"
 #include "WidgetTextInputMultiLine.h"
 
 namespace Rml {
@@ -170,12 +171,12 @@ void ElementFormControlTextArea::OnAttributeChange(const Core::ElementAttributes
 }
 
 // Called when properties on the control are changed.
-void ElementFormControlTextArea::OnPropertyChange(const Core::PropertyNameList& changed_properties)
+void ElementFormControlTextArea::OnPropertyChange(const Core::PropertyIdSet& changed_properties)
 {
 	ElementFormControl::OnPropertyChange(changed_properties);
 
-	if (changed_properties.find(Core::PropertyId::Color) != changed_properties.end() ||
-		changed_properties.find(Core::PropertyId::BackgroundColor) != changed_properties.end())
+	if (changed_properties.Contains(Core::PropertyId::Color) ||
+		changed_properties.Contains(Core::PropertyId::BackgroundColor))
 		widget->UpdateSelectionColours();
 }
 

+ 4 - 3
Source/Controls/ElementTextSelection.cpp

@@ -28,6 +28,7 @@
 
 #include "ElementTextSelection.h"
 #include "WidgetTextInput.h"
+#include "../../Include/RmlUi/Core/PropertyIdSet.h"
 
 namespace Rml {
 namespace Controls {
@@ -48,7 +49,7 @@ void ElementTextSelection::SetWidget(WidgetTextInput* _widget)
 }
 
 // Processes 'color' and 'background-color' property changes.
-void ElementTextSelection::OnPropertyChange(const Rml::Core::PropertyNameList& changed_properties)
+void ElementTextSelection::OnPropertyChange(const Rml::Core::PropertyIdSet& changed_properties)
 {
 	Element::OnPropertyChange(changed_properties);
 
@@ -56,8 +57,8 @@ void ElementTextSelection::OnPropertyChange(const Rml::Core::PropertyNameList& c
 		return;
 
 	// Check for a colour change.
-	if (changed_properties.find(Core::PropertyId::Color) != changed_properties.end() ||
-		changed_properties.find(Core::PropertyId::BackgroundColor) != changed_properties.end())
+	if (changed_properties.Contains(Core::PropertyId::Color) ||
+		changed_properties.Contains(Core::PropertyId::BackgroundColor))
 	{
 		widget->UpdateSelectionColours();
 	}

+ 1 - 1
Source/Controls/ElementTextSelection.h

@@ -55,7 +55,7 @@ public:
 
 protected:
 	/// Processes 'color' and 'background-color' property changes.
-	void OnPropertyChange(const Rml::Core::PropertyNameList& changed_properties) override;
+	void OnPropertyChange(const Rml::Core::PropertyIdSet& changed_properties) override;
 
 private:
 	WidgetTextInput* widget;

+ 1 - 1
Source/Controls/InputType.cpp

@@ -75,7 +75,7 @@ bool InputType::OnAttributeChange(const Core::ElementAttributes& RMLUI_UNUSED_PA
 }
 
 // Called when properties on the control are changed.
-void InputType::OnPropertyChange(const Core::PropertyNameList& RMLUI_UNUSED_PARAMETER(changed_properties))
+void InputType::OnPropertyChange(const Core::PropertyIdSet& RMLUI_UNUSED_PARAMETER(changed_properties))
 {
 	RMLUI_UNUSED(changed_properties);
 }

+ 1 - 1
Source/Controls/InputType.h

@@ -73,7 +73,7 @@ public:
 	virtual bool OnAttributeChange(const Core::ElementAttributes& changed_attributes);
 	/// Called when properties on the control are changed.
 	/// @param[in] changed_properties The properties changed on the element.
-	virtual void OnPropertyChange(const Core::PropertyNameList& changed_properties);
+	virtual void OnPropertyChange(const Core::PropertyIdSet& changed_properties);
 
 	/// Called when the element is added into a hierarchy.
 	virtual void OnChildAdd();

+ 4 - 3
Source/Controls/InputTypeText.cpp

@@ -31,6 +31,7 @@
 #include "WidgetTextInputSingleLine.h"
 #include "WidgetTextInputSingleLinePassword.h"
 #include "../../Include/RmlUi/Controls/ElementFormControlInput.h"
+#include "../../Include/RmlUi/Core/PropertyIdSet.h"
 
 namespace Rml {
 namespace Controls {
@@ -97,10 +98,10 @@ bool InputTypeText::OnAttributeChange(const Core::ElementAttributes& changed_att
 }
 
 // Called when properties on the control are changed.
-void InputTypeText::OnPropertyChange(const Core::PropertyNameList& changed_properties)
+void InputTypeText::OnPropertyChange(const Core::PropertyIdSet& changed_properties)
 {
-	if (changed_properties.find(Core::PropertyId::Color) != changed_properties.end() ||
-		changed_properties.find(Core::PropertyId::BackgroundColor) != changed_properties.end())
+	if (changed_properties.Contains(Core::PropertyId::Color) ||
+		changed_properties.Contains(Core::PropertyId::BackgroundColor))
 		widget->UpdateSelectionColours();
 }
 

+ 1 - 1
Source/Controls/InputTypeText.h

@@ -69,7 +69,7 @@ public:
 	bool OnAttributeChange(const Core::ElementAttributes& changed_attributes) override;
 	/// Called when properties on the control are changed.
 	/// @param[in] changed_properties The properties changed on the element.
-	void OnPropertyChange(const Core::PropertyNameList& changed_properties) override;
+	void OnPropertyChange(const Core::PropertyIdSet& changed_properties) override;
 
 	/// Checks for necessary functional changes in the control as a result of the event.
 	/// @param[in] event The event to process.

+ 0 - 142
Source/Core/DirtyPropertyList.h

@@ -1,142 +0,0 @@
-/*
- * This source file is part of RmlUi, the HTML/CSS Interface Middleware
- *
- * For the latest information, see http://github.com/mikke89/RmlUi
- *
- * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
- * Copyright (c) 2019 The RmlUi Team, and contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- */
-#ifndef RMLUICOREDIRTYPROPERTYLIST_H
-#define RMLUICOREDIRTYPROPERTYLIST_H
-
-#include "../../Include/RmlUi/Core/Types.h"
-#include "../../Include/RmlUi/Core/ID.h"
-#include "../../Include/RmlUi/Core/StyleSheetSpecification.h"
-#include <bitset>
-
-namespace Rml {
-namespace Core {
-
-
-class DirtyPropertyList {
-private:
-	static constexpr size_t N = (size_t)PropertyId::NumDefinedIds;
-	std::bitset<N> dirty_set;
-	PropertyNameList dirty_custom_properties;
-
-public:
-	DirtyPropertyList(bool all_dirty = false)
-	{
-		static_assert((size_t)PropertyId::Invalid == 0, "DirtyPropertyList makes an assumption that PropertyId::Invalid is zero.");
-		if (all_dirty)
-			DirtyAll();
-	}
-
-	void Insert(PropertyId id) {
-		if ((size_t)id < N)
-			dirty_set.set((size_t)id);
-		else
-			dirty_custom_properties.insert(id);
-	}
-	void Insert(const PropertyNameList& properties) {
-		if (dirty_set.test(0)) return;
-		for (auto id : properties)
-			Insert(id);
-	}
-	void DirtyAll() {
-		// We are using PropertyId::Invalid (0) as a flag for all properties dirty.
-		// However, we set the whole set so that we don't have to do the extra check
-		// for the first bit in Contains().
-		dirty_set.set();
-	}
-	void Clear() {
-		dirty_set.reset();
-		dirty_custom_properties.clear();
-	}
-	void Remove(PropertyId id) {
-		if ((size_t)id < N)
-			dirty_set.reset((size_t)id);
-		else
-			dirty_custom_properties.erase(id);
-	}
-
-	bool Empty() const {
-		return dirty_set.none() && dirty_custom_properties.empty();
-	}
-	bool Contains(PropertyId id) const {
-		if ((size_t)id < N)
-			return dirty_set.test((size_t)id);
-		else if (dirty_set.test(0))
-			return true;
-		else
-			return dirty_custom_properties.count(id) == 1;
-	}
-	bool IsAllDirty() const {
-		return dirty_set.test(0);
-	}
-
-	// Union with another set
-	DirtyPropertyList& operator|=(const DirtyPropertyList& other)
-	{
-		dirty_set |= other.dirty_set;
-		if (other.dirty_custom_properties.size() > 0)
-			dirty_custom_properties.insert(other.dirty_custom_properties.begin(), other.dirty_custom_properties.end());
-		return *this;
-	}
-
-	// Intersection with another set
-	DirtyPropertyList& operator&=(const DirtyPropertyList& other)
-	{
-		dirty_set &= other.dirty_set;
-		if (dirty_custom_properties.size() > 0 && other.dirty_custom_properties.size() > 0 && !dirty_set.test(0))
-		{
-			for (auto it = dirty_custom_properties.begin(); it != dirty_custom_properties.end();)
-				if (other.dirty_custom_properties.count(*it) == 0)
-					it = dirty_custom_properties.erase(it);
-				else
-					++it;
-		}
-		else
-		{
-			dirty_custom_properties.clear();
-		}
-		return *this;
-	}
-
-	PropertyNameList ToPropertyList() const {
-		if (IsAllDirty())
-			return StyleSheetSpecification::GetRegisteredProperties();
-
-		PropertyNameList property_list = dirty_custom_properties;
-		property_list.reserve(dirty_set.count());
-		for (size_t i = 1; i < N; i++)
-			if (dirty_set.test(i))
-				property_list.insert((PropertyId)i);
-		return property_list;
-	}
-};
-
-
-}
-}
-
-#endif

+ 48 - 56
Source/Core/Element.cpp

@@ -30,12 +30,12 @@
 #include "precompiled.h"
 #include "../../Include/RmlUi/Core/Element.h"
 #include "../../Include/RmlUi/Core/Dictionary.h"
+#include "../../Include/RmlUi/Core/PropertyIdSet.h"
 #include "../../Include/RmlUi/Core/TransformPrimitive.h"
 #include <algorithm>
 #include <limits>
 #include "Clock.h"
 #include "ComputeProperty.h"
-#include "DirtyPropertyList.h"
 #include "ElementAnimation.h"
 #include "ElementBackground.h"
 #include "ElementBorder.h"
@@ -226,10 +226,8 @@ void Element::Update(float dp_ratio)
 		// Computed values are just calculated and can safely be used in OnPropertyChange.
 		// However, new properties set during this call will not be available until the next update loop.
 		// Enable RMLUI_DEBUG to get a warning when this happens.
-		if (dirty_properties.IsAllDirty())
-			OnPropertyChange(StyleSheetSpecification::GetRegisteredProperties());
-		else if(!dirty_properties.Empty())
-			OnPropertyChange(dirty_properties.ToPropertyList());
+		if(!dirty_properties.Empty())
+			OnPropertyChange(dirty_properties);
 
 #ifdef RMLUI_DEBUG
 		if (style->AnyPropertiesDirty())
@@ -1748,15 +1746,10 @@ void Element::OnAttributeChange(const ElementAttributes& changed_attributes)
 }
 
 // Called when properties on the element are changed.
-void Element::OnPropertyChange(const PropertyNameList& changed_properties)
+void Element::OnPropertyChange(const PropertyIdSet& changed_properties)
 {
-	bool all_dirty = false;
-	{
-		auto& registered_properties = StyleSheetSpecification::GetRegisteredProperties();
-		if (&registered_properties == &changed_properties || registered_properties == changed_properties)
-			all_dirty = true;
-	}
-
+	const bool all_dirty = changed_properties.IsAllSet();
+	
 	if (!IsLayoutDirty())
 	{
 		if (all_dirty)
@@ -1766,9 +1759,9 @@ void Element::OnPropertyChange(const PropertyNameList& changed_properties)
 		else
 		{
 			// Force a relayout if any of the changed properties require it.
-			for (PropertyNameList::const_iterator i = changed_properties.begin(); i != changed_properties.end(); ++i)
+			for (PropertyId id : changed_properties)
 			{
-				const PropertyDefinition* property_definition = StyleSheetSpecification::GetProperty(*i);
+				const PropertyDefinition* property_definition = StyleSheetSpecification::GetProperty(id);
 				if (property_definition)
 				{
 					if (property_definition->IsLayoutForced())
@@ -1782,10 +1775,9 @@ void Element::OnPropertyChange(const PropertyNameList& changed_properties)
 	}
 
 
-
 	// Update the visibility.
-	if (all_dirty || changed_properties.find(PropertyId::Visibility) != changed_properties.end() ||
-		changed_properties.find(PropertyId::Display) != changed_properties.end())
+	if (all_dirty || changed_properties.Contains(PropertyId::Visibility) ||
+		changed_properties.Contains(PropertyId::Display))
 	{
 		bool new_visibility = (element_meta->computed_values.display != Style::Display::None && element_meta->computed_values.visibility == Style::Visibility::Visible);
 			
@@ -1798,7 +1790,7 @@ void Element::OnPropertyChange(const PropertyNameList& changed_properties)
 		}
 
 		if (all_dirty || 
-			changed_properties.find(PropertyId::Display) != changed_properties.end())
+			changed_properties.Contains(PropertyId::Display))
 		{
 			// Due to structural pseudo-classes, this may change the element definition in siblings and parent.
 			// However, the definitions will only be changed on the next update loop which may result in jarring behavior for one @frame.
@@ -1810,11 +1802,11 @@ void Element::OnPropertyChange(const PropertyNameList& changed_properties)
 
 	// Fetch a new font face if it has been changed.
 	if (all_dirty ||
-		changed_properties.find(PropertyId::FontFamily) != changed_properties.end() ||
-		changed_properties.find(PropertyId::FontCharset) != changed_properties.end() ||
-		changed_properties.find(PropertyId::FontWeight) != changed_properties.end() ||
-		changed_properties.find(PropertyId::FontStyle) != changed_properties.end() ||
-		changed_properties.find(PropertyId::FontSize) != changed_properties.end())
+		changed_properties.Contains(PropertyId::FontFamily) ||
+		changed_properties.Contains(PropertyId::FontCharset) ||
+		changed_properties.Contains(PropertyId::FontWeight) ||
+		changed_properties.Contains(PropertyId::FontStyle) ||
+		changed_properties.Contains(PropertyId::FontSize))
 	{
 		// Fetch the new font face.
 		SharedPtr<FontFaceHandle> new_font_face_handle = ElementUtilities::GetFontFaceHandle(element_meta->computed_values);
@@ -1830,10 +1822,10 @@ void Element::OnPropertyChange(const PropertyNameList& changed_properties)
 
 	// Update the position.
 	if (all_dirty ||
-		changed_properties.find(PropertyId::Left) != changed_properties.end() ||
-		changed_properties.find(PropertyId::Right) != changed_properties.end() ||
-		changed_properties.find(PropertyId::Top) != changed_properties.end() ||
-		changed_properties.find(PropertyId::Bottom) != changed_properties.end())
+		changed_properties.Contains(PropertyId::Left) ||
+		changed_properties.Contains(PropertyId::Right) ||
+		changed_properties.Contains(PropertyId::Top) ||
+		changed_properties.Contains(PropertyId::Bottom))
 	{
 		// TODO: This should happen during/after layout, as the containing box is not properly defined yet. Off-by-one @frame issue.
 		UpdateOffset();
@@ -1842,7 +1834,7 @@ void Element::OnPropertyChange(const PropertyNameList& changed_properties)
 
 	// Update the z-index.
 	if (all_dirty || 
-		changed_properties.find(PropertyId::ZIndex) != changed_properties.end())
+		changed_properties.Contains(PropertyId::ZIndex))
 	{
 		Style::ZIndex z_index_property = element_meta->computed_values.z_index;
 
@@ -1887,69 +1879,69 @@ void Element::OnPropertyChange(const PropertyNameList& changed_properties)
 
 	// Dirty the background if it's changed.
     if (all_dirty ||
-        changed_properties.find(PropertyId::BackgroundColor) != changed_properties.end() ||
-		changed_properties.find(PropertyId::Opacity) != changed_properties.end() ||
-		changed_properties.find(PropertyId::ImageColor) != changed_properties.end()) {
+        changed_properties.Contains(PropertyId::BackgroundColor) ||
+		changed_properties.Contains(PropertyId::Opacity) ||
+		changed_properties.Contains(PropertyId::ImageColor)) {
 		background->DirtyBackground();
     }
 	
 	// Dirty the decoration if it's changed.
 	if (all_dirty ||
-		changed_properties.find(PropertyId::Decorator) != changed_properties.end() ||
-		changed_properties.find(PropertyId::Opacity) != changed_properties.end() ||
-		changed_properties.find(PropertyId::ImageColor) != changed_properties.end()) {
+		changed_properties.Contains(PropertyId::Decorator) ||
+		changed_properties.Contains(PropertyId::Opacity) ||
+		changed_properties.Contains(PropertyId::ImageColor)) {
 		decoration->DirtyDecorators();
 	}
 
 	// Dirty the border if it's changed.
 	if (all_dirty || 
-		changed_properties.find(PropertyId::BorderTopWidth) != changed_properties.end() ||
-		changed_properties.find(PropertyId::BorderRightWidth) != changed_properties.end() ||
-		changed_properties.find(PropertyId::BorderBottomWidth) != changed_properties.end() ||
-		changed_properties.find(PropertyId::BorderLeftWidth) != changed_properties.end() ||
-		changed_properties.find(PropertyId::BorderTopColor) != changed_properties.end() ||
-		changed_properties.find(PropertyId::BorderRightColor) != changed_properties.end() ||
-		changed_properties.find(PropertyId::BorderBottomColor) != changed_properties.end() ||
-		changed_properties.find(PropertyId::BorderLeftColor) != changed_properties.end() ||
-		changed_properties.find(PropertyId::Opacity) != changed_properties.end())
+		changed_properties.Contains(PropertyId::BorderTopWidth) ||
+		changed_properties.Contains(PropertyId::BorderRightWidth) ||
+		changed_properties.Contains(PropertyId::BorderBottomWidth) ||
+		changed_properties.Contains(PropertyId::BorderLeftWidth) ||
+		changed_properties.Contains(PropertyId::BorderTopColor) ||
+		changed_properties.Contains(PropertyId::BorderRightColor) ||
+		changed_properties.Contains(PropertyId::BorderBottomColor) ||
+		changed_properties.Contains(PropertyId::BorderLeftColor) ||
+		changed_properties.Contains(PropertyId::Opacity))
 		border->DirtyBorder();
 
 	
 	// Check for clipping state changes
 	if (all_dirty ||
-		changed_properties.find(PropertyId::Clip) != changed_properties.end() ||
-		changed_properties.find(PropertyId::OverflowX) != changed_properties.end() ||
-		changed_properties.find(PropertyId::OverflowY) != changed_properties.end())
+		changed_properties.Contains(PropertyId::Clip) ||
+		changed_properties.Contains(PropertyId::OverflowX) ||
+		changed_properties.Contains(PropertyId::OverflowY))
 	{
 		clipping_state_dirty = true;
 	}
 
 	// Check for `perspective' and `perspective-origin' changes
 	if (all_dirty ||
-		changed_properties.find(PropertyId::Perspective) != changed_properties.end() ||
-		changed_properties.find(PropertyId::PerspectiveOriginX) != changed_properties.end() ||
-		changed_properties.find(PropertyId::PerspectiveOriginY) != changed_properties.end())
+		changed_properties.Contains(PropertyId::Perspective) ||
+		changed_properties.Contains(PropertyId::PerspectiveOriginX) ||
+		changed_properties.Contains(PropertyId::PerspectiveOriginY))
 	{
 		DirtyTransformState(true, false, false);
 	}
 
 	// Check for `transform' and `transform-origin' changes
 	if (all_dirty ||
-		changed_properties.find(PropertyId::Transform) != changed_properties.end() ||
-		changed_properties.find(PropertyId::TransformOriginX) != changed_properties.end() ||
-		changed_properties.find(PropertyId::TransformOriginY) != changed_properties.end() ||
-		changed_properties.find(PropertyId::TransformOriginZ) != changed_properties.end())
+		changed_properties.Contains(PropertyId::Transform) ||
+		changed_properties.Contains(PropertyId::TransformOriginX) ||
+		changed_properties.Contains(PropertyId::TransformOriginY) ||
+		changed_properties.Contains(PropertyId::TransformOriginZ))
 	{
 		DirtyTransformState(false, true, false);
 	}
 
 	// Check for `animation' changes
-	if (all_dirty || changed_properties.find(PropertyId::Animation) != changed_properties.end())
+	if (all_dirty || changed_properties.Contains(PropertyId::Animation))
 	{
 		dirty_animation = true;
 	}
 	// Check for `transition' changes
-	if (all_dirty || changed_properties.find(PropertyId::Transition) != changed_properties.end())
+	if (all_dirty || changed_properties.Contains(PropertyId::Transition))
 	{
 		dirty_transition = true;
 	}

+ 2 - 2
Source/Core/ElementDefinition.cpp

@@ -45,10 +45,10 @@ const Property* ElementDefinition::GetProperty(PropertyId id) const
 	return properties.GetProperty(id);
 }
 
-void ElementDefinition::GetDefinedProperties(PropertyNameList& property_names) const
+void ElementDefinition::GetDefinedProperties(PropertyIdSet& property_names) const
 {
 	for (PropertyMap::const_iterator i = properties.GetProperties().begin(); i != properties.GetProperties().end(); ++i)
-		property_names.insert((*i).first);
+		property_names.Insert((*i).first);
 }
 
 }

+ 1 - 1
Source/Core/ElementDefinition.h

@@ -56,7 +56,7 @@ public:
 
 	/// Returns the list of property ids this element definition defines.
 	/// @param[out] property_names The list to store the defined property ids in.
-	void GetDefinedProperties(PropertyNameList& property_names) const;
+	void GetDefinedProperties(PropertyIdSet& property_names) const;
 
 	const PropertyDictionary& GetProperties() const { return properties; }
 

+ 6 - 6
Source/Core/ElementDocument.cpp

@@ -371,18 +371,18 @@ void ElementDocument::DirtyDpProperties()
 }
 
 // Repositions the document if necessary.
-void ElementDocument::OnPropertyChange(const PropertyNameList& changed_properties)
+void ElementDocument::OnPropertyChange(const PropertyIdSet& changed_properties)
 {
 	Element::OnPropertyChange(changed_properties);
 
 	// If the document's font-size has been changed, we need to dirty all rem properties.
-	if (changed_properties.find(PropertyId::FontSize) != changed_properties.end())
+	if (changed_properties.Contains(PropertyId::FontSize))
 		GetStyle()->DirtyPropertiesWithUnitRecursive(Property::REM);
 
-	if (changed_properties.find(PropertyId::Top) != changed_properties.end() ||
-		changed_properties.find(PropertyId::Right) != changed_properties.end() ||
-		changed_properties.find(PropertyId::Bottom) != changed_properties.end() ||
-		changed_properties.find(PropertyId::Left) != changed_properties.end())
+	if (changed_properties.Contains(PropertyId::Top) ||
+		changed_properties.Contains(PropertyId::Right) ||
+		changed_properties.Contains(PropertyId::Bottom) ||
+		changed_properties.Contains(PropertyId::Left))
 		DirtyPosition();
 }
 

+ 3 - 3
Source/Core/ElementImage.cpp

@@ -156,12 +156,12 @@ void ElementImage::OnAttributeChange(const Rml::Core::ElementAttributes& changed
 		DirtyLayout();
 }
 
-void ElementImage::OnPropertyChange(const PropertyNameList& changed_properties)
+void ElementImage::OnPropertyChange(const PropertyIdSet& changed_properties)
 {
     Element::OnPropertyChange(changed_properties);
 
-    if (changed_properties.find(PropertyId::ImageColor) != changed_properties.end() ||
-        changed_properties.find(PropertyId::Opacity) != changed_properties.end()) {
+    if (changed_properties.Contains(PropertyId::ImageColor) ||
+        changed_properties.Contains(PropertyId::Opacity)) {
         GenerateGeometry();
     }
 }

+ 1 - 1
Source/Core/ElementImage.h

@@ -88,7 +88,7 @@ protected:
 
 	/// Called when properties on the element are changed.
 	/// @param[in] changed_properties The properties changed on the element.
-	void OnPropertyChange(const PropertyNameList& changed_properties) override;
+	void OnPropertyChange(const PropertyIdSet& changed_properties) override;
 
 private:
 	// Generates the element's geometry.

+ 23 - 21
Source/Core/ElementStyle.cpp

@@ -36,6 +36,7 @@
 #include "../../Include/RmlUi/Core/Property.h"
 #include "../../Include/RmlUi/Core/PropertyDefinition.h"
 #include "../../Include/RmlUi/Core/PropertyDictionary.h"
+#include "../../Include/RmlUi/Core/PropertyIdSet.h"
 #include "../../Include/RmlUi/Core/StyleSheetSpecification.h"
 #include "../../Include/RmlUi/Core/TransformPrimitive.h"
 #include "ElementBackground.h"
@@ -44,18 +45,18 @@
 #include "ElementDefinition.h"
 #include "FontFaceHandle.h"
 #include "ComputeProperty.h"
-#include "DirtyPropertyList.h"
 #include "PropertiesIterator.h"
 
 
 namespace Rml {
 namespace Core {
 
-ElementStyle::ElementStyle(Element* _element) : dirty_properties(true)
+ElementStyle::ElementStyle(Element* _element)
 {
 	definition = nullptr;
 	element = _element;
 
+	dirty_properties.SetAll();
 	definition_dirty = true;
 }
 
@@ -111,10 +112,10 @@ const Property* ElementStyle::GetProperty(PropertyId id, const Element* element,
 
 // Apply transition to relevant properties if a transition is defined on element.
 // Properties that are part of a transition are removed from the properties list.
-void ElementStyle::TransitionPropertyChanges(Element* element, PropertyNameList& properties, const PropertyDictionary& inline_properties, const ElementDefinition* old_definition, const ElementDefinition* new_definition)
+void ElementStyle::TransitionPropertyChanges(Element* element, PropertyIdSet& properties, const PropertyDictionary& inline_properties, const ElementDefinition* old_definition, const ElementDefinition* new_definition)
 {
 	RMLUI_ASSERT(element);
-	if (!old_definition || !new_definition || properties.empty())
+	if (!old_definition || !new_definition || properties.Empty())
 		return;
 
 	// We get the local property instead of the computed value here, because we want to intercept property changes even before the computed values are ready.
@@ -143,7 +144,7 @@ void ElementStyle::TransitionPropertyChanges(Element* element, PropertyNameList&
 				{
 					transition.id = *it;
 					if (add_transition(transition))
-						it = properties.erase(it);
+						it = properties.Erase(it);
 					else
 						++it;
 				}
@@ -152,11 +153,10 @@ void ElementStyle::TransitionPropertyChanges(Element* element, PropertyNameList&
 			{
 				for (auto& transition : transition_list.transitions)
 				{
-					auto it = properties.find(transition.id);
-					if (it != properties.end())
+					if (properties.Contains(transition.id))
 					{
 						if (add_transition(transition))
-							properties.erase(it);
+							properties.Erase(transition.id);
 					}
 				}
 			}
@@ -182,12 +182,12 @@ void ElementStyle::UpdateDefinition()
 		{
 			// Since we had no definition before there is a likelihood that everything is dirty.
 			// We could do as in the next else-if block, but this is considerably faster.
-			dirty_properties.DirtyAll();
+			dirty_properties.SetAll();
 			definition = new_definition;
 		}
 		else if (new_definition != definition)
 		{
-			PropertyNameList changed_properties;
+			PropertyIdSet changed_properties;
 			
 			if (definition)
 				definition->GetDefinedProperties(changed_properties);
@@ -198,13 +198,14 @@ void ElementStyle::UpdateDefinition()
 			if (definition && new_definition)
 			{
 				// Remove properties that compare equal from the changed list.
+				// todo/performance: We can 'and' two property id sets instead and only compare the resulting set.
 				for (auto it = changed_properties.begin(); it != changed_properties.end();)
 				{
 					PropertyId id = *it;
 					const Property* p0 = definition->GetProperty(id);
 					const Property* p1 = new_definition->GetProperty(id);
 					if (p0 && p1 && *p0 == *p1)
-						it = changed_properties.erase(it);
+						it = changed_properties.Erase(it);
 					else
 						++it;
 				}
@@ -488,24 +489,25 @@ void ElementStyle::DirtyProperty(PropertyId id)
 }
 
 // Sets a list of properties as dirty.
-void ElementStyle::DirtyProperties(const PropertyNameList& properties)
+void ElementStyle::DirtyProperties(const PropertyIdSet& properties)
 {
-	dirty_properties.Insert(properties);
+	dirty_properties |= properties;
 }
 
 // Sets a list of our potentially inherited properties as dirtied by an ancestor.
-void ElementStyle::DirtyInheritedProperties(const PropertyNameList& properties)
+void ElementStyle::DirtyInheritedProperties(const PropertyIdSet& properties)
 {
-	dirty_properties.Insert(properties);
+	// todo: We no longer need to do anything special (inheritance handled in computevalues): Can safely remove function, replace with DirtyProperties().
+	dirty_properties |= properties;
 }
 
-static void DirtyEmProperties(DirtyPropertyList& dirty_properties, Element* element)
+static void DirtyEmProperties(PropertyIdSet& dirty_properties, Element* element)
 {
 	// Either we can dirty every property, or we can iterate over all properties and see if anyone uses em-units.
 	// Choose whichever is fastest based on benchmarking.
 #if 1
 	// Dirty every property
-	dirty_properties.DirtyAll();
+	dirty_properties.SetAll();
 #else
 	if (dirty_properties.AllDirty())
 		return;
@@ -526,10 +528,10 @@ static void DirtyEmProperties(DirtyPropertyList& dirty_properties, Element* elem
 }
 
 
-DirtyPropertyList ElementStyle::ComputeValues(Style::ComputedValues& values, const Style::ComputedValues* parent_values, const Style::ComputedValues* document_values, bool values_are_default_initialized, float dp_ratio)
+PropertyIdSet ElementStyle::ComputeValues(Style::ComputedValues& values, const Style::ComputedValues* parent_values, const Style::ComputedValues* document_values, bool values_are_default_initialized, float dp_ratio)
 {
 	if (dirty_properties.Empty())
-		return DirtyPropertyList();
+		return PropertyIdSet();
 
 	// Generally, this is how it works:
 	//   1. Assign default values (clears any removed properties)
@@ -889,7 +891,7 @@ DirtyPropertyList ElementStyle::ComputeValues(Style::ComputedValues& values, con
 
 	// Next, pass inheritable dirty properties onto our children
 	// @performance: We might avoid an allocation here in case of dirty non-inherited custom properties. Instead of the initial copy and &=, introduce & operator.
-	DirtyPropertyList dirty_inherited_properties = dirty_properties;
+	PropertyIdSet dirty_inherited_properties = dirty_properties;
 	dirty_inherited_properties &= StyleSheetSpecification::GetRegisteredInheritedPropertyBitList();
 
 	if (!dirty_inherited_properties.Empty())
@@ -901,7 +903,7 @@ DirtyPropertyList ElementStyle::ComputeValues(Style::ComputedValues& values, con
 		}
 	}
 	
-	DirtyPropertyList result(std::move(dirty_properties));
+	PropertyIdSet result(std::move(dirty_properties));
 	dirty_properties.Clear();
 	return result;
 }

+ 6 - 6
Source/Core/ElementStyle.h

@@ -30,7 +30,7 @@
 #define RMLUICOREELEMENTSTYLE_H
 
 #include "../../Include/RmlUi/Core/Types.h"
-#include "DirtyPropertyList.h"
+#include "../../Include/RmlUi/Core/PropertyIdSet.h"
 
 namespace Rml {
 namespace Core {
@@ -123,7 +123,7 @@ public:
 
 	/// Turns the local and inherited properties into computed values for this element. These values can in turn be used during the layout procedure.
 	/// Must be called in correct order, always parent before its children.
-	DirtyPropertyList ComputeValues(Style::ComputedValues& values, const Style::ComputedValues* parent_values, const Style::ComputedValues* document_values, bool values_are_default_initialized, float dp_ratio);
+	PropertyIdSet ComputeValues(Style::ComputedValues& values, const Style::ComputedValues* parent_values, const Style::ComputedValues* document_values, bool values_are_default_initialized, float dp_ratio);
 
 	/// Returns an iterator for iterating the local properties of this element.
 	/// Note: Modifying the element's style invalidates its iterator.
@@ -135,13 +135,13 @@ private:
 	// Sets a single property as dirty.
 	void DirtyProperty(PropertyId id);
 	// Sets a list of properties as dirty.
-	void DirtyProperties(const PropertyNameList& properties);
+	void DirtyProperties(const PropertyIdSet& properties);
 	// Sets a list of our potentially inherited properties as dirtied by an ancestor.
-	void DirtyInheritedProperties(const PropertyNameList& properties);
+	void DirtyInheritedProperties(const PropertyIdSet& properties);
 
 	static const Property* GetLocalProperty(PropertyId id, const PropertyDictionary & inline_properties, const ElementDefinition * definition);
 	static const Property* GetProperty(PropertyId id, const Element * element, const PropertyDictionary & inline_properties, const ElementDefinition * definition);
-	static void TransitionPropertyChanges(Element * element, PropertyNameList & properties, const PropertyDictionary & inline_properties, const ElementDefinition * old_definition, const ElementDefinition * new_definition);
+	static void TransitionPropertyChanges(Element * element, PropertyIdSet & properties, const PropertyDictionary & inline_properties, const ElementDefinition * old_definition, const ElementDefinition * new_definition);
 
 	// Element these properties belong to
 	Element* element;
@@ -158,7 +158,7 @@ private:
 	// Set if a new element definition should be fetched from the style.
 	bool definition_dirty;
 
-	DirtyPropertyList dirty_properties;
+	PropertyIdSet dirty_properties;
 };
 
 }

+ 9 - 9
Source/Core/ElementTextDefault.cpp

@@ -283,7 +283,7 @@ void ElementTextDefault::SuppressAutoLayout()
 	dirty_layout_on_change = false;
 }
 
-void ElementTextDefault::OnPropertyChange(const PropertyNameList& changed_properties)
+void ElementTextDefault::OnPropertyChange(const PropertyIdSet& changed_properties)
 {
 	Element::OnPropertyChange(changed_properties);
 
@@ -291,8 +291,8 @@ void ElementTextDefault::OnPropertyChange(const PropertyNameList& changed_proper
 	bool font_face_changed = false;
 	auto& computed = GetComputedValues();
 
-	if (changed_properties.find(PropertyId::Color) != changed_properties.end() ||
-		changed_properties.find(PropertyId::Opacity) != changed_properties.end())
+	if (changed_properties.Contains(PropertyId::Color) ||
+		changed_properties.Contains(PropertyId::Opacity))
 	{
 		// Fetch our (potentially) new colour.
 		Colourb new_colour = computed.color;
@@ -303,11 +303,11 @@ void ElementTextDefault::OnPropertyChange(const PropertyNameList& changed_proper
 			colour = new_colour;
 	}
 
-	if (changed_properties.find(PropertyId::FontFamily) != changed_properties.end() ||
-		changed_properties.find(PropertyId::FontCharset) != changed_properties.end() ||
-		changed_properties.find(PropertyId::FontWeight) != changed_properties.end() ||
-		changed_properties.find(PropertyId::FontStyle) != changed_properties.end() ||
-		changed_properties.find(PropertyId::FontSize) != changed_properties.end())
+	if (changed_properties.Contains(PropertyId::FontFamily) ||
+		changed_properties.Contains(PropertyId::FontCharset) ||
+		changed_properties.Contains(PropertyId::FontWeight) ||
+		changed_properties.Contains(PropertyId::FontStyle) ||
+		changed_properties.Contains(PropertyId::FontSize))
 	{
 		font_face_changed = true;
 
@@ -315,7 +315,7 @@ void ElementTextDefault::OnPropertyChange(const PropertyNameList& changed_proper
 		font_dirty = true;
 	}
 
-	if (changed_properties.find(PropertyId::TextDecoration) != changed_properties.end())
+	if (changed_properties.Contains(PropertyId::TextDecoration))
 	{
 		decoration_property = (int)computed.text_decoration;
 		if (decoration_property != TEXT_DECORATION_NONE)

+ 1 - 1
Source/Core/ElementTextDefault.h

@@ -78,7 +78,7 @@ public:
 	void SuppressAutoLayout() override;
 
 protected:
-	void OnPropertyChange(const PropertyNameList& properties) override;
+	void OnPropertyChange(const PropertyIdSet& properties) override;
 
 	/// Returns the RML of this element
 	/// @param content[out] The raw text.

+ 2 - 2
Source/Core/PropertiesIterator.h

@@ -29,7 +29,7 @@
 #define RMLUICOREPROPERTIESITERATOR_H
 
 #include "../../Include/RmlUi/Core/Types.h"
-#include "DirtyPropertyList.h"
+#include "../../Include/RmlUi/Core/PropertyIdSet.h"
 
 namespace Rml {
 namespace Core {
@@ -80,7 +80,7 @@ public:
 	}
 
 private:
-	DirtyPropertyList iterated_properties;
+	PropertyIdSet iterated_properties;
 	PropertyIt it_style, it_style_end;
 	PropertyIt it_definition, it_definition_end;
 	bool at_end = false;

+ 6 - 6
Source/Core/PropertyParserAnimation.cpp

@@ -234,7 +234,7 @@ static bool ParseTransition(Property & property, const StringList& transition_va
 	{
 
 		Transition transition;
-		PropertyNameList target_property_names;
+		PropertyIdSet target_property_names;
 
 		StringList arguments;
 		StringUtilities::ExpandString(arguments, single_transition_value, ' ');
@@ -313,13 +313,13 @@ static bool ParseTransition(Property & property, const StringList& transition_va
 					// Must be a property name or shorthand, expand now
 					if (auto shorthand = StyleSheetSpecification::GetShorthand(argument))
 					{
-						auto underlying_properties = StyleSheetSpecification::GetShorthandUnderlyingProperties(shorthand->id);
-						target_property_names.insert(underlying_properties.begin(), underlying_properties.end());
+						PropertyIdSet underlying_properties = StyleSheetSpecification::GetShorthandUnderlyingProperties(shorthand->id);
+						target_property_names |= underlying_properties;
 					}
 					else if (auto definition = StyleSheetSpecification::GetProperty(argument))
 					{
 						// Single property
-						target_property_names.insert(definition->GetId());
+						target_property_names.Insert(definition->GetId());
 					}
 					else
 					{
@@ -331,8 +331,8 @@ static bool ParseTransition(Property & property, const StringList& transition_va
 		}
 
 		// Validate the parsed transition
-		if (target_property_names.empty() || transition.duration <= 0.0f || transition.reverse_adjustment_factor < 0.0f || transition.reverse_adjustment_factor > 1.0f
-			|| (transition_list.all && target_property_names.size() != 1))
+		if (target_property_names.Empty() || transition.duration <= 0.0f || transition.reverse_adjustment_factor < 0.0f || transition.reverse_adjustment_factor > 1.0f
+			|| (transition_list.all && target_property_names.Size() != 1))
 		{
 			return false;
 		}

+ 4 - 5
Source/Core/PropertySpecification.cpp

@@ -40,7 +40,6 @@ PropertySpecification::PropertySpecification(size_t reserve_num_properties, size
 	// Increment reserve numbers by one because the 'invalid' property occupies the first element
 	properties(reserve_num_properties + 1, nullptr), shorthands(reserve_num_shorthands + 1, nullptr), property_map(reserve_num_properties + 1), shorthand_map(reserve_num_shorthands + 1)
 {
-	property_names.reserve(reserve_num_properties);
 }
 
 PropertySpecification::~PropertySpecification()
@@ -81,9 +80,9 @@ PropertyDefinition& PropertySpecification::RegisterProperty(const String& proper
 	PropertyDefinition* property_definition = new PropertyDefinition(id, default_value, inherited, forces_layout);
 
 	properties[index] = property_definition;
-	property_names.insert(id);
+	property_names.Insert(id);
 	if (inherited)
-		inherited_property_names.insert(id);
+		inherited_property_names.Insert(id);
 
 	return *property_definition;
 }
@@ -103,13 +102,13 @@ const PropertyDefinition* PropertySpecification::GetProperty(const String& prope
 }
 
 // Fetches a list of the names of all registered property definitions.
-const PropertyNameList& PropertySpecification::GetRegisteredProperties(void) const
+const PropertyIdSet& PropertySpecification::GetRegisteredProperties(void) const
 {
 	return property_names;
 }
 
 // Fetches a list of the names of all registered property definitions.
-const PropertyNameList& PropertySpecification::GetRegisteredInheritedProperties(void) const
+const PropertyIdSet& PropertySpecification::GetRegisteredInheritedProperties(void) const
 {
 	return inherited_property_names;
 }

+ 10 - 12
Source/Core/StyleSheetSpecification.cpp

@@ -29,6 +29,7 @@
  
 #include "precompiled.h"
 #include "../../Include/RmlUi/Core/StyleSheetSpecification.h"
+#include "../../Include/RmlUi/Core/PropertyIdSet.h"
 #include "PropertyParserNumber.h"
 #include "PropertyParserAnimation.h"
 #include "PropertyParserColour.h"
@@ -36,7 +37,6 @@
 #include "PropertyParserString.h"
 #include "PropertyParserTransform.h"
 #include "PropertyShorthandDefinition.h"
-#include "DirtyPropertyList.h"
 
 namespace Rml {
 namespace Core {
@@ -44,7 +44,7 @@ namespace Core {
 
 static StyleSheetSpecification* instance = nullptr;
 
-static DirtyPropertyList registered_inherited_properties;
+static PropertyIdSet registered_inherited_properties;
 
 
 StyleSheetSpecification::StyleSheetSpecification() : 
@@ -139,12 +139,12 @@ const PropertyDefinition* StyleSheetSpecification::GetProperty(PropertyId id)
 }
 
 // Fetches a list of the names of all registered property definitions.
-const PropertyNameList& StyleSheetSpecification::GetRegisteredProperties()
+const PropertyIdSet& StyleSheetSpecification::GetRegisteredProperties()
 {
 	return instance->properties.GetRegisteredProperties();
 }
 
-const PropertyNameList & StyleSheetSpecification::GetRegisteredInheritedProperties()
+const PropertyIdSet & StyleSheetSpecification::GetRegisteredInheritedProperties()
 {
 	return instance->properties.GetRegisteredInheritedProperties();
 }
@@ -194,30 +194,28 @@ const String& StyleSheetSpecification::GetShorthandName(ShorthandId id)
 	return instance->properties.shorthand_map.GetName(id);
 }
 
-const DirtyPropertyList& StyleSheetSpecification::GetRegisteredInheritedPropertyBitList()
+const PropertyIdSet& StyleSheetSpecification::GetRegisteredInheritedPropertyBitList()
 {
 	return registered_inherited_properties;
 }
 
-std::vector<PropertyId> StyleSheetSpecification::GetShorthandUnderlyingProperties(ShorthandId id)
+PropertyIdSet StyleSheetSpecification::GetShorthandUnderlyingProperties(ShorthandId id)
 {
-	std::vector<PropertyId> result;
+	PropertyIdSet result;
 	const ShorthandDefinition* shorthand = instance->properties.GetShorthand(id);
 	if (!shorthand)
 		return result;
 
-	result.reserve(shorthand->items.size());
 	for (auto& item : shorthand->items)
 	{
 		if (item.type == ShorthandItemType::Property)
 		{
-			result.push_back(item.property_id);
+			result.Insert(item.property_id);
 		}
 		else if (item.type == ShorthandItemType::Shorthand)
 		{
-			// When we have a shorthand pointing to another shorthands, call us recursively
-			std::vector<PropertyId> new_items = GetShorthandUnderlyingProperties(item.shorthand_id);
-			result.insert(result.end(), new_items.begin(), new_items.end());
+			// When we have a shorthand pointing to another shorthands, call us recursively. Add the union of the previous result and new properties.
+			result |= GetShorthandUnderlyingProperties(item.shorthand_id);
 		}
 	}
 	return result;