migrating to the latest JUCE version
This commit is contained in:
@ -1,155 +1,155 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class CachedValueTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
CachedValueTests()
|
||||
: UnitTest ("CachedValues", UnitTestCategories::values)
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("default constructor");
|
||||
{
|
||||
CachedValue<String> cv;
|
||||
expect (cv.isUsingDefault());
|
||||
expect (cv.get() == String());
|
||||
}
|
||||
|
||||
beginTest ("without default value");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testkey", "testvalue", nullptr);
|
||||
|
||||
CachedValue<String> cv (t, "testkey", nullptr);
|
||||
|
||||
expect (! cv.isUsingDefault());
|
||||
expect (cv.get() == "testvalue");
|
||||
|
||||
cv.resetToDefault();
|
||||
|
||||
expect (cv.isUsingDefault());
|
||||
expect (cv.get() == String());
|
||||
}
|
||||
|
||||
beginTest ("with default value");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testkey", "testvalue", nullptr);
|
||||
|
||||
CachedValue<String> cv (t, "testkey", nullptr, "defaultvalue");
|
||||
|
||||
expect (! cv.isUsingDefault());
|
||||
expect (cv.get() == "testvalue");
|
||||
|
||||
cv.resetToDefault();
|
||||
|
||||
expect (cv.isUsingDefault());
|
||||
expect (cv.get() == "defaultvalue");
|
||||
}
|
||||
|
||||
beginTest ("with default value (int)");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testkey", 23, nullptr);
|
||||
|
||||
CachedValue<int> cv (t, "testkey", nullptr, 34);
|
||||
|
||||
expect (! cv.isUsingDefault());
|
||||
expect (cv == 23);
|
||||
expectEquals (cv.get(), 23);
|
||||
|
||||
cv.resetToDefault();
|
||||
|
||||
expect (cv.isUsingDefault());
|
||||
expect (cv == 34);
|
||||
}
|
||||
|
||||
beginTest ("with void value");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testkey", var(), nullptr);
|
||||
|
||||
CachedValue<String> cv (t, "testkey", nullptr, "defaultvalue");
|
||||
|
||||
expect (! cv.isUsingDefault());
|
||||
expect (cv == "");
|
||||
expectEquals (cv.get(), String());
|
||||
}
|
||||
|
||||
beginTest ("with non-existent value");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
|
||||
CachedValue<String> cv (t, "testkey", nullptr, "defaultvalue");
|
||||
|
||||
expect (cv.isUsingDefault());
|
||||
expect (cv == "defaultvalue");
|
||||
expect (cv.get() == "defaultvalue");
|
||||
}
|
||||
|
||||
beginTest ("with value changing");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testkey", "oldvalue", nullptr);
|
||||
|
||||
CachedValue<String> cv (t, "testkey", nullptr, "defaultvalue");
|
||||
expect (cv == "oldvalue");
|
||||
|
||||
t.setProperty ("testkey", "newvalue", nullptr);
|
||||
expect (cv != "oldvalue");
|
||||
expect (cv == "newvalue");
|
||||
}
|
||||
|
||||
beginTest ("set value");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testkey", 23, nullptr);
|
||||
|
||||
CachedValue<int> cv (t, "testkey", nullptr, 45);
|
||||
cv = 34;
|
||||
|
||||
expectEquals ((int) t["testkey"], 34);
|
||||
|
||||
cv.resetToDefault();
|
||||
expect (cv == 45);
|
||||
expectEquals (cv.get(), 45);
|
||||
|
||||
expect (t["testkey"] == var());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static CachedValueTests cachedValueTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class CachedValueTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
CachedValueTests()
|
||||
: UnitTest ("CachedValues", UnitTestCategories::values)
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("default constructor");
|
||||
{
|
||||
CachedValue<String> cv;
|
||||
expect (cv.isUsingDefault());
|
||||
expect (cv.get() == String());
|
||||
}
|
||||
|
||||
beginTest ("without default value");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testkey", "testvalue", nullptr);
|
||||
|
||||
CachedValue<String> cv (t, "testkey", nullptr);
|
||||
|
||||
expect (! cv.isUsingDefault());
|
||||
expect (cv.get() == "testvalue");
|
||||
|
||||
cv.resetToDefault();
|
||||
|
||||
expect (cv.isUsingDefault());
|
||||
expect (cv.get() == String());
|
||||
}
|
||||
|
||||
beginTest ("with default value");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testkey", "testvalue", nullptr);
|
||||
|
||||
CachedValue<String> cv (t, "testkey", nullptr, "defaultvalue");
|
||||
|
||||
expect (! cv.isUsingDefault());
|
||||
expect (cv.get() == "testvalue");
|
||||
|
||||
cv.resetToDefault();
|
||||
|
||||
expect (cv.isUsingDefault());
|
||||
expect (cv.get() == "defaultvalue");
|
||||
}
|
||||
|
||||
beginTest ("with default value (int)");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testkey", 23, nullptr);
|
||||
|
||||
CachedValue<int> cv (t, "testkey", nullptr, 34);
|
||||
|
||||
expect (! cv.isUsingDefault());
|
||||
expect (cv == 23);
|
||||
expectEquals (cv.get(), 23);
|
||||
|
||||
cv.resetToDefault();
|
||||
|
||||
expect (cv.isUsingDefault());
|
||||
expect (cv == 34);
|
||||
}
|
||||
|
||||
beginTest ("with void value");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testkey", var(), nullptr);
|
||||
|
||||
CachedValue<String> cv (t, "testkey", nullptr, "defaultvalue");
|
||||
|
||||
expect (! cv.isUsingDefault());
|
||||
expect (cv == "");
|
||||
expectEquals (cv.get(), String());
|
||||
}
|
||||
|
||||
beginTest ("with non-existent value");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
|
||||
CachedValue<String> cv (t, "testkey", nullptr, "defaultvalue");
|
||||
|
||||
expect (cv.isUsingDefault());
|
||||
expect (cv == "defaultvalue");
|
||||
expect (cv.get() == "defaultvalue");
|
||||
}
|
||||
|
||||
beginTest ("with value changing");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testkey", "oldvalue", nullptr);
|
||||
|
||||
CachedValue<String> cv (t, "testkey", nullptr, "defaultvalue");
|
||||
expect (cv == "oldvalue");
|
||||
|
||||
t.setProperty ("testkey", "newvalue", nullptr);
|
||||
expect (cv != "oldvalue");
|
||||
expect (cv == "newvalue");
|
||||
}
|
||||
|
||||
beginTest ("set value");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testkey", 23, nullptr);
|
||||
|
||||
CachedValue<int> cv (t, "testkey", nullptr, 45);
|
||||
cv = 34;
|
||||
|
||||
expectEquals ((int) t["testkey"], 34);
|
||||
|
||||
cv.resetToDefault();
|
||||
expect (cv == 45);
|
||||
expectEquals (cv.get(), 45);
|
||||
|
||||
expect (t["testkey"] == var());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static CachedValueTests cachedValueTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,314 +1,314 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This class acts as a typed wrapper around a property inside a ValueTree.
|
||||
|
||||
A CachedValue provides an easy way to read and write a ValueTree property with
|
||||
a chosen type. So for example a CachedValue<int> allows you to read or write the
|
||||
property as an int, and a CachedValue<String> lets you work with it as a String.
|
||||
|
||||
It also allows efficient access to the value, by caching a copy of it in the
|
||||
type that is being used.
|
||||
|
||||
You can give the CachedValue an optional UndoManager which it will use when writing
|
||||
to the underlying ValueTree.
|
||||
|
||||
If the property inside the ValueTree is missing, the CachedValue will automatically
|
||||
return an optional default value, which can be specified when initialising the CachedValue.
|
||||
|
||||
To create one, you can either use the constructor to attach the CachedValue to a
|
||||
ValueTree, or can create an uninitialised CachedValue with its default constructor and
|
||||
then attach it later with the referTo() methods.
|
||||
|
||||
Common types like String, int, double which can be easily converted to a var should work
|
||||
out-of-the-box, but if you want to use more complex custom types, you may need to implement
|
||||
some template specialisations of VariantConverter which this class uses to convert between
|
||||
the type and the ValueTree's internal var.
|
||||
|
||||
@tags{DataStructures}
|
||||
*/
|
||||
template <typename Type>
|
||||
class CachedValue : private ValueTree::Listener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Default constructor.
|
||||
Creates a default CachedValue not referring to any property. To initialise the
|
||||
object, call one of the referTo() methods.
|
||||
*/
|
||||
CachedValue();
|
||||
|
||||
/** Constructor.
|
||||
|
||||
Creates a CachedValue referring to a Value property inside a ValueTree.
|
||||
If you use this constructor, the fallback value will be a default-constructed
|
||||
instance of Type.
|
||||
|
||||
@param tree The ValueTree containing the property
|
||||
@param propertyID The identifier of the property
|
||||
@param undoManager The UndoManager to use when writing to the property
|
||||
*/
|
||||
CachedValue (ValueTree& tree, const Identifier& propertyID,
|
||||
UndoManager* undoManager);
|
||||
|
||||
/** Constructor.
|
||||
|
||||
Creates a default Cached Value referring to a Value property inside a ValueTree,
|
||||
and specifies a fallback value to use if the property does not exist.
|
||||
|
||||
@param tree The ValueTree containing the property
|
||||
@param propertyID The identifier of the property
|
||||
@param undoManager The UndoManager to use when writing to the property
|
||||
@param defaultToUse The fallback default value to use.
|
||||
*/
|
||||
CachedValue (ValueTree& tree, const Identifier& propertyID,
|
||||
UndoManager* undoManager, const Type& defaultToUse);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current value of the property. If the property does not exist,
|
||||
returns the fallback default value.
|
||||
|
||||
This is the same as calling get().
|
||||
*/
|
||||
operator Type() const noexcept { return cachedValue; }
|
||||
|
||||
/** Returns the current value of the property. If the property does not exist,
|
||||
returns the fallback default value.
|
||||
*/
|
||||
Type get() const noexcept { return cachedValue; }
|
||||
|
||||
/** Dereference operator. Provides direct access to the property. */
|
||||
Type& operator*() noexcept { return cachedValue; }
|
||||
|
||||
/** Dereference operator. Provides direct access to members of the property
|
||||
if it is of object type.
|
||||
*/
|
||||
Type* operator->() noexcept { return &cachedValue; }
|
||||
|
||||
/** Returns true if the current value of the property (or the fallback value)
|
||||
is equal to other.
|
||||
*/
|
||||
template <typename OtherType>
|
||||
bool operator== (const OtherType& other) const { return cachedValue == other; }
|
||||
|
||||
/** Returns true if the current value of the property (or the fallback value)
|
||||
is not equal to other.
|
||||
*/
|
||||
template <typename OtherType>
|
||||
bool operator!= (const OtherType& other) const { return cachedValue != other; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current property as a Value object. */
|
||||
Value getPropertyAsValue();
|
||||
|
||||
/** Returns true if the current property does not exist and the CachedValue is using
|
||||
the fallback default value instead.
|
||||
*/
|
||||
bool isUsingDefault() const;
|
||||
|
||||
/** Returns the current fallback default value. */
|
||||
Type getDefault() const { return defaultValue; }
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the property. This will actually modify the property in the referenced ValueTree. */
|
||||
CachedValue& operator= (const Type& newValue);
|
||||
|
||||
/** Sets the property. This will actually modify the property in the referenced ValueTree. */
|
||||
void setValue (const Type& newValue, UndoManager* undoManagerToUse);
|
||||
|
||||
/** Removes the property from the referenced ValueTree and makes the CachedValue
|
||||
return the fallback default value instead.
|
||||
*/
|
||||
void resetToDefault();
|
||||
|
||||
/** Removes the property from the referenced ValueTree and makes the CachedValue
|
||||
return the fallback default value instead.
|
||||
*/
|
||||
void resetToDefault (UndoManager* undoManagerToUse);
|
||||
|
||||
/** Resets the fallback default value. */
|
||||
void setDefault (const Type& value) { defaultValue = value; }
|
||||
|
||||
//==============================================================================
|
||||
/** Makes the CachedValue refer to the specified property inside the given ValueTree. */
|
||||
void referTo (ValueTree& tree, const Identifier& property, UndoManager* um);
|
||||
|
||||
/** Makes the CachedValue refer to the specified property inside the given ValueTree,
|
||||
and specifies a fallback value to use if the property does not exist.
|
||||
*/
|
||||
void referTo (ValueTree& tree, const Identifier& property, UndoManager* um, const Type& defaultVal);
|
||||
|
||||
/** Force an update in case the referenced property has been changed from elsewhere.
|
||||
|
||||
Note: The CachedValue is a ValueTree::Listener and therefore will be informed of
|
||||
changes of the referenced property anyway (and update itself). But this may happen
|
||||
asynchronously. forceUpdateOfCachedValue() forces an update immediately.
|
||||
*/
|
||||
void forceUpdateOfCachedValue();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a reference to the ValueTree containing the referenced property. */
|
||||
ValueTree& getValueTree() noexcept { return targetTree; }
|
||||
|
||||
/** Returns the property ID of the referenced property. */
|
||||
const Identifier& getPropertyID() const noexcept { return targetProperty; }
|
||||
|
||||
/** Returns the UndoManager that is being used. */
|
||||
UndoManager* getUndoManager() noexcept { return undoManager; }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ValueTree targetTree;
|
||||
Identifier targetProperty;
|
||||
UndoManager* undoManager = nullptr;
|
||||
Type defaultValue;
|
||||
Type cachedValue;
|
||||
|
||||
//==============================================================================
|
||||
void referToWithDefault (ValueTree&, const Identifier&, UndoManager*, const Type&);
|
||||
Type getTypedValue() const;
|
||||
|
||||
void valueTreePropertyChanged (ValueTree& changedTree, const Identifier& changedProperty) override;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (CachedValue)
|
||||
JUCE_DECLARE_NON_COPYABLE (CachedValue)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
template <typename Type>
|
||||
inline CachedValue<Type>::CachedValue() = default;
|
||||
|
||||
template <typename Type>
|
||||
inline CachedValue<Type>::CachedValue (ValueTree& v, const Identifier& i, UndoManager* um)
|
||||
: targetTree (v), targetProperty (i), undoManager (um),
|
||||
defaultValue(), cachedValue (getTypedValue())
|
||||
{
|
||||
targetTree.addListener (this);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline CachedValue<Type>::CachedValue (ValueTree& v, const Identifier& i, UndoManager* um, const Type& defaultToUse)
|
||||
: targetTree (v), targetProperty (i), undoManager (um),
|
||||
defaultValue (defaultToUse), cachedValue (getTypedValue())
|
||||
{
|
||||
targetTree.addListener (this);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline Value CachedValue<Type>::getPropertyAsValue()
|
||||
{
|
||||
return targetTree.getPropertyAsValue (targetProperty, undoManager);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline bool CachedValue<Type>::isUsingDefault() const
|
||||
{
|
||||
return ! targetTree.hasProperty (targetProperty);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline CachedValue<Type>& CachedValue<Type>::operator= (const Type& newValue)
|
||||
{
|
||||
setValue (newValue, undoManager);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::setValue (const Type& newValue, UndoManager* undoManagerToUse)
|
||||
{
|
||||
if (cachedValue != newValue || isUsingDefault())
|
||||
{
|
||||
cachedValue = newValue;
|
||||
targetTree.setProperty (targetProperty, VariantConverter<Type>::toVar (newValue), undoManagerToUse);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::resetToDefault()
|
||||
{
|
||||
resetToDefault (undoManager);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::resetToDefault (UndoManager* undoManagerToUse)
|
||||
{
|
||||
targetTree.removeProperty (targetProperty, undoManagerToUse);
|
||||
forceUpdateOfCachedValue();
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::referTo (ValueTree& v, const Identifier& i, UndoManager* um)
|
||||
{
|
||||
referToWithDefault (v, i, um, Type());
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::referTo (ValueTree& v, const Identifier& i, UndoManager* um, const Type& defaultVal)
|
||||
{
|
||||
referToWithDefault (v, i, um, defaultVal);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::forceUpdateOfCachedValue()
|
||||
{
|
||||
cachedValue = getTypedValue();
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::referToWithDefault (ValueTree& v, const Identifier& i, UndoManager* um, const Type& defaultVal)
|
||||
{
|
||||
targetTree.removeListener (this);
|
||||
targetTree = v;
|
||||
targetProperty = i;
|
||||
undoManager = um;
|
||||
defaultValue = defaultVal;
|
||||
cachedValue = getTypedValue();
|
||||
targetTree.addListener (this);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline Type CachedValue<Type>::getTypedValue() const
|
||||
{
|
||||
if (const var* property = targetTree.getPropertyPointer (targetProperty))
|
||||
return VariantConverter<Type>::fromVar (*property);
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::valueTreePropertyChanged (ValueTree& changedTree, const Identifier& changedProperty)
|
||||
{
|
||||
if (changedProperty == targetProperty && targetTree == changedTree)
|
||||
forceUpdateOfCachedValue();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This class acts as a typed wrapper around a property inside a ValueTree.
|
||||
|
||||
A CachedValue provides an easy way to read and write a ValueTree property with
|
||||
a chosen type. So for example a CachedValue<int> allows you to read or write the
|
||||
property as an int, and a CachedValue<String> lets you work with it as a String.
|
||||
|
||||
It also allows efficient access to the value, by caching a copy of it in the
|
||||
type that is being used.
|
||||
|
||||
You can give the CachedValue an optional UndoManager which it will use when writing
|
||||
to the underlying ValueTree.
|
||||
|
||||
If the property inside the ValueTree is missing, the CachedValue will automatically
|
||||
return an optional default value, which can be specified when initialising the CachedValue.
|
||||
|
||||
To create one, you can either use the constructor to attach the CachedValue to a
|
||||
ValueTree, or can create an uninitialised CachedValue with its default constructor and
|
||||
then attach it later with the referTo() methods.
|
||||
|
||||
Common types like String, int, double which can be easily converted to a var should work
|
||||
out-of-the-box, but if you want to use more complex custom types, you may need to implement
|
||||
some template specialisations of VariantConverter which this class uses to convert between
|
||||
the type and the ValueTree's internal var.
|
||||
|
||||
@tags{DataStructures}
|
||||
*/
|
||||
template <typename Type>
|
||||
class CachedValue : private ValueTree::Listener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Default constructor.
|
||||
Creates a default CachedValue not referring to any property. To initialise the
|
||||
object, call one of the referTo() methods.
|
||||
*/
|
||||
CachedValue();
|
||||
|
||||
/** Constructor.
|
||||
|
||||
Creates a CachedValue referring to a Value property inside a ValueTree.
|
||||
If you use this constructor, the fallback value will be a default-constructed
|
||||
instance of Type.
|
||||
|
||||
@param tree The ValueTree containing the property
|
||||
@param propertyID The identifier of the property
|
||||
@param undoManager The UndoManager to use when writing to the property
|
||||
*/
|
||||
CachedValue (ValueTree& tree, const Identifier& propertyID,
|
||||
UndoManager* undoManager);
|
||||
|
||||
/** Constructor.
|
||||
|
||||
Creates a default Cached Value referring to a Value property inside a ValueTree,
|
||||
and specifies a fallback value to use if the property does not exist.
|
||||
|
||||
@param tree The ValueTree containing the property
|
||||
@param propertyID The identifier of the property
|
||||
@param undoManager The UndoManager to use when writing to the property
|
||||
@param defaultToUse The fallback default value to use.
|
||||
*/
|
||||
CachedValue (ValueTree& tree, const Identifier& propertyID,
|
||||
UndoManager* undoManager, const Type& defaultToUse);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current value of the property. If the property does not exist,
|
||||
returns the fallback default value.
|
||||
|
||||
This is the same as calling get().
|
||||
*/
|
||||
operator Type() const noexcept { return cachedValue; }
|
||||
|
||||
/** Returns the current value of the property. If the property does not exist,
|
||||
returns the fallback default value.
|
||||
*/
|
||||
Type get() const noexcept { return cachedValue; }
|
||||
|
||||
/** Dereference operator. Provides direct access to the property. */
|
||||
Type& operator*() noexcept { return cachedValue; }
|
||||
|
||||
/** Dereference operator. Provides direct access to members of the property
|
||||
if it is of object type.
|
||||
*/
|
||||
Type* operator->() noexcept { return &cachedValue; }
|
||||
|
||||
/** Returns true if the current value of the property (or the fallback value)
|
||||
is equal to other.
|
||||
*/
|
||||
template <typename OtherType>
|
||||
bool operator== (const OtherType& other) const { return cachedValue == other; }
|
||||
|
||||
/** Returns true if the current value of the property (or the fallback value)
|
||||
is not equal to other.
|
||||
*/
|
||||
template <typename OtherType>
|
||||
bool operator!= (const OtherType& other) const { return cachedValue != other; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current property as a Value object. */
|
||||
Value getPropertyAsValue();
|
||||
|
||||
/** Returns true if the current property does not exist and the CachedValue is using
|
||||
the fallback default value instead.
|
||||
*/
|
||||
bool isUsingDefault() const;
|
||||
|
||||
/** Returns the current fallback default value. */
|
||||
Type getDefault() const { return defaultValue; }
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the property. This will actually modify the property in the referenced ValueTree. */
|
||||
CachedValue& operator= (const Type& newValue);
|
||||
|
||||
/** Sets the property. This will actually modify the property in the referenced ValueTree. */
|
||||
void setValue (const Type& newValue, UndoManager* undoManagerToUse);
|
||||
|
||||
/** Removes the property from the referenced ValueTree and makes the CachedValue
|
||||
return the fallback default value instead.
|
||||
*/
|
||||
void resetToDefault();
|
||||
|
||||
/** Removes the property from the referenced ValueTree and makes the CachedValue
|
||||
return the fallback default value instead.
|
||||
*/
|
||||
void resetToDefault (UndoManager* undoManagerToUse);
|
||||
|
||||
/** Resets the fallback default value. */
|
||||
void setDefault (const Type& value) { defaultValue = value; }
|
||||
|
||||
//==============================================================================
|
||||
/** Makes the CachedValue refer to the specified property inside the given ValueTree. */
|
||||
void referTo (ValueTree& tree, const Identifier& property, UndoManager* um);
|
||||
|
||||
/** Makes the CachedValue refer to the specified property inside the given ValueTree,
|
||||
and specifies a fallback value to use if the property does not exist.
|
||||
*/
|
||||
void referTo (ValueTree& tree, const Identifier& property, UndoManager* um, const Type& defaultVal);
|
||||
|
||||
/** Force an update in case the referenced property has been changed from elsewhere.
|
||||
|
||||
Note: The CachedValue is a ValueTree::Listener and therefore will be informed of
|
||||
changes of the referenced property anyway (and update itself). But this may happen
|
||||
asynchronously. forceUpdateOfCachedValue() forces an update immediately.
|
||||
*/
|
||||
void forceUpdateOfCachedValue();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a reference to the ValueTree containing the referenced property. */
|
||||
ValueTree& getValueTree() noexcept { return targetTree; }
|
||||
|
||||
/** Returns the property ID of the referenced property. */
|
||||
const Identifier& getPropertyID() const noexcept { return targetProperty; }
|
||||
|
||||
/** Returns the UndoManager that is being used. */
|
||||
UndoManager* getUndoManager() noexcept { return undoManager; }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ValueTree targetTree;
|
||||
Identifier targetProperty;
|
||||
UndoManager* undoManager = nullptr;
|
||||
Type defaultValue;
|
||||
Type cachedValue;
|
||||
|
||||
//==============================================================================
|
||||
void referToWithDefault (ValueTree&, const Identifier&, UndoManager*, const Type&);
|
||||
Type getTypedValue() const;
|
||||
|
||||
void valueTreePropertyChanged (ValueTree& changedTree, const Identifier& changedProperty) override;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (CachedValue)
|
||||
JUCE_DECLARE_NON_COPYABLE (CachedValue)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
template <typename Type>
|
||||
inline CachedValue<Type>::CachedValue() = default;
|
||||
|
||||
template <typename Type>
|
||||
inline CachedValue<Type>::CachedValue (ValueTree& v, const Identifier& i, UndoManager* um)
|
||||
: targetTree (v), targetProperty (i), undoManager (um),
|
||||
defaultValue(), cachedValue (getTypedValue())
|
||||
{
|
||||
targetTree.addListener (this);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline CachedValue<Type>::CachedValue (ValueTree& v, const Identifier& i, UndoManager* um, const Type& defaultToUse)
|
||||
: targetTree (v), targetProperty (i), undoManager (um),
|
||||
defaultValue (defaultToUse), cachedValue (getTypedValue())
|
||||
{
|
||||
targetTree.addListener (this);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline Value CachedValue<Type>::getPropertyAsValue()
|
||||
{
|
||||
return targetTree.getPropertyAsValue (targetProperty, undoManager);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline bool CachedValue<Type>::isUsingDefault() const
|
||||
{
|
||||
return ! targetTree.hasProperty (targetProperty);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline CachedValue<Type>& CachedValue<Type>::operator= (const Type& newValue)
|
||||
{
|
||||
setValue (newValue, undoManager);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::setValue (const Type& newValue, UndoManager* undoManagerToUse)
|
||||
{
|
||||
if (cachedValue != newValue || isUsingDefault())
|
||||
{
|
||||
cachedValue = newValue;
|
||||
targetTree.setProperty (targetProperty, VariantConverter<Type>::toVar (newValue), undoManagerToUse);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::resetToDefault()
|
||||
{
|
||||
resetToDefault (undoManager);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::resetToDefault (UndoManager* undoManagerToUse)
|
||||
{
|
||||
targetTree.removeProperty (targetProperty, undoManagerToUse);
|
||||
forceUpdateOfCachedValue();
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::referTo (ValueTree& v, const Identifier& i, UndoManager* um)
|
||||
{
|
||||
referToWithDefault (v, i, um, Type());
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::referTo (ValueTree& v, const Identifier& i, UndoManager* um, const Type& defaultVal)
|
||||
{
|
||||
referToWithDefault (v, i, um, defaultVal);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::forceUpdateOfCachedValue()
|
||||
{
|
||||
cachedValue = getTypedValue();
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::referToWithDefault (ValueTree& v, const Identifier& i, UndoManager* um, const Type& defaultVal)
|
||||
{
|
||||
targetTree.removeListener (this);
|
||||
targetTree = v;
|
||||
targetProperty = i;
|
||||
undoManager = um;
|
||||
defaultValue = defaultVal;
|
||||
cachedValue = getTypedValue();
|
||||
targetTree.addListener (this);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline Type CachedValue<Type>::getTypedValue() const
|
||||
{
|
||||
if (const var* property = targetTree.getPropertyPointer (targetProperty))
|
||||
return VariantConverter<Type>::fromVar (*property);
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::valueTreePropertyChanged (ValueTree& changedTree, const Identifier& changedProperty)
|
||||
{
|
||||
if (changedProperty == targetProperty && targetTree == changedTree)
|
||||
forceUpdateOfCachedValue();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,241 +1,241 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
Value::ValueSource::ValueSource()
|
||||
{
|
||||
}
|
||||
|
||||
Value::ValueSource::~ValueSource()
|
||||
{
|
||||
cancelPendingUpdate();
|
||||
}
|
||||
|
||||
void Value::ValueSource::handleAsyncUpdate()
|
||||
{
|
||||
sendChangeMessage (true);
|
||||
}
|
||||
|
||||
void Value::ValueSource::sendChangeMessage (const bool synchronous)
|
||||
{
|
||||
const int numListeners = valuesWithListeners.size();
|
||||
|
||||
if (numListeners > 0)
|
||||
{
|
||||
if (synchronous)
|
||||
{
|
||||
const ReferenceCountedObjectPtr<ValueSource> localRef (this);
|
||||
|
||||
cancelPendingUpdate();
|
||||
|
||||
for (int i = numListeners; --i >= 0;)
|
||||
if (Value* const v = valuesWithListeners[i])
|
||||
v->callListeners();
|
||||
}
|
||||
else
|
||||
{
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class SimpleValueSource : public Value::ValueSource
|
||||
{
|
||||
public:
|
||||
SimpleValueSource()
|
||||
{
|
||||
}
|
||||
|
||||
SimpleValueSource (const var& initialValue)
|
||||
: value (initialValue)
|
||||
{
|
||||
}
|
||||
|
||||
var getValue() const override
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
void setValue (const var& newValue) override
|
||||
{
|
||||
if (! newValue.equalsWithSameType (value))
|
||||
{
|
||||
value = newValue;
|
||||
sendChangeMessage (false);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
var value;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SimpleValueSource)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
Value::Value() : value (new SimpleValueSource())
|
||||
{
|
||||
}
|
||||
|
||||
Value::Value (ValueSource* const v) : value (v)
|
||||
{
|
||||
jassert (v != nullptr);
|
||||
}
|
||||
|
||||
Value::Value (const var& initialValue) : value (new SimpleValueSource (initialValue))
|
||||
{
|
||||
}
|
||||
|
||||
Value::Value (const Value& other) : value (other.value)
|
||||
{
|
||||
}
|
||||
|
||||
Value::Value (Value&& other) noexcept
|
||||
{
|
||||
// moving a Value with listeners will lose those listeners, which
|
||||
// probably isn't what you wanted to happen!
|
||||
jassert (other.listeners.size() == 0);
|
||||
|
||||
other.removeFromListenerList();
|
||||
value = std::move (other.value);
|
||||
}
|
||||
|
||||
Value& Value::operator= (Value&& other) noexcept
|
||||
{
|
||||
// moving a Value with listeners will lose those listeners, which
|
||||
// probably isn't what you wanted to happen!
|
||||
jassert (other.listeners.size() == 0);
|
||||
|
||||
other.removeFromListenerList();
|
||||
value = std::move (other.value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value::~Value()
|
||||
{
|
||||
removeFromListenerList();
|
||||
}
|
||||
|
||||
void Value::removeFromListenerList()
|
||||
{
|
||||
if (listeners.size() > 0 && value != nullptr) // may be nullptr after a move operation
|
||||
value->valuesWithListeners.removeValue (this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
var Value::getValue() const
|
||||
{
|
||||
return value->getValue();
|
||||
}
|
||||
|
||||
Value::operator var() const
|
||||
{
|
||||
return value->getValue();
|
||||
}
|
||||
|
||||
void Value::setValue (const var& newValue)
|
||||
{
|
||||
value->setValue (newValue);
|
||||
}
|
||||
|
||||
String Value::toString() const
|
||||
{
|
||||
return value->getValue().toString();
|
||||
}
|
||||
|
||||
Value& Value::operator= (const var& newValue)
|
||||
{
|
||||
value->setValue (newValue);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Value::referTo (const Value& valueToReferTo)
|
||||
{
|
||||
if (valueToReferTo.value != value)
|
||||
{
|
||||
if (listeners.size() > 0)
|
||||
{
|
||||
value->valuesWithListeners.removeValue (this);
|
||||
valueToReferTo.value->valuesWithListeners.add (this);
|
||||
}
|
||||
|
||||
value = valueToReferTo.value;
|
||||
callListeners();
|
||||
}
|
||||
}
|
||||
|
||||
bool Value::refersToSameSourceAs (const Value& other) const
|
||||
{
|
||||
return value == other.value;
|
||||
}
|
||||
|
||||
bool Value::operator== (const Value& other) const
|
||||
{
|
||||
return value == other.value || value->getValue() == other.getValue();
|
||||
}
|
||||
|
||||
bool Value::operator!= (const Value& other) const
|
||||
{
|
||||
return value != other.value && value->getValue() != other.getValue();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Value::addListener (Value::Listener* listener)
|
||||
{
|
||||
if (listener != nullptr)
|
||||
{
|
||||
if (listeners.size() == 0)
|
||||
value->valuesWithListeners.add (this);
|
||||
|
||||
listeners.add (listener);
|
||||
}
|
||||
}
|
||||
|
||||
void Value::removeListener (Value::Listener* listener)
|
||||
{
|
||||
listeners.remove (listener);
|
||||
|
||||
if (listeners.size() == 0)
|
||||
value->valuesWithListeners.removeValue (this);
|
||||
}
|
||||
|
||||
void Value::callListeners()
|
||||
{
|
||||
if (listeners.size() > 0)
|
||||
{
|
||||
Value v (*this); // (create a copy in case this gets deleted by a callback)
|
||||
listeners.call ([&] (Value::Listener& l) { l.valueChanged (v); });
|
||||
}
|
||||
}
|
||||
|
||||
OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const Value& value)
|
||||
{
|
||||
return stream << value.toString();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
Value::ValueSource::ValueSource()
|
||||
{
|
||||
}
|
||||
|
||||
Value::ValueSource::~ValueSource()
|
||||
{
|
||||
cancelPendingUpdate();
|
||||
}
|
||||
|
||||
void Value::ValueSource::handleAsyncUpdate()
|
||||
{
|
||||
sendChangeMessage (true);
|
||||
}
|
||||
|
||||
void Value::ValueSource::sendChangeMessage (const bool synchronous)
|
||||
{
|
||||
const int numListeners = valuesWithListeners.size();
|
||||
|
||||
if (numListeners > 0)
|
||||
{
|
||||
if (synchronous)
|
||||
{
|
||||
const ReferenceCountedObjectPtr<ValueSource> localRef (this);
|
||||
|
||||
cancelPendingUpdate();
|
||||
|
||||
for (int i = numListeners; --i >= 0;)
|
||||
if (Value* const v = valuesWithListeners[i])
|
||||
v->callListeners();
|
||||
}
|
||||
else
|
||||
{
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class SimpleValueSource : public Value::ValueSource
|
||||
{
|
||||
public:
|
||||
SimpleValueSource()
|
||||
{
|
||||
}
|
||||
|
||||
SimpleValueSource (const var& initialValue)
|
||||
: value (initialValue)
|
||||
{
|
||||
}
|
||||
|
||||
var getValue() const override
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
void setValue (const var& newValue) override
|
||||
{
|
||||
if (! newValue.equalsWithSameType (value))
|
||||
{
|
||||
value = newValue;
|
||||
sendChangeMessage (false);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
var value;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SimpleValueSource)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
Value::Value() : value (new SimpleValueSource())
|
||||
{
|
||||
}
|
||||
|
||||
Value::Value (ValueSource* const v) : value (v)
|
||||
{
|
||||
jassert (v != nullptr);
|
||||
}
|
||||
|
||||
Value::Value (const var& initialValue) : value (new SimpleValueSource (initialValue))
|
||||
{
|
||||
}
|
||||
|
||||
Value::Value (const Value& other) : value (other.value)
|
||||
{
|
||||
}
|
||||
|
||||
Value::Value (Value&& other) noexcept
|
||||
{
|
||||
// moving a Value with listeners will lose those listeners, which
|
||||
// probably isn't what you wanted to happen!
|
||||
jassert (other.listeners.size() == 0);
|
||||
|
||||
other.removeFromListenerList();
|
||||
value = std::move (other.value);
|
||||
}
|
||||
|
||||
Value& Value::operator= (Value&& other) noexcept
|
||||
{
|
||||
// moving a Value with listeners will lose those listeners, which
|
||||
// probably isn't what you wanted to happen!
|
||||
jassert (other.listeners.size() == 0);
|
||||
|
||||
other.removeFromListenerList();
|
||||
value = std::move (other.value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value::~Value()
|
||||
{
|
||||
removeFromListenerList();
|
||||
}
|
||||
|
||||
void Value::removeFromListenerList()
|
||||
{
|
||||
if (listeners.size() > 0 && value != nullptr) // may be nullptr after a move operation
|
||||
value->valuesWithListeners.removeValue (this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
var Value::getValue() const
|
||||
{
|
||||
return value->getValue();
|
||||
}
|
||||
|
||||
Value::operator var() const
|
||||
{
|
||||
return value->getValue();
|
||||
}
|
||||
|
||||
void Value::setValue (const var& newValue)
|
||||
{
|
||||
value->setValue (newValue);
|
||||
}
|
||||
|
||||
String Value::toString() const
|
||||
{
|
||||
return value->getValue().toString();
|
||||
}
|
||||
|
||||
Value& Value::operator= (const var& newValue)
|
||||
{
|
||||
value->setValue (newValue);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Value::referTo (const Value& valueToReferTo)
|
||||
{
|
||||
if (valueToReferTo.value != value)
|
||||
{
|
||||
if (listeners.size() > 0)
|
||||
{
|
||||
value->valuesWithListeners.removeValue (this);
|
||||
valueToReferTo.value->valuesWithListeners.add (this);
|
||||
}
|
||||
|
||||
value = valueToReferTo.value;
|
||||
callListeners();
|
||||
}
|
||||
}
|
||||
|
||||
bool Value::refersToSameSourceAs (const Value& other) const
|
||||
{
|
||||
return value == other.value;
|
||||
}
|
||||
|
||||
bool Value::operator== (const Value& other) const
|
||||
{
|
||||
return value == other.value || value->getValue() == other.getValue();
|
||||
}
|
||||
|
||||
bool Value::operator!= (const Value& other) const
|
||||
{
|
||||
return value != other.value && value->getValue() != other.getValue();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Value::addListener (Value::Listener* listener)
|
||||
{
|
||||
if (listener != nullptr)
|
||||
{
|
||||
if (listeners.size() == 0)
|
||||
value->valuesWithListeners.add (this);
|
||||
|
||||
listeners.add (listener);
|
||||
}
|
||||
}
|
||||
|
||||
void Value::removeListener (Value::Listener* listener)
|
||||
{
|
||||
listeners.remove (listener);
|
||||
|
||||
if (listeners.size() == 0)
|
||||
value->valuesWithListeners.removeValue (this);
|
||||
}
|
||||
|
||||
void Value::callListeners()
|
||||
{
|
||||
if (listeners.size() > 0)
|
||||
{
|
||||
Value v (*this); // (create a copy in case this gets deleted by a callback)
|
||||
listeners.call ([&] (Value::Listener& l) { l.valueChanged (v); });
|
||||
}
|
||||
}
|
||||
|
||||
OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const Value& value)
|
||||
{
|
||||
return stream << value.toString();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,242 +1,243 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Represents a shared variant value.
|
||||
|
||||
A Value object contains a reference to a var object, and can get and set its value.
|
||||
Listeners can be attached to be told when the value is changed.
|
||||
|
||||
The Value class is a wrapper around a shared, reference-counted underlying data
|
||||
object - this means that multiple Value objects can all refer to the same piece of
|
||||
data, allowing all of them to be notified when any of them changes it.
|
||||
|
||||
When you create a Value with its default constructor, it acts as a wrapper around a
|
||||
simple var object, but by creating a Value that refers to a custom subclass of ValueSource,
|
||||
you can map the Value onto any kind of underlying data.
|
||||
|
||||
Important note! The Value class is not thread-safe! If you're accessing one from
|
||||
multiple threads, then you'll need to use your own synchronisation around any code
|
||||
that accesses it.
|
||||
|
||||
@tags{DataStructures}
|
||||
*/
|
||||
class JUCE_API Value final
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty Value, containing a void var. */
|
||||
Value();
|
||||
|
||||
/** Creates a Value that refers to the same value as another one.
|
||||
|
||||
Note that this doesn't make a copy of the other value - both this and the other
|
||||
Value will share the same underlying value, so that when either one alters it, both
|
||||
will see it change.
|
||||
*/
|
||||
Value (const Value& other);
|
||||
|
||||
/** Creates a Value that is set to the specified value. */
|
||||
explicit Value (const var& initialValue);
|
||||
|
||||
/** Move constructor */
|
||||
Value (Value&&) noexcept;
|
||||
|
||||
/** Destructor. */
|
||||
~Value();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current value. */
|
||||
var getValue() const;
|
||||
|
||||
/** Returns the current value. */
|
||||
operator var() const;
|
||||
|
||||
/** Returns the value as a string.
|
||||
This is a shortcut for "myValue.getValue().toString()".
|
||||
*/
|
||||
String toString() const;
|
||||
|
||||
/** Sets the current value.
|
||||
|
||||
You can also use operator= to set the value.
|
||||
|
||||
If there are any listeners registered, they will be notified of the
|
||||
change asynchronously.
|
||||
*/
|
||||
void setValue (const var& newValue);
|
||||
|
||||
/** Sets the current value.
|
||||
|
||||
This is the same as calling setValue().
|
||||
|
||||
If there are any listeners registered, they will be notified of the
|
||||
change asynchronously.
|
||||
*/
|
||||
Value& operator= (const var& newValue);
|
||||
|
||||
/** Move assignment operator */
|
||||
Value& operator= (Value&&) noexcept;
|
||||
|
||||
/** Makes this object refer to the same underlying ValueSource as another one.
|
||||
|
||||
Once this object has been connected to another one, changing either one
|
||||
will update the other.
|
||||
|
||||
Existing listeners will still be registered after you call this method, and
|
||||
they'll continue to receive messages when the new value changes.
|
||||
*/
|
||||
void referTo (const Value& valueToReferTo);
|
||||
|
||||
/** Returns true if this value and the other one are references to the same value.
|
||||
*/
|
||||
bool refersToSameSourceAs (const Value& other) const;
|
||||
|
||||
/** Compares two values.
|
||||
This is a compare-by-value comparison, so is effectively the same as
|
||||
saying (this->getValue() == other.getValue()).
|
||||
*/
|
||||
bool operator== (const Value& other) const;
|
||||
|
||||
/** Compares two values.
|
||||
This is a compare-by-value comparison, so is effectively the same as
|
||||
saying (this->getValue() != other.getValue()).
|
||||
*/
|
||||
bool operator!= (const Value& other) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Receives callbacks when a Value object changes.
|
||||
@see Value::addListener
|
||||
*/
|
||||
class JUCE_API Listener
|
||||
{
|
||||
public:
|
||||
Listener() = default;
|
||||
virtual ~Listener() = default;
|
||||
|
||||
/** Called when a Value object is changed.
|
||||
|
||||
Note that the Value object passed as a parameter may not be exactly the same
|
||||
object that you registered the listener with - it might be a copy that refers
|
||||
to the same underlying ValueSource. To find out, you can call Value::refersToSameSourceAs().
|
||||
*/
|
||||
virtual void valueChanged (Value& value) = 0;
|
||||
};
|
||||
|
||||
/** Adds a listener to receive callbacks when the value changes.
|
||||
|
||||
The listener is added to this specific Value object, and not to the shared
|
||||
object that it refers to. When this object is deleted, all the listeners will
|
||||
be lost, even if other references to the same Value still exist. So when you're
|
||||
adding a listener, make sure that you add it to a Value instance that will last
|
||||
for as long as you need the listener. In general, you'd never want to add a listener
|
||||
to a local stack-based Value, but more likely to one that's a member variable.
|
||||
|
||||
@see removeListener
|
||||
*/
|
||||
void addListener (Listener* listener);
|
||||
|
||||
/** Removes a listener that was previously added with addListener(). */
|
||||
void removeListener (Listener* listener);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Used internally by the Value class as the base class for its shared value objects.
|
||||
|
||||
The Value class is essentially a reference-counted pointer to a shared instance
|
||||
of a ValueSource object. If you're feeling adventurous, you can create your own custom
|
||||
ValueSource classes to allow Value objects to represent your own custom data items.
|
||||
*/
|
||||
class JUCE_API ValueSource : public ReferenceCountedObject,
|
||||
private AsyncUpdater
|
||||
{
|
||||
public:
|
||||
ValueSource();
|
||||
~ValueSource() override;
|
||||
|
||||
/** Returns the current value of this object. */
|
||||
virtual var getValue() const = 0;
|
||||
|
||||
/** Changes the current value.
|
||||
This must also trigger a change message if the value actually changes.
|
||||
*/
|
||||
virtual void setValue (const var& newValue) = 0;
|
||||
|
||||
/** Delivers a change message to all the listeners that are registered with
|
||||
this value.
|
||||
|
||||
If dispatchSynchronously is true, the method will call all the listeners
|
||||
before returning; otherwise it'll dispatch a message and make the call later.
|
||||
*/
|
||||
void sendChangeMessage (bool dispatchSynchronously);
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
friend class Value;
|
||||
SortedSet<Value*> valuesWithListeners;
|
||||
|
||||
private:
|
||||
void handleAsyncUpdate() override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueSource)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a Value object that uses this valueSource object as its underlying data. */
|
||||
explicit Value (ValueSource* valueSource);
|
||||
|
||||
/** Returns the ValueSource that this value is referring to. */
|
||||
ValueSource& getValueSource() noexcept { return *value; }
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
friend class ValueSource;
|
||||
ReferenceCountedObjectPtr<ValueSource> value;
|
||||
ListenerList<Listener> listeners;
|
||||
|
||||
void callListeners();
|
||||
void removeFromListenerList();
|
||||
|
||||
// This is disallowed to avoid confusion about whether it should
|
||||
// do a by-value or by-reference copy.
|
||||
Value& operator= (const Value&) = delete;
|
||||
|
||||
// This declaration prevents accidental construction from an integer of 0,
|
||||
// which is possible in some compilers via an implicit cast to a pointer.
|
||||
explicit Value (void*) = delete;
|
||||
};
|
||||
|
||||
/** Writes a Value to an OutputStream as a UTF8 string. */
|
||||
OutputStream& JUCE_CALLTYPE operator<< (OutputStream&, const Value&);
|
||||
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Represents a shared variant value.
|
||||
|
||||
A Value object contains a reference to a var object, and can get and set its value.
|
||||
Listeners can be attached to be told when the value is changed.
|
||||
|
||||
The Value class is a wrapper around a shared, reference-counted underlying data
|
||||
object - this means that multiple Value objects can all refer to the same piece of
|
||||
data, allowing all of them to be notified when any of them changes it.
|
||||
|
||||
When you create a Value with its default constructor, it acts as a wrapper around a
|
||||
simple var object, but by creating a Value that refers to a custom subclass of ValueSource,
|
||||
you can map the Value onto any kind of underlying data.
|
||||
|
||||
Important note! The Value class is not thread-safe! If you're accessing one from
|
||||
multiple threads, then you'll need to use your own synchronisation around any code
|
||||
that accesses it.
|
||||
|
||||
@tags{DataStructures}
|
||||
*/
|
||||
class JUCE_API Value final
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty Value, containing a void var. */
|
||||
Value();
|
||||
|
||||
/** Creates a Value that refers to the same value as another one.
|
||||
|
||||
Note that this doesn't make a copy of the other value - both this and the other
|
||||
Value will share the same underlying value, so that when either one alters it, both
|
||||
will see it change.
|
||||
*/
|
||||
Value (const Value& other);
|
||||
|
||||
/** Creates a Value that is set to the specified value. */
|
||||
explicit Value (const var& initialValue);
|
||||
|
||||
/** Move constructor */
|
||||
Value (Value&&) noexcept;
|
||||
|
||||
/** Destructor. */
|
||||
~Value();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current value. */
|
||||
var getValue() const;
|
||||
|
||||
/** Returns the current value. */
|
||||
operator var() const;
|
||||
|
||||
/** Returns the value as a string.
|
||||
This is a shortcut for "myValue.getValue().toString()".
|
||||
*/
|
||||
String toString() const;
|
||||
|
||||
/** Sets the current value.
|
||||
|
||||
You can also use operator= to set the value.
|
||||
|
||||
If there are any listeners registered, they will be notified of the
|
||||
change asynchronously.
|
||||
*/
|
||||
void setValue (const var& newValue);
|
||||
|
||||
/** Sets the current value.
|
||||
|
||||
This is the same as calling setValue().
|
||||
|
||||
If there are any listeners registered, they will be notified of the
|
||||
change asynchronously.
|
||||
*/
|
||||
Value& operator= (const var& newValue);
|
||||
|
||||
/** Move assignment operator */
|
||||
Value& operator= (Value&&) noexcept;
|
||||
|
||||
/** Makes this object refer to the same underlying ValueSource as another one.
|
||||
|
||||
Once this object has been connected to another one, changing either one
|
||||
will update the other.
|
||||
|
||||
Existing listeners will still be registered after you call this method, and
|
||||
they'll continue to receive messages when the new value changes.
|
||||
*/
|
||||
void referTo (const Value& valueToReferTo);
|
||||
|
||||
/** Returns true if this object and the other one use the same underlying
|
||||
ValueSource object.
|
||||
*/
|
||||
bool refersToSameSourceAs (const Value& other) const;
|
||||
|
||||
/** Compares two values.
|
||||
This is a compare-by-value comparison, so is effectively the same as
|
||||
saying (this->getValue() == other.getValue()).
|
||||
*/
|
||||
bool operator== (const Value& other) const;
|
||||
|
||||
/** Compares two values.
|
||||
This is a compare-by-value comparison, so is effectively the same as
|
||||
saying (this->getValue() != other.getValue()).
|
||||
*/
|
||||
bool operator!= (const Value& other) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Receives callbacks when a Value object changes.
|
||||
@see Value::addListener
|
||||
*/
|
||||
class JUCE_API Listener
|
||||
{
|
||||
public:
|
||||
Listener() = default;
|
||||
virtual ~Listener() = default;
|
||||
|
||||
/** Called when a Value object is changed.
|
||||
|
||||
Note that the Value object passed as a parameter may not be exactly the same
|
||||
object that you registered the listener with - it might be a copy that refers
|
||||
to the same underlying ValueSource. To find out, you can call Value::refersToSameSourceAs().
|
||||
*/
|
||||
virtual void valueChanged (Value& value) = 0;
|
||||
};
|
||||
|
||||
/** Adds a listener to receive callbacks when the value changes.
|
||||
|
||||
The listener is added to this specific Value object, and not to the shared
|
||||
object that it refers to. When this object is deleted, all the listeners will
|
||||
be lost, even if other references to the same Value still exist. So when you're
|
||||
adding a listener, make sure that you add it to a Value instance that will last
|
||||
for as long as you need the listener. In general, you'd never want to add a listener
|
||||
to a local stack-based Value, but more likely to one that's a member variable.
|
||||
|
||||
@see removeListener
|
||||
*/
|
||||
void addListener (Listener* listener);
|
||||
|
||||
/** Removes a listener that was previously added with addListener(). */
|
||||
void removeListener (Listener* listener);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Used internally by the Value class as the base class for its shared value objects.
|
||||
|
||||
The Value class is essentially a reference-counted pointer to a shared instance
|
||||
of a ValueSource object. If you're feeling adventurous, you can create your own custom
|
||||
ValueSource classes to allow Value objects to represent your own custom data items.
|
||||
*/
|
||||
class JUCE_API ValueSource : public ReferenceCountedObject,
|
||||
private AsyncUpdater
|
||||
{
|
||||
public:
|
||||
ValueSource();
|
||||
~ValueSource() override;
|
||||
|
||||
/** Returns the current value of this object. */
|
||||
virtual var getValue() const = 0;
|
||||
|
||||
/** Changes the current value.
|
||||
This must also trigger a change message if the value actually changes.
|
||||
*/
|
||||
virtual void setValue (const var& newValue) = 0;
|
||||
|
||||
/** Delivers a change message to all the listeners that are registered with
|
||||
this value.
|
||||
|
||||
If dispatchSynchronously is true, the method will call all the listeners
|
||||
before returning; otherwise it'll dispatch a message and make the call later.
|
||||
*/
|
||||
void sendChangeMessage (bool dispatchSynchronously);
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
friend class Value;
|
||||
SortedSet<Value*> valuesWithListeners;
|
||||
|
||||
private:
|
||||
void handleAsyncUpdate() override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueSource)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a Value object that uses this valueSource object as its underlying data. */
|
||||
explicit Value (ValueSource* valueSource);
|
||||
|
||||
/** Returns the ValueSource that this value is referring to. */
|
||||
ValueSource& getValueSource() noexcept { return *value; }
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
friend class ValueSource;
|
||||
ReferenceCountedObjectPtr<ValueSource> value;
|
||||
ListenerList<Listener> listeners;
|
||||
|
||||
void callListeners();
|
||||
void removeFromListenerList();
|
||||
|
||||
// This is disallowed to avoid confusion about whether it should
|
||||
// do a by-value or by-reference copy.
|
||||
Value& operator= (const Value&) = delete;
|
||||
|
||||
// This declaration prevents accidental construction from an integer of 0,
|
||||
// which is possible in some compilers via an implicit cast to a pointer.
|
||||
explicit Value (void*) = delete;
|
||||
};
|
||||
|
||||
/** Writes a Value to an OutputStream as a UTF8 string. */
|
||||
OutputStream& JUCE_CALLTYPE operator<< (OutputStream&, const Value&);
|
||||
|
||||
|
||||
} // namespace juce
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
334
deps/juce/modules/juce_data_structures/values/juce_ValueTreePropertyWithDefault.h
vendored
Normal file
334
deps/juce/modules/juce_data_structures/values/juce_ValueTreePropertyWithDefault.h
vendored
Normal file
@ -0,0 +1,334 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This class acts as a wrapper around a property inside a ValueTree.
|
||||
|
||||
If the property inside the ValueTree is missing it will return a default value,
|
||||
which can be specified in the constructor or by calling setDefault().
|
||||
|
||||
@tags{DataStructures}
|
||||
*/
|
||||
class JUCE_API ValueTreePropertyWithDefault : private Value::Listener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an uninitialised ValueTreePropertyWithDefault object.
|
||||
|
||||
Initialise it using one of the referTo() methods.
|
||||
*/
|
||||
ValueTreePropertyWithDefault() = default;
|
||||
|
||||
/** Creates a ValueTreePropertyWithDefault object for the specified property.
|
||||
|
||||
The default value will be an empty var.
|
||||
*/
|
||||
ValueTreePropertyWithDefault (ValueTree& tree,
|
||||
const Identifier& propertyID,
|
||||
UndoManager* um)
|
||||
{
|
||||
referTo (tree, propertyID, um);
|
||||
}
|
||||
|
||||
/** Creates an ValueTreePropertyWithDefault object for the specified property.
|
||||
|
||||
The default value will be defaultToUse.
|
||||
*/
|
||||
ValueTreePropertyWithDefault (ValueTree& tree,
|
||||
const Identifier& propertyID,
|
||||
UndoManager* um,
|
||||
var defaultToUse)
|
||||
{
|
||||
referTo (tree, propertyID, um, defaultToUse);
|
||||
}
|
||||
|
||||
/** Creates a ValueTreePropertyWithDefault object for the specified property.
|
||||
|
||||
The default value will be defaultToUse.
|
||||
|
||||
Use this constructor if the underlying var object being controlled is an array and
|
||||
it will handle the conversion to/from a delimited String that can be written to
|
||||
XML format.
|
||||
*/
|
||||
ValueTreePropertyWithDefault (ValueTree& tree,
|
||||
const Identifier& propertyID,
|
||||
UndoManager* um,
|
||||
var defaultToUse,
|
||||
StringRef arrayDelimiter)
|
||||
{
|
||||
referTo (tree, propertyID, um, defaultToUse, arrayDelimiter);
|
||||
}
|
||||
|
||||
/** Creates a ValueTreePropertyWithDefault object from another ValueTreePropertyWithDefault object. */
|
||||
ValueTreePropertyWithDefault (const ValueTreePropertyWithDefault& other)
|
||||
{
|
||||
referToWithDefault (other.targetTree,
|
||||
other.targetProperty,
|
||||
other.undoManager,
|
||||
other.defaultValue,
|
||||
other.delimiter);
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
~ValueTreePropertyWithDefault() override
|
||||
{
|
||||
defaultValue.removeListener (this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current value of the property.
|
||||
|
||||
If the property does not exist this returns the default value.
|
||||
*/
|
||||
var get() const noexcept
|
||||
{
|
||||
if (isUsingDefault())
|
||||
return defaultValue;
|
||||
|
||||
if (delimiter.isNotEmpty())
|
||||
return delimitedStringToVarArray (targetTree[targetProperty].toString(), delimiter);
|
||||
|
||||
return targetTree[targetProperty];
|
||||
}
|
||||
|
||||
/** Returns the current property as a Value object. */
|
||||
Value getPropertyAsValue() { return targetTree.getPropertyAsValue (targetProperty, undoManager); }
|
||||
|
||||
/** Returns the current default value. */
|
||||
var getDefault() const { return defaultValue; }
|
||||
|
||||
/** Sets the default value to a new var. */
|
||||
void setDefault (const var& newDefault) { defaultValue = newDefault; }
|
||||
|
||||
/** Returns true if the property does not exist in the referenced ValueTree. */
|
||||
bool isUsingDefault() const { return ! targetTree.hasProperty (targetProperty); }
|
||||
|
||||
/** Removes the property from the referenced ValueTree. */
|
||||
void resetToDefault() noexcept { targetTree.removeProperty (targetProperty, nullptr); }
|
||||
|
||||
/** You can assign a lambda to this callback and it will called when the default
|
||||
value is changed.
|
||||
|
||||
@see setDefault
|
||||
*/
|
||||
std::function<void()> onDefaultChange;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the property and returns the new ValueTreePropertyWithDefault.
|
||||
|
||||
This will modify the property in the referenced ValueTree.
|
||||
*/
|
||||
ValueTreePropertyWithDefault& operator= (const var& newValue)
|
||||
{
|
||||
setValue (newValue, undoManager);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Sets the property.
|
||||
|
||||
This will modify the property in the referenced ValueTree.
|
||||
*/
|
||||
void setValue (const var& newValue, UndoManager* undoManagerToUse)
|
||||
{
|
||||
if (auto* array = newValue.getArray())
|
||||
targetTree.setProperty (targetProperty, varArrayToDelimitedString (*array, delimiter), undoManagerToUse);
|
||||
else
|
||||
targetTree.setProperty (targetProperty, newValue, undoManagerToUse);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Makes the ValueTreePropertyWithDefault refer to the specified property inside
|
||||
the given ValueTree.
|
||||
|
||||
The default value will be an empty var.
|
||||
*/
|
||||
void referTo (ValueTree tree,
|
||||
const Identifier& property,
|
||||
UndoManager* um)
|
||||
{
|
||||
referToWithDefault (tree,
|
||||
property,
|
||||
um,
|
||||
Value (new SynchronousValueSource (var())),
|
||||
{});
|
||||
}
|
||||
|
||||
/** Makes the ValueTreePropertyWithDefault refer to the specified property inside
|
||||
the given ValueTree.
|
||||
|
||||
The default value will be defaultVal.
|
||||
*/
|
||||
void referTo (ValueTree tree,
|
||||
const Identifier& property,
|
||||
UndoManager* um,
|
||||
var defaultVal)
|
||||
{
|
||||
referToWithDefault (tree,
|
||||
property,
|
||||
um,
|
||||
Value (new SynchronousValueSource (defaultVal)),
|
||||
{});
|
||||
}
|
||||
|
||||
/** Makes the ValueTreePropertyWithDefault refer to the specified property inside
|
||||
the given ValueTree.
|
||||
|
||||
The default value will be defaultVal.
|
||||
*/
|
||||
void referTo (ValueTree tree,
|
||||
const Identifier& property,
|
||||
UndoManager* um,
|
||||
var defaultVal,
|
||||
StringRef arrayDelimiter)
|
||||
{
|
||||
referToWithDefault (tree,
|
||||
property,
|
||||
um,
|
||||
Value (new SynchronousValueSource (defaultVal)),
|
||||
arrayDelimiter);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a reference to the ValueTree containing the referenced property. */
|
||||
ValueTree& getValueTree() noexcept { return targetTree; }
|
||||
|
||||
/** Returns the property ID of the referenced property. */
|
||||
Identifier& getPropertyID() noexcept { return targetProperty; }
|
||||
|
||||
/** Returns the UndoManager that is being used. */
|
||||
UndoManager* getUndoManager() noexcept { return undoManager; }
|
||||
|
||||
//==============================================================================
|
||||
ValueTreePropertyWithDefault& operator= (const ValueTreePropertyWithDefault& other)
|
||||
{
|
||||
referToWithDefault (other.targetTree,
|
||||
other.targetProperty,
|
||||
other.undoManager,
|
||||
other.defaultValue,
|
||||
other.delimiter);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class SynchronousValueSource : public Value::ValueSource
|
||||
{
|
||||
public:
|
||||
explicit SynchronousValueSource (const var& initialValue)
|
||||
: value (initialValue)
|
||||
{
|
||||
}
|
||||
|
||||
var getValue() const override
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
void setValue (const var& newValue) override
|
||||
{
|
||||
if (! newValue.equalsWithSameType (value))
|
||||
{
|
||||
value = newValue;
|
||||
sendChangeMessage (true);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
var value;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SynchronousValueSource)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
static String varArrayToDelimitedString (const Array<var>& input, StringRef delim)
|
||||
{
|
||||
// if you are trying to control a var that is an array then you need to
|
||||
// set a delimiter string that will be used when writing to XML!
|
||||
jassert (delim.isNotEmpty());
|
||||
|
||||
StringArray elements;
|
||||
|
||||
for (auto& v : input)
|
||||
elements.add (v.toString());
|
||||
|
||||
return elements.joinIntoString (delim);
|
||||
}
|
||||
|
||||
static Array<var> delimitedStringToVarArray (StringRef input, StringRef delim)
|
||||
{
|
||||
Array<var> arr;
|
||||
|
||||
for (auto t : StringArray::fromTokens (input, delim, {}))
|
||||
arr.add (t);
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
void valueChanged (Value&) override
|
||||
{
|
||||
if (onDefaultChange != nullptr)
|
||||
onDefaultChange();
|
||||
}
|
||||
|
||||
void referToWithDefault (ValueTree v,
|
||||
const Identifier& i,
|
||||
UndoManager* um,
|
||||
const Value& defaultVal,
|
||||
StringRef del)
|
||||
{
|
||||
targetTree = v;
|
||||
targetProperty = i;
|
||||
undoManager = um;
|
||||
defaultValue.referTo (defaultVal);
|
||||
delimiter = del;
|
||||
|
||||
defaultValue.addListener (this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ValueTree targetTree;
|
||||
Identifier targetProperty;
|
||||
UndoManager* undoManager = nullptr;
|
||||
Value defaultValue;
|
||||
String delimiter;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_LEAK_DETECTOR (ValueTreePropertyWithDefault)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
using ValueWithDefault [[deprecated ("This class has been renamed to better describe what is does. "
|
||||
"This declaration is here for backwards compatibility and new "
|
||||
"code should use the new class name.")]]
|
||||
= ValueTreePropertyWithDefault;
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
97
deps/juce/modules/juce_data_structures/values/juce_ValueTreePropertyWithDefault_test.cpp
vendored
Normal file
97
deps/juce/modules/juce_data_structures/values/juce_ValueTreePropertyWithDefault_test.cpp
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class ValueTreePropertyWithDefaultTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
ValueTreePropertyWithDefaultTests()
|
||||
: UnitTest ("ValueTreePropertyWithDefault", UnitTestCategories::values)
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("default constructor");
|
||||
{
|
||||
ValueTreePropertyWithDefault value;
|
||||
expect (value.isUsingDefault());
|
||||
expect (value.get() == var());
|
||||
}
|
||||
|
||||
beginTest ("missing property");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
ValueTreePropertyWithDefault value (t, "testKey", nullptr, "default");
|
||||
|
||||
expect (value.isUsingDefault());
|
||||
expectEquals (value.get().toString(), String ("default"));
|
||||
}
|
||||
|
||||
beginTest ("non-empty property");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testKey", "non-default", nullptr);
|
||||
|
||||
ValueTreePropertyWithDefault value (t, "testKey", nullptr, "default");
|
||||
|
||||
expect (! value.isUsingDefault());
|
||||
expectEquals (value.get().toString(), String ("non-default"));
|
||||
}
|
||||
|
||||
beginTest ("set default");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
|
||||
ValueTreePropertyWithDefault value (t, "testkey", nullptr);
|
||||
value.setDefault ("default");
|
||||
|
||||
expect (value.isUsingDefault());
|
||||
expectEquals (value.get().toString(), String ("default"));
|
||||
}
|
||||
|
||||
beginTest ("set value");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testkey", "testvalue", nullptr);
|
||||
|
||||
ValueTreePropertyWithDefault value (t, "testkey", nullptr, "default");
|
||||
value = "newvalue";
|
||||
|
||||
expect (! value.isUsingDefault());
|
||||
expectEquals (t["testkey"].toString(), String ("newvalue"));
|
||||
|
||||
value.resetToDefault();
|
||||
|
||||
expect (value.isUsingDefault());
|
||||
expect (t["testkey"] == var());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static ValueTreePropertyWithDefaultTests valueTreePropertyWithDefaultTests;
|
||||
|
||||
} // namespace juce
|
@ -1,242 +1,242 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
namespace ValueTreeSynchroniserHelpers
|
||||
{
|
||||
enum ChangeType
|
||||
{
|
||||
propertyChanged = 1,
|
||||
fullSync = 2,
|
||||
childAdded = 3,
|
||||
childRemoved = 4,
|
||||
childMoved = 5,
|
||||
propertyRemoved = 6
|
||||
};
|
||||
|
||||
static void getValueTreePath (ValueTree v, const ValueTree& topLevelTree, Array<int>& path)
|
||||
{
|
||||
while (v != topLevelTree)
|
||||
{
|
||||
ValueTree parent (v.getParent());
|
||||
|
||||
if (! parent.isValid())
|
||||
break;
|
||||
|
||||
path.add (parent.indexOf (v));
|
||||
v = parent;
|
||||
}
|
||||
}
|
||||
|
||||
static void writeHeader (MemoryOutputStream& stream, ChangeType type)
|
||||
{
|
||||
stream.writeByte ((char) type);
|
||||
}
|
||||
|
||||
static void writeHeader (ValueTreeSynchroniser& target, MemoryOutputStream& stream,
|
||||
ChangeType type, ValueTree v)
|
||||
{
|
||||
writeHeader (stream, type);
|
||||
|
||||
Array<int> path;
|
||||
getValueTreePath (v, target.getRoot(), path);
|
||||
|
||||
stream.writeCompressedInt (path.size());
|
||||
|
||||
for (int i = path.size(); --i >= 0;)
|
||||
stream.writeCompressedInt (path.getUnchecked(i));
|
||||
}
|
||||
|
||||
static ValueTree readSubTreeLocation (MemoryInputStream& input, ValueTree v)
|
||||
{
|
||||
const int numLevels = input.readCompressedInt();
|
||||
|
||||
if (! isPositiveAndBelow (numLevels, 65536)) // sanity-check
|
||||
return {};
|
||||
|
||||
for (int i = numLevels; --i >= 0;)
|
||||
{
|
||||
const int index = input.readCompressedInt();
|
||||
|
||||
if (! isPositiveAndBelow (index, v.getNumChildren()))
|
||||
return {};
|
||||
|
||||
v = v.getChild (index);
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
ValueTreeSynchroniser::ValueTreeSynchroniser (const ValueTree& tree) : valueTree (tree)
|
||||
{
|
||||
valueTree.addListener (this);
|
||||
}
|
||||
|
||||
ValueTreeSynchroniser::~ValueTreeSynchroniser()
|
||||
{
|
||||
valueTree.removeListener (this);
|
||||
}
|
||||
|
||||
void ValueTreeSynchroniser::sendFullSyncCallback()
|
||||
{
|
||||
MemoryOutputStream m;
|
||||
writeHeader (m, ValueTreeSynchroniserHelpers::fullSync);
|
||||
valueTree.writeToStream (m);
|
||||
stateChanged (m.getData(), m.getDataSize());
|
||||
}
|
||||
|
||||
void ValueTreeSynchroniser::valueTreePropertyChanged (ValueTree& vt, const Identifier& property)
|
||||
{
|
||||
MemoryOutputStream m;
|
||||
|
||||
if (auto* value = vt.getPropertyPointer (property))
|
||||
{
|
||||
ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::propertyChanged, vt);
|
||||
m.writeString (property.toString());
|
||||
value->writeToStream (m);
|
||||
}
|
||||
else
|
||||
{
|
||||
ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::propertyRemoved, vt);
|
||||
m.writeString (property.toString());
|
||||
}
|
||||
|
||||
stateChanged (m.getData(), m.getDataSize());
|
||||
}
|
||||
|
||||
void ValueTreeSynchroniser::valueTreeChildAdded (ValueTree& parentTree, ValueTree& childTree)
|
||||
{
|
||||
const int index = parentTree.indexOf (childTree);
|
||||
jassert (index >= 0);
|
||||
|
||||
MemoryOutputStream m;
|
||||
ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childAdded, parentTree);
|
||||
m.writeCompressedInt (index);
|
||||
childTree.writeToStream (m);
|
||||
stateChanged (m.getData(), m.getDataSize());
|
||||
}
|
||||
|
||||
void ValueTreeSynchroniser::valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int oldIndex)
|
||||
{
|
||||
MemoryOutputStream m;
|
||||
ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childRemoved, parentTree);
|
||||
m.writeCompressedInt (oldIndex);
|
||||
stateChanged (m.getData(), m.getDataSize());
|
||||
}
|
||||
|
||||
void ValueTreeSynchroniser::valueTreeChildOrderChanged (ValueTree& parent, int oldIndex, int newIndex)
|
||||
{
|
||||
MemoryOutputStream m;
|
||||
ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childMoved, parent);
|
||||
m.writeCompressedInt (oldIndex);
|
||||
m.writeCompressedInt (newIndex);
|
||||
stateChanged (m.getData(), m.getDataSize());
|
||||
}
|
||||
|
||||
bool ValueTreeSynchroniser::applyChange (ValueTree& root, const void* data, size_t dataSize, UndoManager* undoManager)
|
||||
{
|
||||
MemoryInputStream input (data, dataSize, false);
|
||||
|
||||
const ValueTreeSynchroniserHelpers::ChangeType type = (ValueTreeSynchroniserHelpers::ChangeType) input.readByte();
|
||||
|
||||
if (type == ValueTreeSynchroniserHelpers::fullSync)
|
||||
{
|
||||
root = ValueTree::readFromStream (input);
|
||||
return true;
|
||||
}
|
||||
|
||||
ValueTree v (ValueTreeSynchroniserHelpers::readSubTreeLocation (input, root));
|
||||
|
||||
if (! v.isValid())
|
||||
return false;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ValueTreeSynchroniserHelpers::propertyChanged:
|
||||
{
|
||||
Identifier property (input.readString());
|
||||
v.setProperty (property, var::readFromStream (input), undoManager);
|
||||
return true;
|
||||
}
|
||||
|
||||
case ValueTreeSynchroniserHelpers::propertyRemoved:
|
||||
{
|
||||
Identifier property (input.readString());
|
||||
v.removeProperty (property, undoManager);
|
||||
return true;
|
||||
}
|
||||
|
||||
case ValueTreeSynchroniserHelpers::childAdded:
|
||||
{
|
||||
const int index = input.readCompressedInt();
|
||||
v.addChild (ValueTree::readFromStream (input), index, undoManager);
|
||||
return true;
|
||||
}
|
||||
|
||||
case ValueTreeSynchroniserHelpers::childRemoved:
|
||||
{
|
||||
const int index = input.readCompressedInt();
|
||||
|
||||
if (isPositiveAndBelow (index, v.getNumChildren()))
|
||||
{
|
||||
v.removeChild (index, undoManager);
|
||||
return true;
|
||||
}
|
||||
|
||||
jassertfalse; // Either received some corrupt data, or the trees have drifted out of sync
|
||||
break;
|
||||
}
|
||||
|
||||
case ValueTreeSynchroniserHelpers::childMoved:
|
||||
{
|
||||
const int oldIndex = input.readCompressedInt();
|
||||
const int newIndex = input.readCompressedInt();
|
||||
|
||||
if (isPositiveAndBelow (oldIndex, v.getNumChildren())
|
||||
&& isPositiveAndBelow (newIndex, v.getNumChildren()))
|
||||
{
|
||||
v.moveChild (oldIndex, newIndex, undoManager);
|
||||
return true;
|
||||
}
|
||||
|
||||
jassertfalse; // Either received some corrupt data, or the trees have drifted out of sync
|
||||
break;
|
||||
}
|
||||
|
||||
case ValueTreeSynchroniserHelpers::fullSync:
|
||||
break;
|
||||
|
||||
default:
|
||||
jassertfalse; // Seem to have received some corrupt data?
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
namespace ValueTreeSynchroniserHelpers
|
||||
{
|
||||
enum ChangeType
|
||||
{
|
||||
propertyChanged = 1,
|
||||
fullSync = 2,
|
||||
childAdded = 3,
|
||||
childRemoved = 4,
|
||||
childMoved = 5,
|
||||
propertyRemoved = 6
|
||||
};
|
||||
|
||||
static void getValueTreePath (ValueTree v, const ValueTree& topLevelTree, Array<int>& path)
|
||||
{
|
||||
while (v != topLevelTree)
|
||||
{
|
||||
ValueTree parent (v.getParent());
|
||||
|
||||
if (! parent.isValid())
|
||||
break;
|
||||
|
||||
path.add (parent.indexOf (v));
|
||||
v = parent;
|
||||
}
|
||||
}
|
||||
|
||||
static void writeHeader (MemoryOutputStream& stream, ChangeType type)
|
||||
{
|
||||
stream.writeByte ((char) type);
|
||||
}
|
||||
|
||||
static void writeHeader (ValueTreeSynchroniser& target, MemoryOutputStream& stream,
|
||||
ChangeType type, ValueTree v)
|
||||
{
|
||||
writeHeader (stream, type);
|
||||
|
||||
Array<int> path;
|
||||
getValueTreePath (v, target.getRoot(), path);
|
||||
|
||||
stream.writeCompressedInt (path.size());
|
||||
|
||||
for (int i = path.size(); --i >= 0;)
|
||||
stream.writeCompressedInt (path.getUnchecked(i));
|
||||
}
|
||||
|
||||
static ValueTree readSubTreeLocation (MemoryInputStream& input, ValueTree v)
|
||||
{
|
||||
const int numLevels = input.readCompressedInt();
|
||||
|
||||
if (! isPositiveAndBelow (numLevels, 65536)) // sanity-check
|
||||
return {};
|
||||
|
||||
for (int i = numLevels; --i >= 0;)
|
||||
{
|
||||
const int index = input.readCompressedInt();
|
||||
|
||||
if (! isPositiveAndBelow (index, v.getNumChildren()))
|
||||
return {};
|
||||
|
||||
v = v.getChild (index);
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
ValueTreeSynchroniser::ValueTreeSynchroniser (const ValueTree& tree) : valueTree (tree)
|
||||
{
|
||||
valueTree.addListener (this);
|
||||
}
|
||||
|
||||
ValueTreeSynchroniser::~ValueTreeSynchroniser()
|
||||
{
|
||||
valueTree.removeListener (this);
|
||||
}
|
||||
|
||||
void ValueTreeSynchroniser::sendFullSyncCallback()
|
||||
{
|
||||
MemoryOutputStream m;
|
||||
writeHeader (m, ValueTreeSynchroniserHelpers::fullSync);
|
||||
valueTree.writeToStream (m);
|
||||
stateChanged (m.getData(), m.getDataSize());
|
||||
}
|
||||
|
||||
void ValueTreeSynchroniser::valueTreePropertyChanged (ValueTree& vt, const Identifier& property)
|
||||
{
|
||||
MemoryOutputStream m;
|
||||
|
||||
if (auto* value = vt.getPropertyPointer (property))
|
||||
{
|
||||
ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::propertyChanged, vt);
|
||||
m.writeString (property.toString());
|
||||
value->writeToStream (m);
|
||||
}
|
||||
else
|
||||
{
|
||||
ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::propertyRemoved, vt);
|
||||
m.writeString (property.toString());
|
||||
}
|
||||
|
||||
stateChanged (m.getData(), m.getDataSize());
|
||||
}
|
||||
|
||||
void ValueTreeSynchroniser::valueTreeChildAdded (ValueTree& parentTree, ValueTree& childTree)
|
||||
{
|
||||
const int index = parentTree.indexOf (childTree);
|
||||
jassert (index >= 0);
|
||||
|
||||
MemoryOutputStream m;
|
||||
ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childAdded, parentTree);
|
||||
m.writeCompressedInt (index);
|
||||
childTree.writeToStream (m);
|
||||
stateChanged (m.getData(), m.getDataSize());
|
||||
}
|
||||
|
||||
void ValueTreeSynchroniser::valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int oldIndex)
|
||||
{
|
||||
MemoryOutputStream m;
|
||||
ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childRemoved, parentTree);
|
||||
m.writeCompressedInt (oldIndex);
|
||||
stateChanged (m.getData(), m.getDataSize());
|
||||
}
|
||||
|
||||
void ValueTreeSynchroniser::valueTreeChildOrderChanged (ValueTree& parent, int oldIndex, int newIndex)
|
||||
{
|
||||
MemoryOutputStream m;
|
||||
ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childMoved, parent);
|
||||
m.writeCompressedInt (oldIndex);
|
||||
m.writeCompressedInt (newIndex);
|
||||
stateChanged (m.getData(), m.getDataSize());
|
||||
}
|
||||
|
||||
bool ValueTreeSynchroniser::applyChange (ValueTree& root, const void* data, size_t dataSize, UndoManager* undoManager)
|
||||
{
|
||||
MemoryInputStream input (data, dataSize, false);
|
||||
|
||||
const ValueTreeSynchroniserHelpers::ChangeType type = (ValueTreeSynchroniserHelpers::ChangeType) input.readByte();
|
||||
|
||||
if (type == ValueTreeSynchroniserHelpers::fullSync)
|
||||
{
|
||||
root = ValueTree::readFromStream (input);
|
||||
return true;
|
||||
}
|
||||
|
||||
ValueTree v (ValueTreeSynchroniserHelpers::readSubTreeLocation (input, root));
|
||||
|
||||
if (! v.isValid())
|
||||
return false;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ValueTreeSynchroniserHelpers::propertyChanged:
|
||||
{
|
||||
Identifier property (input.readString());
|
||||
v.setProperty (property, var::readFromStream (input), undoManager);
|
||||
return true;
|
||||
}
|
||||
|
||||
case ValueTreeSynchroniserHelpers::propertyRemoved:
|
||||
{
|
||||
Identifier property (input.readString());
|
||||
v.removeProperty (property, undoManager);
|
||||
return true;
|
||||
}
|
||||
|
||||
case ValueTreeSynchroniserHelpers::childAdded:
|
||||
{
|
||||
const int index = input.readCompressedInt();
|
||||
v.addChild (ValueTree::readFromStream (input), index, undoManager);
|
||||
return true;
|
||||
}
|
||||
|
||||
case ValueTreeSynchroniserHelpers::childRemoved:
|
||||
{
|
||||
const int index = input.readCompressedInt();
|
||||
|
||||
if (isPositiveAndBelow (index, v.getNumChildren()))
|
||||
{
|
||||
v.removeChild (index, undoManager);
|
||||
return true;
|
||||
}
|
||||
|
||||
jassertfalse; // Either received some corrupt data, or the trees have drifted out of sync
|
||||
break;
|
||||
}
|
||||
|
||||
case ValueTreeSynchroniserHelpers::childMoved:
|
||||
{
|
||||
const int oldIndex = input.readCompressedInt();
|
||||
const int newIndex = input.readCompressedInt();
|
||||
|
||||
if (isPositiveAndBelow (oldIndex, v.getNumChildren())
|
||||
&& isPositiveAndBelow (newIndex, v.getNumChildren()))
|
||||
{
|
||||
v.moveChild (oldIndex, newIndex, undoManager);
|
||||
return true;
|
||||
}
|
||||
|
||||
jassertfalse; // Either received some corrupt data, or the trees have drifted out of sync
|
||||
break;
|
||||
}
|
||||
|
||||
case ValueTreeSynchroniserHelpers::fullSync:
|
||||
break;
|
||||
|
||||
default:
|
||||
jassertfalse; // Seem to have received some corrupt data?
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,97 +1,97 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This class can be used to watch for all changes to the state of a ValueTree,
|
||||
and to convert them to a transmittable binary encoding.
|
||||
|
||||
The purpose of this class is to allow two or more ValueTrees to be remotely
|
||||
synchronised by transmitting encoded changes over some kind of transport
|
||||
mechanism.
|
||||
|
||||
To use it, you'll need to implement a subclass of ValueTreeSynchroniser
|
||||
and implement the stateChanged() method to transmit the encoded change (maybe
|
||||
via a network or other means) to a remote destination, where it can be
|
||||
applied to a target tree.
|
||||
|
||||
@tags{DataStructures}
|
||||
*/
|
||||
class JUCE_API ValueTreeSynchroniser : private ValueTree::Listener
|
||||
{
|
||||
public:
|
||||
/** Creates a ValueTreeSynchroniser that watches the given tree.
|
||||
|
||||
After creating an instance of this class and somehow attaching it to
|
||||
a target tree, you probably want to call sendFullSyncCallback() to
|
||||
get them into a common starting state.
|
||||
*/
|
||||
ValueTreeSynchroniser (const ValueTree& tree);
|
||||
|
||||
/** Destructor. */
|
||||
~ValueTreeSynchroniser() override;
|
||||
|
||||
/** This callback happens when the ValueTree changes and the given state-change message
|
||||
needs to be applied to any other trees that need to stay in sync with it.
|
||||
The data is an opaque blob of binary that you should transmit to wherever your
|
||||
target tree lives, and use applyChange() to apply this to the target tree.
|
||||
*/
|
||||
virtual void stateChanged (const void* encodedChange, size_t encodedChangeSize) = 0;
|
||||
|
||||
/** Forces the sending of a full state message, which may be large, as it
|
||||
encodes the entire ValueTree.
|
||||
|
||||
This will internally invoke stateChanged() with the encoded version of the state.
|
||||
*/
|
||||
void sendFullSyncCallback();
|
||||
|
||||
/** Applies an encoded change to the given destination tree.
|
||||
|
||||
When you implement a receiver for changes that were sent by the stateChanged()
|
||||
message, this is the function that you'll need to call to apply them to the
|
||||
target tree that you want to be synced.
|
||||
*/
|
||||
static bool applyChange (ValueTree& target,
|
||||
const void* encodedChangeData, size_t encodedChangeDataSize,
|
||||
UndoManager* undoManager);
|
||||
|
||||
/** Returns the root ValueTree that is being observed. */
|
||||
const ValueTree& getRoot() noexcept { return valueTree; }
|
||||
|
||||
private:
|
||||
ValueTree valueTree;
|
||||
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier&) override;
|
||||
void valueTreeChildAdded (ValueTree&, ValueTree&) override;
|
||||
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override;
|
||||
void valueTreeChildOrderChanged (ValueTree&, int, int) override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreeSynchroniser)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This class can be used to watch for all changes to the state of a ValueTree,
|
||||
and to convert them to a transmittable binary encoding.
|
||||
|
||||
The purpose of this class is to allow two or more ValueTrees to be remotely
|
||||
synchronised by transmitting encoded changes over some kind of transport
|
||||
mechanism.
|
||||
|
||||
To use it, you'll need to implement a subclass of ValueTreeSynchroniser
|
||||
and implement the stateChanged() method to transmit the encoded change (maybe
|
||||
via a network or other means) to a remote destination, where it can be
|
||||
applied to a target tree.
|
||||
|
||||
@tags{DataStructures}
|
||||
*/
|
||||
class JUCE_API ValueTreeSynchroniser : private ValueTree::Listener
|
||||
{
|
||||
public:
|
||||
/** Creates a ValueTreeSynchroniser that watches the given tree.
|
||||
|
||||
After creating an instance of this class and somehow attaching it to
|
||||
a target tree, you probably want to call sendFullSyncCallback() to
|
||||
get them into a common starting state.
|
||||
*/
|
||||
ValueTreeSynchroniser (const ValueTree& tree);
|
||||
|
||||
/** Destructor. */
|
||||
~ValueTreeSynchroniser() override;
|
||||
|
||||
/** This callback happens when the ValueTree changes and the given state-change message
|
||||
needs to be applied to any other trees that need to stay in sync with it.
|
||||
The data is an opaque blob of binary that you should transmit to wherever your
|
||||
target tree lives, and use applyChange() to apply this to the target tree.
|
||||
*/
|
||||
virtual void stateChanged (const void* encodedChange, size_t encodedChangeSize) = 0;
|
||||
|
||||
/** Forces the sending of a full state message, which may be large, as it
|
||||
encodes the entire ValueTree.
|
||||
|
||||
This will internally invoke stateChanged() with the encoded version of the state.
|
||||
*/
|
||||
void sendFullSyncCallback();
|
||||
|
||||
/** Applies an encoded change to the given destination tree.
|
||||
|
||||
When you implement a receiver for changes that were sent by the stateChanged()
|
||||
message, this is the function that you'll need to call to apply them to the
|
||||
target tree that you want to be synced.
|
||||
*/
|
||||
static bool applyChange (ValueTree& target,
|
||||
const void* encodedChangeData, size_t encodedChangeDataSize,
|
||||
UndoManager* undoManager);
|
||||
|
||||
/** Returns the root ValueTree that is being observed. */
|
||||
const ValueTree& getRoot() noexcept { return valueTree; }
|
||||
|
||||
private:
|
||||
ValueTree valueTree;
|
||||
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier&) override;
|
||||
void valueTreeChildAdded (ValueTree&, ValueTree&) override;
|
||||
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override;
|
||||
void valueTreeChildOrderChanged (ValueTree&, int, int) override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreeSynchroniser)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,101 +0,0 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class ValueWithDefaultTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
ValueWithDefaultTests()
|
||||
: UnitTest ("ValueWithDefault", UnitTestCategories::values)
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("default constructor");
|
||||
{
|
||||
ValueWithDefault vwd;
|
||||
expect (vwd.isUsingDefault());
|
||||
expect (vwd.get() == var());
|
||||
}
|
||||
|
||||
beginTest ("missing property");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
ValueWithDefault vwd (t, "testKey", nullptr, "default");
|
||||
|
||||
expect (vwd.isUsingDefault());
|
||||
expectEquals (vwd.get().toString(), String ("default"));
|
||||
}
|
||||
|
||||
beginTest ("non-empty property");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testKey", "non-default", nullptr);
|
||||
|
||||
ValueWithDefault vwd (t, "testKey", nullptr, "default");
|
||||
|
||||
expect (! vwd.isUsingDefault());
|
||||
expectEquals (vwd.get().toString(), String ("non-default"));
|
||||
}
|
||||
|
||||
beginTest ("set default");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
|
||||
ValueWithDefault vwd (t, "testkey", nullptr);
|
||||
vwd.setDefault ("default");
|
||||
|
||||
expect (vwd.isUsingDefault());
|
||||
expectEquals (vwd.get().toString(), String ("default"));
|
||||
}
|
||||
|
||||
beginTest ("set value");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testkey", "testvalue", nullptr);
|
||||
|
||||
ValueWithDefault vwd (t, "testkey", nullptr, "default");
|
||||
vwd = "newvalue";
|
||||
|
||||
expect (! vwd.isUsingDefault());
|
||||
expectEquals (t["testkey"].toString(), String ("newvalue"));
|
||||
|
||||
vwd.resetToDefault();
|
||||
|
||||
expect (vwd.isUsingDefault());
|
||||
expect (t["testkey"] == var());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static ValueWithDefaultTests valueWithDefaultTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
@ -1,244 +0,0 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This class acts as a wrapper around a property inside a ValueTree.
|
||||
|
||||
If the property inside the ValueTree is missing the ValueWithDefault will automatically
|
||||
return a default value, which can be specified when initialising the ValueWithDefault.
|
||||
|
||||
@tags{DataStructures}
|
||||
*/
|
||||
class ValueWithDefault
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an unitialised ValueWithDefault. Initialise it using one of the referTo() methods. */
|
||||
ValueWithDefault() = default;
|
||||
|
||||
/** Creates an ValueWithDefault object. The default value will be an empty var. */
|
||||
ValueWithDefault (ValueTree& tree, const Identifier& propertyID, UndoManager* um)
|
||||
: targetTree (tree),
|
||||
targetProperty (propertyID),
|
||||
undoManager (um),
|
||||
defaultValue()
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates an ValueWithDefault object. The default value will be defaultToUse. */
|
||||
ValueWithDefault (ValueTree& tree, const Identifier& propertyID, UndoManager* um,
|
||||
const var& defaultToUse)
|
||||
: targetTree (tree),
|
||||
targetProperty (propertyID),
|
||||
undoManager (um),
|
||||
defaultValue (defaultToUse)
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates an ValueWithDefault object. The default value will be defaultToUse.
|
||||
|
||||
Use this constructor if the underlying var object being controlled is an array and
|
||||
it will handle the conversion to/from a delimited String that can be written to
|
||||
XML format.
|
||||
*/
|
||||
ValueWithDefault (ValueTree& tree, const Identifier& propertyID, UndoManager* um,
|
||||
const var& defaultToUse, StringRef arrayDelimiter)
|
||||
: targetTree (tree),
|
||||
targetProperty (propertyID),
|
||||
undoManager (um),
|
||||
defaultValue (defaultToUse),
|
||||
delimiter (arrayDelimiter)
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates a ValueWithDefault object from another ValueWithDefault object. */
|
||||
ValueWithDefault (const ValueWithDefault& other)
|
||||
: targetTree (other.targetTree),
|
||||
targetProperty (other.targetProperty),
|
||||
undoManager (other.undoManager),
|
||||
defaultValue (other.defaultValue),
|
||||
delimiter (other.delimiter)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current value of the property. If the property does not exist this
|
||||
returns the default value.
|
||||
*/
|
||||
var get() const noexcept
|
||||
{
|
||||
if (isUsingDefault())
|
||||
return defaultValue;
|
||||
|
||||
if (delimiter.isNotEmpty())
|
||||
return delimitedStringToVarArray (targetTree[targetProperty].toString());
|
||||
|
||||
return targetTree[targetProperty];
|
||||
}
|
||||
|
||||
/** Returns the current property as a Value object. */
|
||||
Value getPropertyAsValue() { return targetTree.getPropertyAsValue (targetProperty, undoManager); }
|
||||
|
||||
/** Returns the current default value. */
|
||||
var getDefault() const { return defaultValue; }
|
||||
|
||||
/** Sets the default value to a new var. */
|
||||
void setDefault (const var& newDefault)
|
||||
{
|
||||
if (defaultValue != newDefault)
|
||||
{
|
||||
defaultValue = newDefault;
|
||||
|
||||
if (onDefaultChange != nullptr)
|
||||
onDefaultChange();
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true if the property does not exist in the referenced ValueTree. */
|
||||
bool isUsingDefault() const
|
||||
{
|
||||
return ! targetTree.hasProperty (targetProperty);
|
||||
}
|
||||
|
||||
/** Removes the property from the referenced ValueTree. */
|
||||
void resetToDefault() noexcept
|
||||
{
|
||||
targetTree.removeProperty (targetProperty, nullptr);
|
||||
}
|
||||
|
||||
/** You can assign a lambda to this callback object to have it called when the default value is changed. */
|
||||
std::function<void()> onDefaultChange;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the property and returns the new ValueWithDefault. This will modify the property in the referenced ValueTree. */
|
||||
ValueWithDefault& operator= (const var& newValue)
|
||||
{
|
||||
setValue (newValue, undoManager);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Sets the property. This will actually modify the property in the referenced ValueTree. */
|
||||
void setValue (const var& newValue, UndoManager* undoManagerToUse)
|
||||
{
|
||||
if (auto* array = newValue.getArray())
|
||||
targetTree.setProperty (targetProperty, varArrayToDelimitedString (*array), undoManagerToUse);
|
||||
else
|
||||
targetTree.setProperty (targetProperty, newValue, undoManagerToUse);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Makes the ValueWithDefault refer to the specified property inside the given ValueTree. */
|
||||
void referTo (ValueTree& tree, const Identifier& property, UndoManager* um)
|
||||
{
|
||||
referToWithDefault (tree, property, um, var(), {});
|
||||
}
|
||||
|
||||
/** Makes the ValueWithDefault refer to the specified property inside the given ValueTree,
|
||||
and specifies a default value to use.
|
||||
*/
|
||||
void referTo (ValueTree& tree, const Identifier& property, UndoManager* um, const var& defaultVal)
|
||||
{
|
||||
referToWithDefault (tree, property, um, defaultVal, {});
|
||||
}
|
||||
|
||||
void referTo (ValueTree& tree, const Identifier& property, UndoManager* um,
|
||||
const var& defaultVal, StringRef arrayDelimiter)
|
||||
{
|
||||
referToWithDefault (tree, property, um, defaultVal, arrayDelimiter);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a reference to the ValueTree containing the referenced property. */
|
||||
ValueTree& getValueTree() noexcept { return targetTree; }
|
||||
|
||||
/** Returns the property ID of the referenced property. */
|
||||
Identifier& getPropertyID() noexcept { return targetProperty; }
|
||||
|
||||
/** Returns the UndoManager that is being used. */
|
||||
UndoManager* getUndoManager() noexcept { return undoManager; }
|
||||
|
||||
//==============================================================================
|
||||
ValueWithDefault& operator= (const ValueWithDefault& other)
|
||||
{
|
||||
referToWithDefault (other.targetTree, other.targetProperty, other.undoManager,
|
||||
other.defaultValue, other.delimiter);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ValueTree targetTree;
|
||||
Identifier targetProperty;
|
||||
UndoManager* undoManager = nullptr;
|
||||
var defaultValue;
|
||||
|
||||
String delimiter;
|
||||
|
||||
//==============================================================================
|
||||
void referToWithDefault (const ValueTree& v, const Identifier& i, UndoManager* um,
|
||||
const var& defaultVal, StringRef del)
|
||||
{
|
||||
targetTree = v;
|
||||
targetProperty = i;
|
||||
undoManager = um;
|
||||
defaultValue = defaultVal;
|
||||
delimiter = del;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String varArrayToDelimitedString (const Array<var>& input) const noexcept
|
||||
{
|
||||
// if you are trying to control a var that is an array then you need to
|
||||
// set a delimiter string that will be used when writing to XML!
|
||||
jassert (delimiter.isNotEmpty());
|
||||
|
||||
StringArray elements;
|
||||
|
||||
for (auto& v : input)
|
||||
elements.add (v.toString());
|
||||
|
||||
return elements.joinIntoString (delimiter);
|
||||
}
|
||||
|
||||
Array<var> delimitedStringToVarArray (StringRef input) const noexcept
|
||||
{
|
||||
Array<var> arr;
|
||||
|
||||
for (auto t : StringArray::fromTokens (input, delimiter, {}))
|
||||
arr.add (t);
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (ValueWithDefault)
|
||||
};
|
||||
|
||||
} // namespace juce
|
Reference in New Issue
Block a user