1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

DynamicObject: Add virtual functions that are called to indicate a property change

This commit is contained in:
reuk 2024-10-15 14:10:58 +01:00
parent 14f663bc0e
commit 67396435e5
No known key found for this signature in database
GPG key ID: FCB43929F012EE5C
2 changed files with 144 additions and 17 deletions

View file

@ -35,19 +35,13 @@
namespace juce
{
DynamicObject::DynamicObject()
{
}
DynamicObject::DynamicObject() = default;
DynamicObject::DynamicObject (const DynamicObject& other)
: ReferenceCountedObject(), properties (other.properties)
{
}
DynamicObject::~DynamicObject()
{
}
bool DynamicObject::hasProperty (const Identifier& propertyName) const
{
const var* const v = properties.getVarPointer (propertyName);
@ -62,11 +56,13 @@ const var& DynamicObject::getProperty (const Identifier& propertyName) const
void DynamicObject::setProperty (const Identifier& propertyName, const var& newValue)
{
properties.set (propertyName, newValue);
didModifyProperty (propertyName, newValue);
}
void DynamicObject::removeProperty (const Identifier& propertyName)
{
properties.remove (propertyName);
didModifyProperty (propertyName, std::nullopt);
}
bool DynamicObject::hasMethod (const Identifier& methodName) const
@ -84,12 +80,16 @@ var DynamicObject::invokeMethod (Identifier method, const var::NativeFunctionArg
void DynamicObject::setMethod (Identifier name, var::NativeFunction function)
{
properties.set (name, var (function));
setProperty (name, var (function));
}
void DynamicObject::clear()
{
auto copy = properties;
properties.clear();
for (auto& prop : copy)
didModifyProperty (prop.name, std::nullopt);
}
void DynamicObject::cloneAllProperties()
@ -151,4 +151,128 @@ void DynamicObject::writeAsJSON (OutputStream& out, const JSON::FormatOptions& f
out << '}';
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
class DynamicObjectTests : public UnitTest
{
public:
DynamicObjectTests() : UnitTest { "DynamicObject", UnitTestCategories::containers } {}
void runTest() override
{
struct Action
{
Identifier key;
std::optional<var> value;
bool operator== (const Action& other) const
{
return other.key == key && other.value == value;
}
};
using Actions = std::vector<Action>;
struct DerivedObject : public DynamicObject
{
explicit DerivedObject (Actions& a) : actions (a) {}
void didModifyProperty (const Identifier& key, const std::optional<var>& value) override
{
actions.push_back (Action { key, value });
}
Actions& actions;
};
Actions actions;
DerivedObject object { actions };
beginTest ("didModifyProperty is emitted on setProperty");
{
expect (object.getProperties().isEmpty());
const Identifier key = "foo";
const var value = 123;
object.setProperty (key, value);
expect (actions == Actions { Action { key, value } });
expect (object.getProperties() == NamedValueSet { { key, value } });
}
object.clear();
actions.clear();
beginTest ("didModifyProperty is emitted on setMethod");
{
expect (object.getProperties().isEmpty());
const Identifier key = "foo";
const var::NativeFunction value = [] (const var::NativeFunctionArgs&) { return var{}; };
object.setMethod (key, value);
expect (actions.size() == 1);
expect (actions.back().key == key);
expect (object.getProperties().size() == 1);
expect (object.hasMethod (key));
}
object.clear();
actions.clear();
beginTest ("didModifyProperty is emitted on removeProperty");
{
expect (object.getProperties().isEmpty());
const Identifier key = "bar";
object.removeProperty (key);
expect (actions == Actions { Action { key, std::nullopt } });
expect (object.getProperties().isEmpty());
}
object.clear();
actions.clear();
beginTest ("didModifyProperty is emitted on clear");
{
expect (object.getProperties().isEmpty());
object.clear();
expect (actions.empty());
const Identifier keys[] { "foo", "bar", "baz" };
for (auto [index, key] : enumerate (keys, int{}))
object.setProperty (key, index);
for (auto& key : keys)
expect (object.hasProperty (key));
actions.clear();
object.clear();
expect (actions.size() == std::size (keys));
for (auto& key : keys)
{
expect (std::find_if (actions.begin(), actions.end(), [&key] (auto& action)
{
return action.key == key && action.value == std::nullopt;
}) != actions.end());
}
}
}
};
static DynamicObjectTests dynamicObjectTests;
#endif
} // namespace juce

View file

@ -54,7 +54,6 @@ public:
//==============================================================================
DynamicObject();
DynamicObject (const DynamicObject&);
~DynamicObject() override;
using Ptr = ReferenceCountedObjectPtr<DynamicObject>;
@ -76,11 +75,8 @@ public:
void removeProperty (const Identifier& propertyName);
//==============================================================================
/** Checks whether this object has the specified method.
The default implementation of this just checks whether there's a property
with this name that's actually a method, but this can be overridden for
building objects with dynamic invocation.
/** Checks whether this object has a property with the given name that has a
value of type NativeFunction.
*/
bool hasMethod (const Identifier& methodName) const;
@ -88,9 +84,6 @@ public:
The default implementation looks up the named property, and if it's a method
call, then it invokes it.
This method is virtual to allow more dynamic invocation to used for objects
where the methods may not already be set as properties.
*/
var invokeMethod (Identifier methodName,
const var::NativeFunctionArgs& args);
@ -133,6 +126,16 @@ public:
virtual void writeAsJSON (OutputStream&, const JSON::FormatOptions&);
private:
/** Derived classes may override this function to take additional actions after
properties are assigned or removed.
@param name the name of the property that changed
@param value if non-null, the value of the property after assignment
if null, indicates that the property was removed
*/
virtual void didModifyProperty ([[maybe_unused]] const Identifier& name,
[[maybe_unused]] const std::optional<var>& value) {}
//==============================================================================
NamedValueSet properties;