fix macOS build (following Projucer changes made in Windows, which removed /Applications/JUCE/modules from its headers). move JUCE headers under source control, so that Windows and macOS can both build against same version of JUCE. remove AUv3 target (I think it's an iOS thing, so it will never work with this macOS fluidsynth dylib).
This commit is contained in:
@ -0,0 +1,658 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
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 5 End-User License
|
||||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
|
||||
27th April 2017).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-5-licence
|
||||
Privacy Policy: www.juce.com/juce-5-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
|
||||
{
|
||||
|
||||
struct AudioProcessorValueTreeState::Parameter : public AudioProcessorParameterWithID,
|
||||
private ValueTree::Listener
|
||||
{
|
||||
Parameter (AudioProcessorValueTreeState& s,
|
||||
const String& parameterID, const String& paramName, const String& labelText,
|
||||
NormalisableRange<float> r, float defaultVal,
|
||||
std::function<String (float)> valueToText,
|
||||
std::function<float (const String&)> textToValue,
|
||||
bool meta,
|
||||
bool automatable,
|
||||
bool discrete,
|
||||
AudioProcessorParameter::Category category,
|
||||
bool boolean)
|
||||
: AudioProcessorParameterWithID (parameterID, paramName, labelText, category),
|
||||
owner (s), valueToTextFunction (valueToText), textToValueFunction (textToValue),
|
||||
range (r), value (defaultVal), defaultValue (defaultVal),
|
||||
listenersNeedCalling (true),
|
||||
isMetaParam (meta),
|
||||
isAutomatableParam (automatable),
|
||||
isDiscreteParam (discrete),
|
||||
isBooleanParam (boolean)
|
||||
{
|
||||
value = defaultValue;
|
||||
state.addListener (this);
|
||||
}
|
||||
|
||||
~Parameter()
|
||||
{
|
||||
// should have detached all callbacks before destroying the parameters!
|
||||
jassert (listeners.size() <= 1);
|
||||
}
|
||||
|
||||
float getValue() const override { return range.convertTo0to1 (value); }
|
||||
float getDefaultValue() const override { return range.convertTo0to1 (defaultValue); }
|
||||
|
||||
float getValueForText (const String& text) const override
|
||||
{
|
||||
return range.convertTo0to1 (textToValueFunction != nullptr ? textToValueFunction (text)
|
||||
: text.getFloatValue());
|
||||
}
|
||||
|
||||
String getText (float v, int length) const override
|
||||
{
|
||||
return valueToTextFunction != nullptr ? valueToTextFunction (range.convertFrom0to1 (v))
|
||||
: AudioProcessorParameter::getText (v, length);
|
||||
}
|
||||
|
||||
int getNumSteps() const override
|
||||
{
|
||||
if (range.interval > 0)
|
||||
return (static_cast<int> ((range.end - range.start) / range.interval) + 1);
|
||||
|
||||
return AudioProcessor::getDefaultNumParameterSteps();
|
||||
}
|
||||
|
||||
void setValue (float newValue) override
|
||||
{
|
||||
newValue = range.snapToLegalValue (range.convertFrom0to1 (newValue));
|
||||
|
||||
if (value != newValue || listenersNeedCalling)
|
||||
{
|
||||
value = newValue;
|
||||
|
||||
listeners.call ([=] (AudioProcessorValueTreeState::Listener& l) { l.parameterChanged (paramID, value); });
|
||||
listenersNeedCalling = false;
|
||||
|
||||
needsUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
void setNewState (const ValueTree& v)
|
||||
{
|
||||
state = v;
|
||||
updateFromValueTree();
|
||||
}
|
||||
|
||||
void setUnnormalisedValue (float newUnnormalisedValue)
|
||||
{
|
||||
if (value != newUnnormalisedValue)
|
||||
{
|
||||
const float newValue = range.convertTo0to1 (newUnnormalisedValue);
|
||||
setValueNotifyingHost (newValue);
|
||||
}
|
||||
}
|
||||
|
||||
void updateFromValueTree()
|
||||
{
|
||||
setUnnormalisedValue (state.getProperty (owner.valuePropertyID, defaultValue));
|
||||
}
|
||||
|
||||
void copyValueToValueTree()
|
||||
{
|
||||
if (auto* valueProperty = state.getPropertyPointer (owner.valuePropertyID))
|
||||
{
|
||||
if ((float) *valueProperty != value)
|
||||
{
|
||||
ScopedValueSetter<bool> svs (ignoreParameterChangedCallbacks, true);
|
||||
state.setProperty (owner.valuePropertyID, value, owner.undoManager);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state.setProperty (owner.valuePropertyID, value, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier& property) override
|
||||
{
|
||||
if (ignoreParameterChangedCallbacks)
|
||||
return;
|
||||
|
||||
if (property == owner.valuePropertyID)
|
||||
updateFromValueTree();
|
||||
}
|
||||
|
||||
void valueTreeChildAdded (ValueTree&, ValueTree&) override {}
|
||||
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override {}
|
||||
void valueTreeChildOrderChanged (ValueTree&, int, int) override {}
|
||||
void valueTreeParentChanged (ValueTree&) override {}
|
||||
|
||||
static Parameter* getParameterForID (AudioProcessor& processor, StringRef paramID) noexcept
|
||||
{
|
||||
for (auto* ap : processor.getParameters())
|
||||
{
|
||||
// When using this class, you must allow it to manage all the parameters in your AudioProcessor, and
|
||||
// not add any parameter objects of other types!
|
||||
jassert (dynamic_cast<Parameter*> (ap) != nullptr);
|
||||
|
||||
auto* p = static_cast<Parameter*> (ap);
|
||||
|
||||
if (paramID == p->paramID)
|
||||
return p;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool isMetaParameter() const override { return isMetaParam; }
|
||||
bool isAutomatable() const override { return isAutomatableParam; }
|
||||
bool isDiscrete() const override { return isDiscreteParam; }
|
||||
bool isBoolean() const override { return isBooleanParam; }
|
||||
|
||||
AudioProcessorValueTreeState& owner;
|
||||
ValueTree state;
|
||||
ListenerList<AudioProcessorValueTreeState::Listener> listeners;
|
||||
std::function<String (float)> valueToTextFunction;
|
||||
std::function<float (const String&)> textToValueFunction;
|
||||
NormalisableRange<float> range;
|
||||
float value, defaultValue;
|
||||
std::atomic<bool> needsUpdate { true };
|
||||
bool listenersNeedCalling;
|
||||
const bool isMetaParam, isAutomatableParam, isDiscreteParam, isBooleanParam;
|
||||
bool ignoreParameterChangedCallbacks = false;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Parameter)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
AudioProcessorValueTreeState::AudioProcessorValueTreeState (AudioProcessor& p, UndoManager* um)
|
||||
: processor (p), undoManager (um)
|
||||
{
|
||||
startTimerHz (10);
|
||||
state.addListener (this);
|
||||
}
|
||||
|
||||
AudioProcessorValueTreeState::~AudioProcessorValueTreeState() {}
|
||||
|
||||
AudioProcessorParameterWithID* AudioProcessorValueTreeState::createAndAddParameter (const String& paramID, const String& paramName,
|
||||
const String& labelText, NormalisableRange<float> r,
|
||||
float defaultVal, std::function<String (float)> valueToTextFunction,
|
||||
std::function<float (const String&)> textToValueFunction,
|
||||
bool isMetaParameter,
|
||||
bool isAutomatableParameter,
|
||||
bool isDiscreteParameter,
|
||||
AudioProcessorParameter::Category category,
|
||||
bool isBooleanParameter)
|
||||
{
|
||||
// All parameters must be created before giving this manager a ValueTree state!
|
||||
jassert (! state.isValid());
|
||||
|
||||
Parameter* p = new Parameter (*this, paramID, paramName, labelText, r,
|
||||
defaultVal, valueToTextFunction, textToValueFunction,
|
||||
isMetaParameter, isAutomatableParameter,
|
||||
isDiscreteParameter, category, isBooleanParameter);
|
||||
processor.addParameter (p);
|
||||
return p;
|
||||
}
|
||||
|
||||
void AudioProcessorValueTreeState::addParameterListener (StringRef paramID, Listener* listener)
|
||||
{
|
||||
if (Parameter* p = Parameter::getParameterForID (processor, paramID))
|
||||
p->listeners.add (listener);
|
||||
}
|
||||
|
||||
void AudioProcessorValueTreeState::removeParameterListener (StringRef paramID, Listener* listener)
|
||||
{
|
||||
if (Parameter* p = Parameter::getParameterForID (processor, paramID))
|
||||
p->listeners.remove (listener);
|
||||
}
|
||||
|
||||
Value AudioProcessorValueTreeState::getParameterAsValue (StringRef paramID) const
|
||||
{
|
||||
if (Parameter* p = Parameter::getParameterForID (processor, paramID))
|
||||
return p->state.getPropertyAsValue (valuePropertyID, undoManager);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
NormalisableRange<float> AudioProcessorValueTreeState::getParameterRange (StringRef paramID) const noexcept
|
||||
{
|
||||
if (Parameter* p = Parameter::getParameterForID (processor, paramID))
|
||||
return p->range;
|
||||
|
||||
return NormalisableRange<float>();
|
||||
}
|
||||
|
||||
AudioProcessorParameterWithID* AudioProcessorValueTreeState::getParameter (StringRef paramID) const noexcept
|
||||
{
|
||||
return Parameter::getParameterForID (processor, paramID);
|
||||
}
|
||||
|
||||
float* AudioProcessorValueTreeState::getRawParameterValue (StringRef paramID) const noexcept
|
||||
{
|
||||
if (Parameter* p = Parameter::getParameterForID (processor, paramID))
|
||||
return &(p->value);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ValueTree AudioProcessorValueTreeState::copyState()
|
||||
{
|
||||
ScopedLock lock (valueTreeChanging);
|
||||
|
||||
flushParameterValuesToValueTree();
|
||||
|
||||
return state.createCopy();
|
||||
}
|
||||
|
||||
void AudioProcessorValueTreeState::replaceState (const ValueTree& newState)
|
||||
{
|
||||
ScopedLock lock (valueTreeChanging);
|
||||
|
||||
state = newState;
|
||||
|
||||
if (undoManager != nullptr)
|
||||
undoManager->clearUndoHistory();
|
||||
}
|
||||
|
||||
ValueTree AudioProcessorValueTreeState::getOrCreateChildValueTree (const String& paramID)
|
||||
{
|
||||
ValueTree v (state.getChildWithProperty (idPropertyID, paramID));
|
||||
|
||||
if (! v.isValid())
|
||||
{
|
||||
v = ValueTree (valueType);
|
||||
v.setProperty (idPropertyID, paramID, nullptr);
|
||||
state.appendChild (v, nullptr);
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void AudioProcessorValueTreeState::updateParameterConnectionsToChildTrees()
|
||||
{
|
||||
ScopedLock lock (valueTreeChanging);
|
||||
|
||||
for (auto* param : processor.getParameters())
|
||||
{
|
||||
jassert (dynamic_cast<Parameter*> (param) != nullptr);
|
||||
auto* p = static_cast<Parameter*> (param);
|
||||
|
||||
p->setNewState (getOrCreateChildValueTree (p->paramID));
|
||||
}
|
||||
}
|
||||
|
||||
void AudioProcessorValueTreeState::valueTreePropertyChanged (ValueTree& tree, const Identifier& property)
|
||||
{
|
||||
if (property == idPropertyID && tree.hasType (valueType) && tree.getParent() == state)
|
||||
updateParameterConnectionsToChildTrees();
|
||||
}
|
||||
|
||||
void AudioProcessorValueTreeState::valueTreeChildAdded (ValueTree& parent, ValueTree& tree)
|
||||
{
|
||||
if (parent == state && tree.hasType (valueType))
|
||||
if (auto* param = Parameter::getParameterForID (processor, tree.getProperty (idPropertyID).toString()))
|
||||
param->setNewState (getOrCreateChildValueTree (param->paramID));
|
||||
}
|
||||
|
||||
void AudioProcessorValueTreeState::valueTreeChildRemoved (ValueTree& parent, ValueTree& tree, int)
|
||||
{
|
||||
if (parent == state && tree.hasType (valueType))
|
||||
if (auto* param = Parameter::getParameterForID (processor, tree.getProperty (idPropertyID).toString()))
|
||||
param->setNewState (getOrCreateChildValueTree (param->paramID));
|
||||
}
|
||||
|
||||
void AudioProcessorValueTreeState::valueTreeRedirected (ValueTree& v)
|
||||
{
|
||||
if (v == state)
|
||||
updateParameterConnectionsToChildTrees();
|
||||
}
|
||||
|
||||
void AudioProcessorValueTreeState::valueTreeChildOrderChanged (ValueTree&, int, int) {}
|
||||
void AudioProcessorValueTreeState::valueTreeParentChanged (ValueTree&) {}
|
||||
|
||||
bool AudioProcessorValueTreeState::flushParameterValuesToValueTree()
|
||||
{
|
||||
ScopedLock lock (valueTreeChanging);
|
||||
|
||||
bool anythingUpdated = false;
|
||||
|
||||
for (auto* ap : processor.getParameters())
|
||||
{
|
||||
jassert (dynamic_cast<Parameter*> (ap) != nullptr);
|
||||
auto* p = static_cast<Parameter*> (ap);
|
||||
|
||||
bool needsUpdateTestValue = true;
|
||||
|
||||
if (p->needsUpdate.compare_exchange_strong (needsUpdateTestValue, false))
|
||||
{
|
||||
p->copyValueToValueTree();
|
||||
anythingUpdated = true;
|
||||
}
|
||||
}
|
||||
|
||||
return anythingUpdated;
|
||||
}
|
||||
|
||||
void AudioProcessorValueTreeState::timerCallback()
|
||||
{
|
||||
auto anythingUpdated = flushParameterValuesToValueTree();
|
||||
|
||||
startTimer (anythingUpdated ? 1000 / 50
|
||||
: jlimit (50, 500, getTimerInterval() + 20));
|
||||
}
|
||||
|
||||
AudioProcessorValueTreeState::Listener::Listener() {}
|
||||
AudioProcessorValueTreeState::Listener::~Listener() {}
|
||||
|
||||
//==============================================================================
|
||||
struct AttachedControlBase : public AudioProcessorValueTreeState::Listener,
|
||||
public AsyncUpdater
|
||||
{
|
||||
AttachedControlBase (AudioProcessorValueTreeState& s, const String& p)
|
||||
: state (s), paramID (p), lastValue (0)
|
||||
{
|
||||
state.addParameterListener (paramID, this);
|
||||
}
|
||||
|
||||
void removeListener()
|
||||
{
|
||||
state.removeParameterListener (paramID, this);
|
||||
}
|
||||
|
||||
void setNewUnnormalisedValue (float newUnnormalisedValue)
|
||||
{
|
||||
if (AudioProcessorParameter* p = state.getParameter (paramID))
|
||||
{
|
||||
const float newValue = state.getParameterRange (paramID)
|
||||
.convertTo0to1 (newUnnormalisedValue);
|
||||
|
||||
if (p->getValue() != newValue)
|
||||
p->setValueNotifyingHost (newValue);
|
||||
}
|
||||
}
|
||||
|
||||
void sendInitialUpdate()
|
||||
{
|
||||
if (float* v = state.getRawParameterValue (paramID))
|
||||
parameterChanged (paramID, *v);
|
||||
}
|
||||
|
||||
void parameterChanged (const String&, float newValue) override
|
||||
{
|
||||
lastValue = newValue;
|
||||
|
||||
if (MessageManager::getInstance()->isThisTheMessageThread())
|
||||
{
|
||||
cancelPendingUpdate();
|
||||
setValue (newValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void beginParameterChange()
|
||||
{
|
||||
if (AudioProcessorParameter* p = state.getParameter (paramID))
|
||||
{
|
||||
if (state.undoManager != nullptr)
|
||||
state.undoManager->beginNewTransaction();
|
||||
|
||||
p->beginChangeGesture();
|
||||
}
|
||||
}
|
||||
|
||||
void endParameterChange()
|
||||
{
|
||||
if (AudioProcessorParameter* p = state.getParameter (paramID))
|
||||
p->endChangeGesture();
|
||||
}
|
||||
|
||||
void handleAsyncUpdate() override
|
||||
{
|
||||
setValue (lastValue);
|
||||
}
|
||||
|
||||
virtual void setValue (float) = 0;
|
||||
|
||||
AudioProcessorValueTreeState& state;
|
||||
String paramID;
|
||||
float lastValue;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AttachedControlBase)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct AudioProcessorValueTreeState::SliderAttachment::Pimpl : private AttachedControlBase,
|
||||
private Slider::Listener
|
||||
{
|
||||
Pimpl (AudioProcessorValueTreeState& s, const String& p, Slider& sl)
|
||||
: AttachedControlBase (s, p), slider (sl), ignoreCallbacks (false)
|
||||
{
|
||||
NormalisableRange<float> range (state.getParameterRange (paramID));
|
||||
|
||||
if (range.interval != 0 || range.skew != 0)
|
||||
{
|
||||
slider.setRange (range.start, range.end, range.interval);
|
||||
slider.setSkewFactor (range.skew, range.symmetricSkew);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto convertFrom0To1Function = [range] (double currentRangeStart,
|
||||
double currentRangeEnd,
|
||||
double normalisedValue) mutable
|
||||
{
|
||||
range.start = (float) currentRangeStart;
|
||||
range.end = (float) currentRangeEnd;
|
||||
return (double) range.convertFrom0to1 ((float) normalisedValue);
|
||||
};
|
||||
|
||||
auto convertTo0To1Function = [range] (double currentRangeStart,
|
||||
double currentRangeEnd,
|
||||
double mappedValue) mutable
|
||||
{
|
||||
range.start = (float) currentRangeStart;
|
||||
range.end = (float) currentRangeEnd;
|
||||
return (double) range.convertTo0to1 ((float) mappedValue);
|
||||
};
|
||||
|
||||
auto snapToLegalValueFunction = [range] (double currentRangeStart,
|
||||
double currentRangeEnd,
|
||||
double valueToSnap) mutable
|
||||
{
|
||||
range.start = (float) currentRangeStart;
|
||||
range.end = (float) currentRangeEnd;
|
||||
return (double) range.snapToLegalValue ((float) valueToSnap);
|
||||
};
|
||||
|
||||
slider.setNormalisableRange ({ (double) range.start, (double) range.end,
|
||||
convertFrom0To1Function,
|
||||
convertTo0To1Function,
|
||||
snapToLegalValueFunction });
|
||||
}
|
||||
|
||||
if (auto* param = dynamic_cast<AudioProcessorValueTreeState::Parameter*> (state.getParameter (paramID)))
|
||||
{
|
||||
if (param->textToValueFunction != nullptr)
|
||||
slider.valueFromTextFunction = [param] (const String& text) { return (double) param->textToValueFunction (text); };
|
||||
|
||||
if (param->valueToTextFunction != nullptr)
|
||||
slider.textFromValueFunction = [param] (double value) { return param->valueToTextFunction ((float) value); };
|
||||
|
||||
slider.setDoubleClickReturnValue (true, range.convertFrom0to1 (param->getDefaultValue()));
|
||||
}
|
||||
|
||||
sendInitialUpdate();
|
||||
slider.addListener (this);
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
slider.removeListener (this);
|
||||
removeListener();
|
||||
}
|
||||
|
||||
void setValue (float newValue) override
|
||||
{
|
||||
const ScopedLock selfCallbackLock (selfCallbackMutex);
|
||||
|
||||
{
|
||||
ScopedValueSetter<bool> svs (ignoreCallbacks, true);
|
||||
slider.setValue (newValue, sendNotificationSync);
|
||||
}
|
||||
}
|
||||
|
||||
void sliderValueChanged (Slider* s) override
|
||||
{
|
||||
const ScopedLock selfCallbackLock (selfCallbackMutex);
|
||||
|
||||
if ((! ignoreCallbacks) && (! ModifierKeys::currentModifiers.isRightButtonDown()))
|
||||
setNewUnnormalisedValue ((float) s->getValue());
|
||||
}
|
||||
|
||||
void sliderDragStarted (Slider*) override { beginParameterChange(); }
|
||||
void sliderDragEnded (Slider*) override { endParameterChange(); }
|
||||
|
||||
Slider& slider;
|
||||
bool ignoreCallbacks;
|
||||
CriticalSection selfCallbackMutex;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
||||
};
|
||||
|
||||
AudioProcessorValueTreeState::SliderAttachment::SliderAttachment (AudioProcessorValueTreeState& s, const String& p, Slider& sl)
|
||||
: pimpl (new Pimpl (s, p, sl))
|
||||
{
|
||||
}
|
||||
|
||||
AudioProcessorValueTreeState::SliderAttachment::~SliderAttachment() {}
|
||||
|
||||
//==============================================================================
|
||||
struct AudioProcessorValueTreeState::ComboBoxAttachment::Pimpl : private AttachedControlBase,
|
||||
private ComboBox::Listener
|
||||
{
|
||||
Pimpl (AudioProcessorValueTreeState& s, const String& p, ComboBox& c)
|
||||
: AttachedControlBase (s, p), combo (c), ignoreCallbacks (false)
|
||||
{
|
||||
sendInitialUpdate();
|
||||
combo.addListener (this);
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
combo.removeListener (this);
|
||||
removeListener();
|
||||
}
|
||||
|
||||
void setValue (float newValue) override
|
||||
{
|
||||
const ScopedLock selfCallbackLock (selfCallbackMutex);
|
||||
|
||||
{
|
||||
ScopedValueSetter<bool> svs (ignoreCallbacks, true);
|
||||
combo.setSelectedItemIndex (roundToInt (newValue), sendNotificationSync);
|
||||
}
|
||||
}
|
||||
|
||||
void comboBoxChanged (ComboBox* comboBox) override
|
||||
{
|
||||
const ScopedLock selfCallbackLock (selfCallbackMutex);
|
||||
|
||||
if (! ignoreCallbacks)
|
||||
{
|
||||
beginParameterChange();
|
||||
setNewUnnormalisedValue ((float) comboBox->getSelectedId() - 1.0f);
|
||||
endParameterChange();
|
||||
}
|
||||
}
|
||||
|
||||
ComboBox& combo;
|
||||
bool ignoreCallbacks;
|
||||
CriticalSection selfCallbackMutex;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
||||
};
|
||||
|
||||
AudioProcessorValueTreeState::ComboBoxAttachment::ComboBoxAttachment (AudioProcessorValueTreeState& s, const String& p, ComboBox& c)
|
||||
: pimpl (new Pimpl (s, p, c))
|
||||
{
|
||||
}
|
||||
|
||||
AudioProcessorValueTreeState::ComboBoxAttachment::~ComboBoxAttachment() {}
|
||||
|
||||
//==============================================================================
|
||||
struct AudioProcessorValueTreeState::ButtonAttachment::Pimpl : private AttachedControlBase,
|
||||
private Button::Listener
|
||||
{
|
||||
Pimpl (AudioProcessorValueTreeState& s, const String& p, Button& b)
|
||||
: AttachedControlBase (s, p), button (b), ignoreCallbacks (false)
|
||||
{
|
||||
sendInitialUpdate();
|
||||
button.addListener (this);
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
button.removeListener (this);
|
||||
removeListener();
|
||||
}
|
||||
|
||||
void setValue (float newValue) override
|
||||
{
|
||||
const ScopedLock selfCallbackLock (selfCallbackMutex);
|
||||
|
||||
{
|
||||
ScopedValueSetter<bool> svs (ignoreCallbacks, true);
|
||||
button.setToggleState (newValue >= 0.5f, sendNotificationSync);
|
||||
}
|
||||
}
|
||||
|
||||
void buttonClicked (Button* b) override
|
||||
{
|
||||
const ScopedLock selfCallbackLock (selfCallbackMutex);
|
||||
|
||||
if (! ignoreCallbacks)
|
||||
{
|
||||
beginParameterChange();
|
||||
setNewUnnormalisedValue (b->getToggleState() ? 1.0f : 0.0f);
|
||||
endParameterChange();
|
||||
}
|
||||
}
|
||||
|
||||
Button& button;
|
||||
bool ignoreCallbacks;
|
||||
CriticalSection selfCallbackMutex;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
||||
};
|
||||
|
||||
AudioProcessorValueTreeState::ButtonAttachment::ButtonAttachment (AudioProcessorValueTreeState& s, const String& p, Button& b)
|
||||
: pimpl (new Pimpl (s, p, b))
|
||||
{
|
||||
}
|
||||
|
||||
AudioProcessorValueTreeState::ButtonAttachment::~ButtonAttachment() {}
|
||||
|
||||
} // namespace juce
|
Reference in New Issue
Block a user