Hello,
I am making a mixer in which I want to have the ability to select multiple sliders at once, and have them all move simultaneously when one of the selected is dragged. I have the selection successfully implemented (I can mute/solo multiple sliders at once), but am stuck on moving multiple sliders with one drag. Does anyone know what would be the best way? I’m thinking of using listeners but am not sure how, also considering just making a custom listener that I can then add to the sliders.
If it was me, I would make an object ‘in charge’ of syncing the sliders, who would get notified in the sliders’ mouse events and update the state of it’s ‘managed’ sliders. That’s an intuitive way for me, but there are plenty of other ways and some might not like adding extra objects like that.
How did you implement linking the mute/solo? It might make sense to link the sliders in a manner consistent with that.
So I’m not sure that this is the best approach.
class FlagSlider : public juce::Component, public juce::Slider::Listener
{
public:
FlagSlider (juce::AudioProcessorValueTreeState& vts, const juce::String& minParamId, const juce::String& maxParamId)
: valueTreeState (vts),
minParamId (minParamId),
maxParamId (maxParamId),
minSliderAttachment (valueTreeState, minParamId, minSlider),
maxSliderAttachment (valueTreeState, maxParamId, maxSlider)
{
configureSliders();
}
void resized() override { rangeSlider.setBounds (getLocalBounds().reduced (10)); }
void sliderValueChanged (juce::Slider* slider) override
{
if (slider == &rangeSlider)
{
minSlider.setValue (rangeSlider.getMinValue(), juce::NotificationType::sendNotification);
maxSlider.setValue (rangeSlider.getMaxValue(), juce::NotificationType::sendNotification);
}
else if (slider == &minSlider)
{
rangeSlider.setMinValue (minSlider.getValue(), juce::NotificationType::sendNotification);
}
else if (slider == &maxSlider)
{
rangeSlider.setMaxValue (maxSlider.getValue(), juce::NotificationType::sendNotification);
}
}
private:
void configureSliders()
{
rangeSlider.setSliderStyle (juce::Slider::TwoValueHorizontal);
rangeSlider.setTextBoxStyle (juce::Slider::NoTextBox, false, 0, 0);
// Set the range based on the parameters
auto minRange = valueTreeState.getParameter (minParamId)->getNormalisableRange().start;
auto maxRange = valueTreeState.getParameter (maxParamId)->getNormalisableRange().end;
rangeSlider.setRange (minRange, maxRange);
rangeSlider.setMinAndMaxValues (minSlider.getValue(), maxSlider.getValue(), juce::NotificationType::dontSendNotification);
rangeSlider.addListener (this);
addAndMakeVisible (rangeSlider);
minSlider.addListener(this);
maxSlider.addListener(this);
}
juce::Slider rangeSlider;
juce::Slider minSlider;
juce::Slider maxSlider;
juce::AudioProcessorValueTreeState& valueTreeState;
juce::String minParamId;
juce::String maxParamId;
juce::AudioProcessorValueTreeState::SliderAttachment minSliderAttachment;
juce::AudioProcessorValueTreeState::SliderAttachment maxSliderAttachment;
};
This class seems to occasionally cause issues when sliderValueChanged is triggered. I’ve not been able to replicate them myself, but the plugin consistently crashes when attempts are made to move the slider on some systems.
I think a better thing to do would be to have a single slider with two textboxes and its own custom attachment, handled by some sort of AttachmentManager.
This sounds like thinking about the problem backwards. Your ui should be reflecting the data, be it by polling with a timer, vblankattachment, whatever. In your callback to adjust the sliders with a drag you can do the check and update the underlying data in that callback, and the ui will respond accordingly.
Bonus points if you ensure thread safety / no race conditions when doing so.
Sometimes adding the sliders as mouse listeners to each other can achieve the desired results. Then it’s as if they all were dragged. If you need to perform extra logic this falls apart pretty quickly, though.
