浏览代码

Data models: Allow dirtying an arbitrary address (WIP)

Michael Ragazzon 1 年之前
父节点
当前提交
0b7e00638c

+ 2 - 0
Include/RmlUi/Core/DataTypes.h

@@ -66,6 +66,8 @@ using DirtyVariables = SmallUnorderedSet<String>;
 struct DataAddressEntry {
 	DataAddressEntry(String name) : name(std::move(name)), index(-1) {}
 	DataAddressEntry(int index) : index(index) {}
+	bool operator==(const DataAddressEntry& other) const { return name == other.name && index == other.index; }
+	bool operator!=(const DataAddressEntry& other) const { return !(*this == other); }
 	String name;
 	int index;
 };

+ 14 - 8
Samples/basic/player_list/src/main.cpp

@@ -111,17 +111,23 @@ public:
 
 		RMLUI_ZoneScoped;
 
-		for (PlayerEntry& player : data)
-		{
-			player.score = (uint32_t)Rml::Math::RandomInteger(100);
-			player.latency = (uint16_t)Rml::Math::RandomInteger(500);
-		}
+		// for (PlayerEntry& player : data)
+		//{
+		//	player.score = (uint32_t)Rml::Math::RandomInteger(100);
+		//	player.latency = (uint16_t)Rml::Math::RandomInteger(500);
+		// }
+
+		data[5].score = (uint32_t)Rml::Math::RandomInteger(100);
+
+		// Filter();
+		// Sort();
+		//
 
-		Filter();
-		Sort();
+		entries = data;
 
 		RMLUI_ASSERT(data_model);
-		data_model.DirtyVariable("players");
+		data_model.DirtyVariable("players[5].score");
+		// data_model.DirtyVariable("players");
 
 		last_update = t;
 	}

+ 22 - 0
Source/Core/DataExpression.cpp

@@ -1167,6 +1167,28 @@ StringList DataExpression::GetVariableNameList() const
 	return list;
 }
 
+bool DataExpression::HasAddressDependency(const DataAddress& match_address) const
+{
+	for (const DataAddress& address : addresses)
+	{
+		if (match_address.size() > address.size())
+			continue;
+
+		bool match = true;
+		for (size_t i = 0; i < match_address.size(); i++)
+		{
+			if (match_address[i] != address[i])
+			{
+				match = false;
+				break;
+			}
+		}
+		if (match)
+			return true;
+	}
+	return false;
+}
+
 DataExpressionInterface::DataExpressionInterface(DataModel* data_model, Element* element, Event* event) :
 	data_model(data_model), element(element), event(event)
 {}

+ 2 - 0
Source/Core/DataExpression.h

@@ -69,6 +69,8 @@ public:
 
 	// Available after Parse()
 	StringList GetVariableNameList() const;
+	// Available after Parse()
+	bool HasAddressDependency(const DataAddress& match_address) const;
 
 private:
 	String expression;

+ 3 - 3
Source/Core/DataModel.cpp

@@ -244,7 +244,7 @@ void DataModel::CopyAliases(Element* from_element, Element* to_element)
 	{
 		// Need to create a copy to prevent errors during concurrent modification for 3rd party containers
 		auto copy = existing_map->second;
-		for (auto const& it : copy)
+		for (const auto& it : copy)
 			aliases[to_element][it.first] = std::move(it.second);
 	}
 }
@@ -348,8 +348,8 @@ bool DataModel::GetVariableInto(const DataAddress& address, Variant& out_value)
 
 void DataModel::DirtyVariable(const String& variable_name)
 {
-	RMLUI_ASSERTMSG(LegalVariableName(variable_name) == nullptr, "Illegal variable name provided. Only top-level variables can be dirtied.");
-	RMLUI_ASSERTMSG(variables.count(variable_name) == 1, "In DirtyVariable: Variable name not found among added variables.");
+	// RMLUI_ASSERTMSG(LegalVariableName(variable_name) == nullptr, "Illegal variable name provided. Only top-level variables can be dirtied.");
+	// RMLUI_ASSERTMSG(variables.count(variable_name) == 1, "In DirtyVariable: Variable name not found among added variables.");
 	dirty_variables.emplace(variable_name);
 }
 

+ 16 - 4
Source/Core/DataView.cpp

@@ -28,7 +28,7 @@
 
 #include "DataView.h"
 #include "../../Include/RmlUi/Core/Element.h"
