Adding a "detent location" to a slider


#1

Hello, JUCEy people.

My GUI is resizable at various levels and involves sliders for continuous parameters like gain, in dB, and time stretch, in %.

Unfortunately, at some sizes of fader it’s impossible to actually use it to set 0.0dB or 100% stretch! - it jumps from -0.1 to 0.1 or 99% to 101%.

There’s nothing at all wrong with the Slider code, the calculations seem to be exactly correct, but this is simply an artefact of division and not quite enough pixels to cover every point in the range of travel. The trouble is that JUCE has no way to know that 0.0dB or 100% are special numbers that you always need to be able to hit on that slider.

Thus the idea of a software detent - if it were a physical dial, there’d be a little click when you got to 100% or 0.0.

On a quick look, changing Slider would be pretty easy. You’d have a new setter and getter for two values, “detent”, which might not be set at all, and “detent width”, which defaults to 1. In the code for computing the “value”, if you are within 1/2 “detent width” of the slider value which would give the detent value, then you return the detent value instead.


#2

I just let my users double click to reset those faders and sliders to the “neutral position”. Much easier than fiddling with detent.


#3

Hmm, on second blush, looks like overriding snapValue will let me do this with little fuss - so it might not be worth adding. However, I think this is an extremely common use case, as every one of my sliders has a logical detent, so it might be worth the special case. You could do it very simply with a default implementation of snapValue…


#4

It is a certainty that even if I implement the double-click idea and document it heavily, I’ll get bug reports saying, “Can’t drag time fader to 100%”.

You must have smarter users than I do (though I love that idea and am in the process of stealing it). Come to think of it, I’m often not smart enough to think of double-clicking something, it’s happened before…


#5

That worked right the first time![code]class DetentSlider : public juce::Slider {
public:
explicit DetentSlider(const String& name = String::empty) :
Slider(name), detentRadius_(1.0f), detentSet_(false) {
}

void setDetent(float d) { detent_ = d; detentSet_ = true; }
void setDetentRadius(float r) { detentRadius_ = r; }
void clearDetent() { detentSet_ = false; }

virtual double snapValue(double value, bool userIsDragging) {
if (!(userIsDragging && detentSet_))
return value;

float d = fabs(getPositionOfValue(value) - getPositionOfValue(detent_));
return (d < detentRadius_) ? detent_ : value;

}

void resetToDetent() {
if (detentSet_)
setValue(detent_);
}

private:
double detent_;
float detentRadius_;
bool detentSet_;

DISALLOW_COPY_AND_ASSIGN(DetentSlider);
};
[/code]