Hi all, I’m trying to draw a slider with a big thumb by overriding
getSliderThumbRadius in my L&F.
The problem is that the thumb gets clipped by the component bounds:
This happens when the space between the top (bottom) and the track start (finish) is greater than the radius.
I tried tweaking things in my L&F’s
drawLinearSlider (which started as a copy from
juce::LookAndFeel_V4) but with no luck - I was able to change the visible length of the track, but the actual behaviour of the slider didn’t change i.e. I was still able to drag the thumb all the way down as in the screenshot above.
Am I missing something?
This topic was discussed in this old post (Slider thumb get top/bottom clipped by component boundaries) but I’m not sure that’s helping right now (I am overriding
This can only work if you make the bounds for the slider bigger.
You could use the setPaintingIsUnclipped() flag which would allow to write over the bounds of the component. But then the background might not be updated when the slider is repainted. So you will see artifacts.
The other drawback is that hitTest also is only called within the bounds, so the parts of the thumb overlapping would be not reacting as well.
Like I said the best solution is to increase the slider bounds and make the track slimmer in turn to keep the desired appearance.
You should use
auto bounds = getLocalBounds().reduced(x);
auto newY = y + (x);
auto newHeight = height - (2 * x);
Using x as the padding amount
Reducing the Slider’s bounds will have the opposite effect, the slider will adapt and be even smaller and still not be able to paint over the component borders.
But that brought me to another idea, have a look at the SliderLayout.
Those bounds are not component bounds but the bounds the slider shall be drawn inside. So if you increase the component bounds but reduce the sliderBounds in this struct by the same amount, your thumb will have a better chance to not be cut off.
You change that by overriding getSliderLayout() in the lookAndFeel.
Hope that helps
Thanks a lot for the suggestions -
SliderLayout seems to be the way to go!
I still have an issue: despite overriding
getSliderLayout() in my Look&Feel, the override is ignored and
juce::LookAndFeel_V4::getSliderLayout() is called instead.
By looking at the debugger I realised that
Slider::resized (which is the only user of
getSliderLayout()) gets called before the custom Look&Feel is set; then, when the Look&Feel is changed, the components are repainted, but not resized.
What’s the best way of fixing that?
My code is structured as follows:
class MyEditor : public AudioProcessorEditor
Slider slider1, slider2, ...;
One option I’m thinking would be using
std::unique_ptr<Slider> instead of
Slider and constructing them in
MyEditor::MyEditor after the call to
setLookAndFeel … but maybe there’s something better?
Maybe just put a call to
resized(), as the line after
The situation sounds familiar, I’ll have to look tomorrow to see what I’ve done in my code before…
There are a few things that are strange:
- When you change the lookAndFeel, the Slider should receive a lookAndFeelChanged() which in turn calls resized() at the end of the function. I couldn’t spot an early return.
- When you call setSize() of your editor, it should be the last call, because that will trigger the resized() and place all child components correctly.
Some example code puts all setBounds() in the constructor. I would advise never to do that, because if you resize for any reason, the resized() is called and that should do the layout of the children.
The last thing you want is an inconsistency between initial layout and a subsequent call to resized().
And ofc… you don’t want to implement stuff twice.
- make sure you don’t inherit Slider but if you do that you didn’t override lookAndFeelChanged()
- put the slider.setBounds() calls in resized
- call the editors setSize last, but at least after setLookAndFeel() and all the addAndMakeVisible calls
Hope that helps
i’d just make the thumb as big as possible. so basically std::min(getWidth(), getHeight()) / 2 as a radius, you know? then i’d draw this middle line in a way that it starts at radius and ends at getHeight() - radius, so that when you are the min and max-values the thumb is just barely touching the end of the component. (it might be that you have to reduce the thumb’s radius by 1pxl for this to work or something like that.) then you can just define the size of it with setBounds() like you’d do normally with all components. i think i personally wouldn’t use a solution with drawing outside of the bounds, or using look and feel tricks for this, because the more your project grows the more you’ll be thankful that all components more or less work the same as much as possible when re-reading your code
This was my problem! I was calling
The best part is that I don’t even need to override
getSliderLayout - it just works with the base class’ implementation - given a thumb size (set via
getSliderThumbRadius) it will do the right thing.
The problem was that
paint was getting the custom (bigger) thumb size, while
resized was getting the default (smaller) one. Now that they both get the right value it works just fine.
Thanks everyone for your input!