diff --git a/Include/RmlUi/Core/DataTypes.h b/Include/RmlUi/Core/DataTypes.h index 1cd2c8185..c7e052636 100644 --- a/Include/RmlUi/Core/DataTypes.h +++ b/Include/RmlUi/Core/DataTypes.h @@ -66,6 +66,8 @@ using DirtyVariables = SmallUnorderedSet; 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; }; diff --git a/Samples/basic/player_list/src/main.cpp b/Samples/basic/player_list/src/main.cpp index c7c876559..72069bf6c 100644 --- a/Samples/basic/player_list/src/main.cpp +++ b/Samples/basic/player_list/src/main.cpp @@ -111,17 +111,23 @@ class PlayerList final : public Rml::EventListener { 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; } diff --git a/Source/Core/DataExpression.cpp b/Source/Core/DataExpression.cpp index 272a4777f..37efca0bf 100644 --- a/Source/Core/DataExpression.cpp +++ b/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) {} diff --git a/Source/Core/DataExpression.h b/Source/Core/DataExpression.h index 0c4b4b386..1ed81aed4 100644 --- a/Source/Core/DataExpression.h +++ b/Source/Core/DataExpression.h @@ -69,6 +69,8 @@ class DataExpression { // Available after Parse() StringList GetVariableNameList() const; + // Available after Parse() + bool HasAddressDependency(const DataAddress& match_address) const; private: String expression; diff --git a/Source/Core/DataModel.cpp b/Source/Core/DataModel.cpp index 1a6f33b92..2f3a8c729 100644 --- a/Source/Core/DataModel.cpp +++ b/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); } diff --git a/Source/Core/DataView.cpp b/Source/Core/DataView.cpp index 59850d1a1..0293b7d67 100644 --- a/Source/Core/DataView.cpp +++ b/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 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 diff --git a/Source/Core/DataView.h b/Source/Core/DataView.h index 23d6d8fe1..a144a8c90 100644 --- a/Source/Core/DataView.h +++ b/Source/Core/DataView.h @@ -82,6 +82,9 @@ class DataView : public Releasable { // 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; diff --git a/Source/Core/DataViewDefault.cpp b/Source/Core/DataViewDefault.cpp index 4c67271fb..6299e7c51 100644 --- a/Source/Core/DataViewDefault.cpp +++ b/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; diff --git a/Source/Core/DataViewDefault.h b/Source/Core/DataViewDefault.h index a36f42e7c..1ef90b209 100644 --- a/Source/Core/DataViewDefault.h +++ b/Source/Core/DataViewDefault.h @@ -48,6 +48,8 @@ class DataViewCommon : public DataView { StringList GetVariableNameList() const override; + bool HasAddressDependency(const DataAddress& address) const override; + protected: const String& GetModifier() const; DataExpression& GetExpression(); @@ -134,6 +136,8 @@ class DataViewText final : public DataView { bool Update(DataModel& model) override; StringList GetVariableNameList() const override; + bool HasAddressDependency(const DataAddress& address) const override; + protected: void Release() override; @@ -160,6 +164,8 @@ class DataViewFor final : public DataView { StringList GetVariableNameList() const override; + bool HasAddressDependency(const DataAddress& address) const override; + protected: void Release() override; @@ -180,6 +186,8 @@ class DataViewAlias final : public DataView { 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;