diff --git a/modules/juce_data_structures/values/juce_Value.cpp b/modules/juce_data_structures/values/juce_Value.cpp index acbc24c29a..1a28a470d6 100644 --- a/modules/juce_data_structures/values/juce_Value.cpp +++ b/modules/juce_data_structures/values/juce_Value.cpp @@ -23,39 +23,100 @@ ============================================================================== */ +class SharedValueSourceUpdater : public ReferenceCountedObject, + private AsyncUpdater +{ +public: + SharedValueSourceUpdater() : insideCallback (false) {} + + typedef ReferenceCountedObjectPtr Ptr; + + void update (Value::ValueSource* source) + { + sourcesToUpdate.add (source); + + if (! insideCallback) + triggerAsyncUpdate(); + } + + static SharedValueSourceUpdater* getOrCreateSharedUpdater() + { + Ptr& p = getSharedUpdater(); + + if (p == nullptr) + p = new SharedValueSourceUpdater(); + + return p; + } + + static void releaseIfUnused() + { + if (Ptr& p = getSharedUpdater()) + if (p->getReferenceCount() == 1) + p = nullptr; + } + +private: + ReferenceCountedArray sourcesToUpdate; + bool insideCallback; + + static Ptr& getSharedUpdater() + { + static Ptr updater; + return updater; + } + + void handleAsyncUpdate() + { + int maxLoops = 10; + const ScopedValueSetter inside (insideCallback, true, false); + const Ptr localRef (this); + + while (sourcesToUpdate.size() > 0 && --maxLoops >= 0) + { + ReferenceCountedArray sources; + sources.swapWithArray (sourcesToUpdate); + + for (int i = 0; i < sources.size(); ++i) + sources.getObjectPointerUnchecked(i)->sendChangeMessage (true); + } + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SharedValueSourceUpdater); +}; + Value::ValueSource::ValueSource() { } Value::ValueSource::~ValueSource() { + asyncUpdater = nullptr; + SharedValueSourceUpdater::releaseIfUnused(); } void Value::ValueSource::sendChangeMessage (const bool synchronous) { - if (synchronous) - { - // (hold a local reference to this object in case it's freed during the callbacks) - const ReferenceCountedObjectPtr localRef (this); + const int numListeners = valuesWithListeners.size(); - for (int i = valuesWithListeners.size(); --i >= 0;) + if (numListeners > 0) + { + if (synchronous) { - Value* const v = valuesWithListeners[i]; + asyncUpdater = nullptr; + const ReferenceCountedObjectPtr localRef (this); - if (v != nullptr) - v->callListeners(); + for (int i = numListeners; --i >= 0;) + if (Value* const v = valuesWithListeners[i]) + v->callListeners(); + } + else if (asyncUpdater == nullptr) + { + SharedValueSourceUpdater* const updater = SharedValueSourceUpdater::getOrCreateSharedUpdater(); + asyncUpdater = updater; + updater->update (this); } } - else - { - if (valuesWithListeners.size() > 0) - triggerAsyncUpdate(); - } -} - -void Value::ValueSource::handleAsyncUpdate() -{ - sendChangeMessage (true); } //============================================================================== diff --git a/modules/juce_data_structures/values/juce_Value.h b/modules/juce_data_structures/values/juce_Value.h index 1fce306e3c..6a4c745351 100644 --- a/modules/juce_data_structures/values/juce_Value.h +++ b/modules/juce_data_structures/values/juce_Value.h @@ -169,8 +169,7 @@ public: 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 SingleThreadedReferenceCountedObject, - private AsyncUpdater + class JUCE_API ValueSource : public SingleThreadedReferenceCountedObject { public: ValueSource(); @@ -196,8 +195,7 @@ public: //============================================================================== friend class Value; SortedSet valuesWithListeners; - - void handleAsyncUpdate(); + ReferenceCountedObjectPtr asyncUpdater; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueSource); }; diff --git a/modules/juce_data_structures/values/juce_ValueTree.cpp b/modules/juce_data_structures/values/juce_ValueTree.cpp index 874cf6453b..e94e86c858 100644 --- a/modules/juce_data_structures/values/juce_ValueTree.cpp +++ b/modules/juce_data_structures/values/juce_ValueTree.cpp @@ -60,11 +60,8 @@ public: void sendPropertyChangeMessage (ValueTree& tree, const Identifier& property) { for (int i = valueTreesWithListeners.size(); --i >= 0;) - { - ValueTree* const v = valueTreesWithListeners[i]; - if (v != nullptr) + if (ValueTree* const v = valueTreesWithListeners[i]) v->listeners.call (&ValueTree::Listener::valueTreePropertyChanged, tree, property); - } } void sendPropertyChangeMessage (const Identifier& property) @@ -78,11 +75,8 @@ public: void sendChildAddedMessage (ValueTree& tree, ValueTree& child) { for (int i = valueTreesWithListeners.size(); --i >= 0;) - { - ValueTree* const v = valueTreesWithListeners[i]; - if (v != nullptr) + if (ValueTree* const v = valueTreesWithListeners[i]) v->listeners.call (&ValueTree::Listener::valueTreeChildAdded, tree, child); - } } void sendChildAddedMessage (ValueTree child) @@ -96,11 +90,8 @@ public: void sendChildRemovedMessage (ValueTree& tree, ValueTree& child) { for (int i = valueTreesWithListeners.size(); --i >= 0;) - { - ValueTree* const v = valueTreesWithListeners[i]; - if (v != nullptr) + if (ValueTree* const v = valueTreesWithListeners[i]) v->listeners.call (&ValueTree::Listener::valueTreeChildRemoved, tree, child); - } } void sendChildRemovedMessage (ValueTree child) @@ -114,11 +105,8 @@ public: void sendChildOrderChangedMessage (ValueTree& tree) { for (int i = valueTreesWithListeners.size(); --i >= 0;) - { - ValueTree* const v = valueTreesWithListeners[i]; - if (v != nullptr) + if (ValueTree* const v = valueTreesWithListeners[i]) v->listeners.call (&ValueTree::Listener::valueTreeChildOrderChanged, tree); - } } void sendChildOrderChangedMessage() @@ -134,18 +122,12 @@ public: ValueTree tree (this); for (int j = children.size(); --j >= 0;) - { - SharedObject* const child = children.getObjectPointer (j); - if (child != nullptr) + if (SharedObject* const child = children.getObjectPointer (j)) child->sendParentChangeMessage(); - } for (int i = valueTreesWithListeners.size(); --i >= 0;) - { - ValueTree* const v = valueTreesWithListeners[i]; - if (v != nullptr) + if (ValueTree* const v = valueTreesWithListeners[i]) v->listeners.call (&ValueTree::Listener::valueTreeParentChanged, tree); - } } const var& getProperty (const Identifier& name) const noexcept @@ -167,9 +149,7 @@ public: } else { - const var* const existingValue = properties.getVarPointer (name); - - if (existingValue != nullptr) + if (const var* const existingValue = properties.getVarPointer (name)) { if (*existingValue != newValue) undoManager->perform (new SetPropertyAction (this, name, newValue, *existingValue, false, false)); @@ -270,16 +250,10 @@ public: bool isAChildOf (const SharedObject* const possibleParent) const noexcept { - const SharedObject* p = parent; - - while (p != nullptr) - { + for (const SharedObject* p = parent; p != nullptr; p = p->parent) if (p == possibleParent) return true; - p = p->parent; - } - return false; } @@ -611,10 +585,9 @@ public: UndoableAction* createCoalescedAction (UndoableAction* nextAction) { - MoveChildAction* next = dynamic_cast (nextAction); - - if (next != nullptr && next->parent == parent && next->startIndex == endIndex) - return new MoveChildAction (parent, startIndex, next->endIndex); + if (MoveChildAction* next = dynamic_cast (nextAction)) + if (next->parent == parent && next->startIndex == endIndex) + return new MoveChildAction (parent, startIndex, next->endIndex); return nullptr; }