/* ============================================================================== 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 allows you to read or write the property as an int, and a CachedValue 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 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 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 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 inline CachedValue::CachedValue() = default; template inline CachedValue::CachedValue (ValueTree& v, const Identifier& i, UndoManager* um) : targetTree (v), targetProperty (i), undoManager (um), defaultValue(), cachedValue (getTypedValue()) { targetTree.addListener (this); } template inline CachedValue::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 inline Value CachedValue::getPropertyAsValue() { return targetTree.getPropertyAsValue (targetProperty, undoManager); } template inline bool CachedValue::isUsingDefault() const { return ! targetTree.hasProperty (targetProperty); } template inline CachedValue& CachedValue::operator= (const Type& newValue) { setValue (newValue, undoManager); return *this; } template inline void CachedValue::setValue (const Type& newValue, UndoManager* undoManagerToUse) { if (cachedValue != newValue || isUsingDefault()) { cachedValue = newValue; targetTree.setProperty (targetProperty, VariantConverter::toVar (newValue), undoManagerToUse); } } template inline void CachedValue::resetToDefault() { resetToDefault (undoManager); } template inline void CachedValue::resetToDefault (UndoManager* undoManagerToUse) { targetTree.removeProperty (targetProperty, undoManagerToUse); forceUpdateOfCachedValue(); } template inline void CachedValue::referTo (ValueTree& v, const Identifier& i, UndoManager* um) { referToWithDefault (v, i, um, Type()); } template inline void CachedValue::referTo (ValueTree& v, const Identifier& i, UndoManager* um, const Type& defaultVal) { referToWithDefault (v, i, um, defaultVal); } template inline void CachedValue::forceUpdateOfCachedValue() { cachedValue = getTypedValue(); } template inline void CachedValue::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 inline Type CachedValue::getTypedValue() const { if (const var* property = targetTree.getPropertyPointer (targetProperty)) return VariantConverter::fromVar (*property); return defaultValue; } template inline void CachedValue::valueTreePropertyChanged (ValueTree& changedTree, const Identifier& changedProperty) { if (changedProperty == targetProperty && targetTree == changedTree) forceUpdateOfCachedValue(); } } // namespace juce