From 672ee78a9073f9f507885571313f183d69e2d9b3 Mon Sep 17 00:00:00 2001 From: James Benton Date: Mon, 4 Dec 2023 11:36:11 +0000 Subject: [PATCH] Allow dynamic array indexing in a data address. --- Include/RmlUi/Core/DataTypes.h | 8 +++++--- Include/RmlUi/Core/DataVariable.h | 17 +++++++++------- Source/Core/DataExpression.cpp | 17 ++++++++++++++-- Source/Core/DataModel.cpp | 29 ++++++++++++++++++-------- Source/Core/DataVariable.cpp | 34 +++++++++++++++++++++++++------ Source/Lua/LuaDataModel.cpp | 11 +++++----- 6 files changed, 84 insertions(+), 32 deletions(-) diff --git a/Include/RmlUi/Core/DataTypes.h b/Include/RmlUi/Core/DataTypes.h index 1cd2c8185..b6bbc4d12 100644 --- a/Include/RmlUi/Core/DataTypes.h +++ b/Include/RmlUi/Core/DataTypes.h @@ -62,14 +62,16 @@ template using MemberSetterFunc = void (Object::*)(AssignType); using DirtyVariables = SmallUnorderedSet; +using DataAddress = Vector; struct DataAddressEntry { - DataAddressEntry(String name) : name(std::move(name)), index(-1) {} - DataAddressEntry(int index) : index(index) {} + DataAddressEntry(String name_) : name(std::move(name_)), index(-1) {} + DataAddressEntry(DataAddress address_) : address(std::move(address_)), index(-1) {} + DataAddressEntry(int index_) : index(index_) {} String name; + DataAddress address; int index; }; -using DataAddress = Vector; template struct PointerTraits { diff --git a/Include/RmlUi/Core/DataVariable.h b/Include/RmlUi/Core/DataVariable.h index cede5715c..48f426570 100644 --- a/Include/RmlUi/Core/DataVariable.h +++ b/Include/RmlUi/Core/DataVariable.h @@ -38,6 +38,8 @@ namespace Rml { +class DataModel; + enum class DataVariableType { Scalar, Array, Struct }; /* @@ -56,7 +58,7 @@ class RMLUICORE_API DataVariable { bool Get(Variant& variant); bool Set(const Variant& variant); int Size(); - DataVariable Child(const DataAddressEntry& address); + DataVariable Child(const DataModel& model, const DataAddressEntry& address); DataVariableType Type(); private: @@ -79,11 +81,13 @@ class RMLUICORE_API VariableDefinition : public NonCopyMoveable { virtual bool Set(void* ptr, const Variant& variant); virtual int Size(void* ptr); - virtual DataVariable Child(void* ptr, const DataAddressEntry& address); + virtual DataVariable Child(const DataModel& model, void* ptr, const DataAddressEntry& address); protected: VariableDefinition(DataVariableType type) : type(type) {} + static int ResolveArrayIndex(const DataModel& model, const DataAddressEntry &address); + private: DataVariableType type; }; @@ -145,7 +149,7 @@ class RMLUICORE_API StructDefinition final : public VariableDefinition { public: StructDefinition(); - DataVariable Child(void* ptr, const DataAddressEntry& address) override; + DataVariable Child(const DataModel& model, void* ptr, const DataAddressEntry& address) override; void AddMember(const String& name, UniquePtr member); @@ -163,11 +167,10 @@ class ArrayDefinition final : public VariableDefinition { int Size(void* ptr) override { return int(static_cast(ptr)->size()); } protected: - DataVariable Child(void* void_ptr, const DataAddressEntry& address) override + DataVariable Child(const DataModel& model, void* void_ptr, const DataAddressEntry& address) override { Container* ptr = static_cast(void_ptr); - const int index = address.index; - + const int index = VariableDefinition::ResolveArrayIndex(model, address); const int container_size = int(ptr->size()); if (index < 0 || index >= container_size) { @@ -196,7 +199,7 @@ class RMLUICORE_API BasePointerDefinition : public VariableDefinition { bool Get(void* ptr, Variant& variant) override; bool Set(void* ptr, const Variant& variant) override; int Size(void* ptr) override; - DataVariable Child(void* ptr, const DataAddressEntry& address) override; + DataVariable Child(const DataModel& model, void* ptr, const DataAddressEntry& address) override; protected: virtual void* DereferencePointer(void* ptr) = 0; diff --git a/Source/Core/DataExpression.cpp b/Source/Core/DataExpression.cpp index 3609e7656..030d40ab7 100644 --- a/Source/Core/DataExpression.cpp +++ b/Source/Core/DataExpression.cpp @@ -1021,15 +1021,28 @@ bool DataExpression::Run(const DataExpressionInterface& expression_interface, Va return true; } +static void CollectAddresses(StringList& list, const DataAddress& address) +{ + if (!address.empty()) + list.push_back(address[0].name); + + for (const DataAddressEntry& entry : address) + { + if (!entry.address.empty()) + CollectAddresses(list, entry.address); + } +} + StringList DataExpression::GetVariableNameList() const { StringList list; list.reserve(addresses.size()); + for (const DataAddress& address : addresses) { - if (!address.empty()) - list.push_back(address[0].name); + CollectAddresses(list, address); } + return list; } diff --git a/Source/Core/DataModel.cpp b/Source/Core/DataModel.cpp index 1a6f33b92..1d1a72e6a 100644 --- a/Source/Core/DataModel.cpp +++ b/Source/Core/DataModel.cpp @@ -37,7 +37,7 @@ namespace Rml { static DataAddress ParseAddress(const String& address_str) { StringList list; - StringUtilities::ExpandString(list, address_str, '.'); + StringUtilities::ExpandString(list, address_str, '.', '[', ']'); DataAddress address; address.reserve(list.size() * 2); @@ -55,15 +55,26 @@ static DataAddress ParseAddress(const String& address_str) while (i_open != String::npos) { - size_t i_close = item.find(']', i_open + 1); - if (i_close == String::npos) - return DataAddress(); + int depth = 1; + size_t i_close = i_open; + while (depth > 0) + { + i_close = item.find_first_of("[]", i_close + 1); + if (i_close == String::npos) + return DataAddress(); - int index = FromString(item.substr(i_open + 1, i_close - i_open), -1); - if (index < 0) - return DataAddress(); + if (item[i_close] == '[') + depth++; + else if (item[i_close] == ']') + depth--; + } - address.emplace_back(index); + std::string accessor = item.substr(i_open + 1, i_close - (i_open + 1)); + int index = FromString(accessor, -1); + if (index >= 0) + address.emplace_back(index); + else + address.emplace_back(ParseAddress(accessor)); i_open = item.find('[', i_close + 1); } @@ -308,7 +319,7 @@ DataVariable DataModel::GetVariable(const DataAddress& address) const for (int i = 1; i < (int)address.size() && variable; i++) { - variable = variable.Child(address[i]); + variable = variable.Child(*this, address[i]); if (!variable) return DataVariable(); } diff --git a/Source/Core/DataVariable.cpp b/Source/Core/DataVariable.cpp index 4da3a9168..b7759d425 100644 --- a/Source/Core/DataVariable.cpp +++ b/Source/Core/DataVariable.cpp @@ -27,6 +27,7 @@ */ #include "../../Include/RmlUi/Core/DataVariable.h" +#include "DataModel.h" namespace Rml { @@ -45,9 +46,9 @@ int DataVariable::Size() return definition->Size(ptr); } -DataVariable DataVariable::Child(const DataAddressEntry& address) +DataVariable DataVariable::Child(const DataModel& model, const DataAddressEntry& address) { - return definition->Child(ptr, address); + return definition->Child(model, ptr, address); } DataVariableType DataVariable::Type() @@ -70,12 +71,33 @@ int VariableDefinition::Size(void* /*ptr*/) Log::Message(Log::LT_WARNING, "Tried to get the size from a non-array data type."); return 0; } -DataVariable VariableDefinition::Child(void* /*ptr*/, const DataAddressEntry& /*address*/) +DataVariable VariableDefinition::Child(const DataModel& /*model*/ , void* /*ptr*/, const DataAddressEntry& /*address*/) { Log::Message(Log::LT_WARNING, "Tried to get the child of a scalar type."); return DataVariable(); } +int VariableDefinition::ResolveArrayIndex(const DataModel& model, const DataAddressEntry &address) +{ + if (address.index >= 0) + return address.index; + + if (!address.address.empty()) + { + Rml::DataVariable variable_index = model.GetVariable(address.address); + if (variable_index) + { + Rml::Variant value; + if (variable_index.Get(value)) + { + return value.Get(-1); + } + } + } + + return -1; +} + class LiteralIntDefinition final : public VariableDefinition { public: LiteralIntDefinition() : VariableDefinition(DataVariableType::Scalar) {} @@ -95,7 +117,7 @@ DataVariable MakeLiteralIntVariable(int value) StructDefinition::StructDefinition() : VariableDefinition(DataVariableType::Struct) {} -DataVariable StructDefinition::Child(void* ptr, const DataAddressEntry& address) +DataVariable StructDefinition::Child(const DataModel& /*model*/, void *ptr, const DataAddressEntry &address) { const String& name = address.name; if (name.empty()) @@ -169,11 +191,11 @@ int BasePointerDefinition::Size(void* ptr) return underlying_definition->Size(DereferencePointer(ptr)); } -DataVariable BasePointerDefinition::Child(void* ptr, const DataAddressEntry& address) +DataVariable BasePointerDefinition::Child(const DataModel& model, void* ptr, const DataAddressEntry& address) { if (!ptr) return DataVariable(); - return underlying_definition->Child(DereferencePointer(ptr), address); + return underlying_definition->Child(model, DereferencePointer(ptr), address); } } // namespace Rml diff --git a/Source/Lua/LuaDataModel.cpp b/Source/Lua/LuaDataModel.cpp index 30379f75d..9acfc5027 100644 --- a/Source/Lua/LuaDataModel.cpp +++ b/Source/Lua/LuaDataModel.cpp @@ -31,6 +31,7 @@ #include #include #include +#include "../Core/DataModel.h" #define RMLDATAMODEL "RMLDATAMODEL" @@ -133,7 +134,7 @@ class LuaTableDef : public VariableDefinition { bool Get(void* ptr, Variant& variant) override; bool Set(void* ptr, const Variant& variant) override; int Size(void* ptr) override; - DataVariable Child(void* ptr, const DataAddressEntry& address) override; + DataVariable Child(const DataModel& data_model, void* ptr, const DataAddressEntry& address) override; protected: const struct LuaDataModel* model; @@ -142,7 +143,7 @@ class LuaTableDef : public VariableDefinition { class LuaScalarDef final : public LuaTableDef { public: LuaScalarDef(const struct LuaDataModel* model); - DataVariable Child(void* ptr, const DataAddressEntry& address) override; + DataVariable Child(const DataModel& data_model, void* ptr, const DataAddressEntry& address) override; }; LuaTableDef::LuaTableDef(const struct LuaDataModel* model) : VariableDefinition(DataVariableType::Scalar), model(model) {} @@ -206,7 +207,7 @@ int LuaTableDef::Size(void* ptr) return size; } -DataVariable LuaTableDef::Child(void* ptr, const DataAddressEntry& address) +DataVariable LuaTableDef::Child(const DataModel& /*data_model*/, void* ptr, const DataAddressEntry& address) { lua_State* L = model->dataL; if (!L) @@ -240,13 +241,13 @@ DataVariable LuaTableDef::Child(void* ptr, const DataAddressEntry& address) LuaScalarDef::LuaScalarDef(const struct LuaDataModel* model) : LuaTableDef(model) {} -DataVariable LuaScalarDef::Child(void* ptr, const DataAddressEntry& address) +DataVariable LuaScalarDef::Child(const DataModel& data_model, void* ptr, const DataAddressEntry& address) { lua_State* L = model->dataL; if (!L) return DataVariable{}; lua_settop(L, model->top); - return LuaTableDef::Child(ptr, address); + return LuaTableDef::Child(data_model, ptr, address); } static void BindVariable(struct LuaDataModel* D, lua_State* L)