-#include "../../Include/RmlUi/Core/Profiling.h"
+#include "DataModel.h"
 #include <algorithm>
 
 namespace Rml {
@@ -120,9 +120,21 @@ bool DataViews::Update(DataModel& model, const DirtyVariables& dirty_variables)
 
 		for (const String& variable_name : dirty_variables)
 		{
-			auto pair = name_view_map.equal_range(variable_name);
-			for (auto it = pair.first; it != pair.second; ++it)
-				dirty_views.push_back(it->second);
+			DataAddress address = model.ResolveAddress(variable_name, nullptr);
+			if (address.size() > 1)
+			{
+				for (const DataViewPtr& view : views)
+				{
+					if (view->HasAddressDependency(address))
+						dirty_views.push_back(view.get());
+				}
+			}
+			else
+			{
+				auto pair = name_view_map.equal_range(variable_name);
+				for (auto it = pair.first; it != pair.second; ++it)
+					dirty_views.push_back(it->second);
+			}
 		}
 
 		// Remove duplicate entries

+ 3 - 0
Source/Core/DataView.h

@@ -82,6 +82,9 @@ public:
 	// Returns the list of data variable name(s) which can modify this view.
 	virtual StringList GetVariableNameList() const = 0;
 
+	// Returns true if the variable at the given data address can modify this view.
+	virtual bool HasAddressDependency(const DataAddress& address) const = 0;
+
 	// Returns the attached element if it still exists.
 	Element* GetElement() const;
 

+ 39 - 1
Source/Core/DataViewDefault.cpp

@@ -69,6 +69,12 @@ StringList DataViewCommon::GetVariableNameList() const
 	return expression->GetVariableNameList();
 }
 
+bool DataViewCommon::HasAddressDependency(const DataAddress& address) const
+{
+	RMLUI_ASSERT(expression);
+	return expression->HasAddressDependency(address);
+}
+
 const String& DataViewCommon::GetModifier() const
 {
 	return modifier;
@@ -423,6 +429,17 @@ StringList DataViewText::GetVariableNameList() const
 	return full_list;
 }
 
+bool DataViewText::HasAddressDependency(const DataAddress& address) const
+{
+	for (const DataEntry& entry : data_entries)
+	{
+		RMLUI_ASSERT(entry.data_expression);
+		if (entry.data_expression->HasAddressDependency(address))
+			return true;
+	}
+	return false;
+}
+
 void DataViewText::Release()
 {
 	delete this;
@@ -503,7 +520,8 @@ bool DataViewFor::Initialize(DataModel& model, Element* element, const String& i
 
 	element->SetProperty(PropertyId::Display, Property(Style::Display::None));
 
-	// Copy over the attributes, but remove the 'data-for' which would otherwise recreate the data-for loop on all constructed children recursively.
+	// Copy over the attributes, but remove the 'data-for' which would otherwise recreate the data-for loop on all constructed children
+	// recursively.
 	attributes = element->GetAttributes();
 	for (auto it = attributes.begin(); it != attributes.end(); ++it)
 	{
@@ -571,6 +589,20 @@ StringList DataViewFor::GetVariableNameList() const
 	return StringList{container_address.front().name};
 }
 
+bool DataViewFor::HasAddressDependency(const DataAddress& address) const
+{
+	if (address.empty())
+		return false;
+
+	for (size_t i = 0; i < Math::Min(address.size(), container_address.size()); i++)
+	{
+		if (container_address[i] != address[i])
+			return false;
+	}
+
+	return true;
+}
+
 void DataViewFor::Release()
 {
 	delete this;
@@ -599,6 +631,12 @@ bool DataViewAlias::Initialize(DataModel& model, Element* element, const String&
 	return true;
 }
 
+bool DataViewAlias::HasAddressDependency(const DataAddress& /*address*/) const
+{
+	// TODO
+	return true;
+}
+
 void DataViewAlias::Release()
 {
 	delete this;

+ 8 - 0
Source/Core/DataViewDefault.h

@@ -48,6 +48,8 @@ public:
 
 	StringList GetVariableNameList() const override;
 
+	bool HasAddressDependency(const DataAddress& address) const override;
+
 protected:
 	const String& GetModifier() const;
 	DataExpression& GetExpression();
@@ -134,6 +136,8 @@ public:
 	bool Update(DataModel& model) override;
 	StringList GetVariableNameList() const override;
 
+	bool HasAddressDependency(const DataAddress& address) const override;
+
 protected:
 	void Release() override;
 
@@ -160,6 +164,8 @@ public:
 
 	StringList GetVariableNameList() const override;
 
+	bool HasAddressDependency(const DataAddress& address) const override;
+
 protected:
 	void Release() override;
 
@@ -180,6 +186,8 @@ public:
 	bool Update(DataModel& model) override;
 	bool Initialize(DataModel& model, Element* element, const String& expression, const String& modifier) override;
 
+	bool HasAddressDependency(const DataAddress& address) const override;
+
 protected:
 	void Release() override;