@@ -38,7 +38,10 @@ | |||
#include "values/juce_ValueTree.cpp" | |||
#include "values/juce_ValueTreeSynchroniser.cpp" | |||
#include "values/juce_CachedValue.cpp" | |||
#include "values/juce_ValueWithDefault.cpp" | |||
#include "undomanager/juce_UndoManager.cpp" | |||
#include "app_properties/juce_ApplicationProperties.cpp" | |||
#include "app_properties/juce_PropertiesFile.cpp" | |||
#if JUCE_UNIT_TESTS | |||
#include "values/juce_ValueTreePropertyWithDefault_test.cpp" | |||
#endif |
@@ -61,6 +61,6 @@ | |||
#include "values/juce_ValueTree.h" | |||
#include "values/juce_ValueTreeSynchroniser.h" | |||
#include "values/juce_CachedValue.h" | |||
#include "values/juce_ValueWithDefault.h" | |||
#include "values/juce_ValueTreePropertyWithDefault.h" | |||
#include "app_properties/juce_PropertiesFile.h" | |||
#include "app_properties/juce_ApplicationProperties.h" |
@@ -0,0 +1,293 @@ | |||
/* | |||
============================================================================== | |||
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 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()); | |||
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), 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, {}, {}); | |||
} | |||
/** 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 (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 (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: | |||
//============================================================================== | |||
ValueTree targetTree; | |||
Identifier targetProperty; | |||
UndoManager* undoManager = nullptr; | |||
Value defaultValue; | |||
String delimiter; | |||
//============================================================================== | |||
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); | |||
} | |||
//============================================================================== | |||
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; | |||
} | |||
}; | |||
//============================================================================== | |||
#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 |
@@ -26,31 +26,29 @@ | |||
namespace juce | |||
{ | |||
#if JUCE_UNIT_TESTS | |||
class ValueWithDefaultTests : public UnitTest | |||
class ValueTreePropertyWithDefaultTests : public UnitTest | |||
{ | |||
public: | |||
ValueWithDefaultTests() | |||
: UnitTest ("ValueWithDefault", UnitTestCategories::values) | |||
ValueTreePropertyWithDefaultTests() | |||
: UnitTest ("ValueTreePropertyWithDefault", UnitTestCategories::values) | |||
{} | |||
void runTest() override | |||
{ | |||
beginTest ("default constructor"); | |||
{ | |||
ValueWithDefault vwd; | |||
expect (vwd.isUsingDefault()); | |||
expect (vwd.get() == var()); | |||
ValueTreePropertyWithDefault value; | |||
expect (value.isUsingDefault()); | |||
expect (value.get() == var()); | |||
} | |||
beginTest ("missing property"); | |||
{ | |||
ValueTree t ("root"); | |||
ValueWithDefault vwd (t, "testKey", nullptr, "default"); | |||
ValueTreePropertyWithDefault value (t, "testKey", nullptr, "default"); | |||
expect (vwd.isUsingDefault()); | |||
expectEquals (vwd.get().toString(), String ("default")); | |||
expect (value.isUsingDefault()); | |||
expectEquals (value.get().toString(), String ("default")); | |||
} | |||
beginTest ("non-empty property"); | |||
@@ -58,21 +56,21 @@ public: | |||
ValueTree t ("root"); | |||
t.setProperty ("testKey", "non-default", nullptr); | |||
ValueWithDefault vwd (t, "testKey", nullptr, "default"); | |||
ValueTreePropertyWithDefault value (t, "testKey", nullptr, "default"); | |||
expect (! vwd.isUsingDefault()); | |||
expectEquals (vwd.get().toString(), String ("non-default")); | |||
expect (! value.isUsingDefault()); | |||
expectEquals (value.get().toString(), String ("non-default")); | |||
} | |||
beginTest ("set default"); | |||
{ | |||
ValueTree t ("root"); | |||
ValueWithDefault vwd (t, "testkey", nullptr); | |||
vwd.setDefault ("default"); | |||
ValueTreePropertyWithDefault value (t, "testkey", nullptr); | |||
value.setDefault ("default"); | |||
expect (vwd.isUsingDefault()); | |||
expectEquals (vwd.get().toString(), String ("default")); | |||
expect (value.isUsingDefault()); | |||
expectEquals (value.get().toString(), String ("default")); | |||
} | |||
beginTest ("set value"); | |||
@@ -80,22 +78,20 @@ public: | |||
ValueTree t ("root"); | |||
t.setProperty ("testkey", "testvalue", nullptr); | |||
ValueWithDefault vwd (t, "testkey", nullptr, "default"); | |||
vwd = "newvalue"; | |||
ValueTreePropertyWithDefault value (t, "testkey", nullptr, "default"); | |||
value = "newvalue"; | |||
expect (! vwd.isUsingDefault()); | |||
expect (! value.isUsingDefault()); | |||
expectEquals (t["testkey"].toString(), String ("newvalue")); | |||
vwd.resetToDefault(); | |||
value.resetToDefault(); | |||
expect (vwd.isUsingDefault()); | |||
expect (value.isUsingDefault()); | |||
expect (t["testkey"] == var()); | |||
} | |||
} | |||
}; | |||
static ValueWithDefaultTests valueWithDefaultTests; | |||
#endif | |||
static ValueTreePropertyWithDefaultTests valueTreePropertyWithDefaultTests; | |||
} // 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 |