Movable (draggable) rectangular component

I am looking to make a component draggable within the confines of a parent component, but not resizable, and ideally constrained dragging only horizontally, as these components would be representations of events that happen in time. I’ve scanned a lot of forum topics and documentation, but I haven’t been able to find the answer I’m looking for. Apologies, if this is something ridiculously simple, but it’s eluding me.

Derive your Component from ComponentDragger and use a ComponentBoundsConstrainer.

Rail

We regularly get people asking questions along the lines of “how do I constrain my GUI component when it’s being dragged” and TBH in almost every case they’re asking the wrong question.

There are actually very few use-cases where it’s the GUI that needs to be constrained.

In almost every case where someone asks this, it turns out that they should be constraining their data model instead. Your GUI should always reflect the position of your model, so its position should just take care of itself.

1 Like

Thank you Rail and Jules. I’m making some progress here. Funny thing is, I just made the mental connection that you two must be the same Pro Tools expert forum contributors I remember from over a decade ago when that was also my profession. I’ve also since moved into software development, except I’ve mostly focused on video games up until now. Juce is an excellent piece of work.

I’ve developed this “curveEditor” widget, which is currently not linked to any parameters or audio processing. I’m honestly not sure how to link the two. @jules are you recommending that the mouseDown/Drags shown in this gif should be modifying the AudioProcessorParameters(not the widgets under the cursor), and the curveEditor should be polling the parameters every 50ms and redrawing accordingly?

1 Like

Yes, exactly. In something like this your GUI would always just mirror changes to the model.

1 Like

The GUI needs be able to do this anyway in order to properly support state loading and undo/redo.

1 Like

@jules @Achder Doesn’t that kind of go against the whole SliderAttachment design?

//start here
state.addParameterListener (paramID, this); //'this' is AttachedControlBase
slider.addListener (this); //'this' is SliderAttachment::Pimpl //

    void sliderValueChanged (Slider* s) override //SliderAttachment::Pimpl
    {
        const ScopedLock selfCallbackLock (selfCallbackMutex);

        if ((! ignoreCallbacks) && (! ModifierKeys::currentModifiers.isRightButtonDown()))
            setNewDenormalisedValue ((float) s->getValue()); //then here when you drag the slider
    }

    void setNewDenormalisedValue (float newDenormalisedValue) //AttachedControlBase::
    {
        if (auto* p = state.getParameter (paramID))
        {
            const float newValue = state.getParameterRange (paramID)
                                        .convertTo0to1 (newDenormalisedValue);

            if (p->getValue() != newValue)
                p->setValueNotifyingHost (newValue); //then here, which fires AttachedControlBase::parameterChanged
        }
    }
    void parameterChanged (const String&, float newValue) override //AttachedControlBase::
    {
        lastValue = newValue;

        if (MessageManager::getInstance()->isThisTheMessageThread())
        {
            cancelPendingUpdate();
            setValue (newValue); //then here
        }
        else
        {
            triggerAsyncUpdate();
        }
    }

    void setValue (float newValue) override //SliderAttachment::
    {
        const ScopedLock selfCallbackLock (selfCallbackMutex);

        {
            ScopedValueSetter<bool> svs (ignoreCallbacks, true);
            slider.setValue (newValue, sendNotificationSync); //finally here
        }
    }

This shows that when you drag the slider, the slider is directly modifying the parameter, you’re not modifying the parameter first, and then the slider is polling the parameter at a fixed frequency.
Am I missing something?