Slider text box ignores look and feel if default parameters are used


#1

Hi guys,

I think i have found an issue with the Slider text box ignoring look and feel if the default text box position or size are used. I created a new project to test this and was able to reproduce the issue.

In short… in my LookAndFeel, I do something like…

setColour (Slider::ColourIds::textBoxBackgroundColourId, Colours::hotpink);

And then in my Component class I create a slider…

Slider testSlider;

in the constructor I add it…

addAndMakeVisible (testSlider);

and then I draw it. But the hotpink colour is ignored (I did apply the LookAndFeel, of course).

Then if I change the text box style or size away from default values it will become hotpink, as per my (pretend) look and feel…e.g. using

testSlider.setTextBoxStyle (Slider::TextBoxLeft, false, 81, 20);

or

testSlider.setTextBoxStyle (Slider::TextBoxRight, false, 80, 20);

But then, to demonstrate the issue, I just apply the default parameters and build again,

testSlider.setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20);

and it ignores the look and feel.

I think this is caused by setTextBoxStyle() in juce_Slider.cpp having an if statement that checks if nothing has changed and if so, doesn’t call lookAndFeelChanged(). That makes sense for new updates, but perhaps something like this is happening for the initialisation of the Slider and that is stopping it from applying the look and feel?

(Of course, I can fix this in several ways myself by applying colours or calling lookAndFeelChanged() directly, but one would expect the default slider text box to use the look and feel colours set in the look and feel class)

Thanks!

Adam


#2

Hi Adam,

Are you calling setDefaultLookAndFeel before you are creating the slider? lookAndFeelChanged() is called in the Slider’s constructor.


#3

Hi Tom,

Thanks for getting back to me. :). I call…

‘LookAndFeel::setDefaultLookAndFeel (&myLookAndFeel);’

as the first line in my constructor. Should that do it?

Thanks!

Adam


#4

If you have

as a member variable then it will be constructed before the constructor of the parent class. A quick way of getting around this is to have

ScopedPointer<Slider> testSlider;

as a member variable and do

testSlider = new Slider()

in the constructor.


#5

Hi Tom,

Thanks for getting back to me. Yes, I figured maybe this was the suggestion
to fix it - but isn’t this a bit unsatisfactory? The situation seems to be
that I call setDefaultLookAndFeel() but my Slider text boxes only take on
that look and feel if…

a) I use a setting that is not the default for the slider

or

b) I use a pointer to a Slider, rather than keeping Slider in my class, for
no good programming reason other than to deal with the look and feel not
being applied in the default case.

It definitely feels like I have to work a bit harder than necessary to
achieve something quite simple (that works in all other cases). I don’t
know what the fix is here, but I wanted to flag it up.

Cheers!

Adam


#6

It’s simpler than that - as long as you call setDefaultLookAndFeel() before creating the Slider then you will be fine. The other way of doing it is to call the global setDefaultLookAndFeel() function before you instantiate your class containing the Slider members.


#7

Hi Tom,

I guess I’m banging an increasingly niche drum, but I definitely feel like
when I set the look and feel at the beginning of the constructor of my top
level component, my components should look the way I have specified them
to. I under that means the look and feel call happens after the slider
constructor, but I shouldn’t be forced to implement certain sliders as
pointers or have a wrapper gui class or something like that. The look and
feel definitions should set out how the app should look.

And if I am wrong, why do components with non-default parameters take up
the look and feel while others don’t? It is quite clearly inconsistent on
that point (and, of course, amazing everywhere else or I wouldn’t be
bothering pointing this out )

Cheers!

Adam


#8

You don’t need a wrapper GUI class - just fire off a call to setDefaultLookAndFeel() as the first thing your main() function does. It’s a global function - calling it in the constructor of the top level component doesn’t mean it’s bound in any way to your top level class, other than ensuring it is executed after the members are constructed. This behaviour is from the C++ language, rather than JUCE.

When you call it in the constructor the following sequence of events happen:

  • you create a Slider with the default appearance
  • you define how all Sliders should look
  • you make a change to a Slider’s appearance and the new look is applied

In an ideal world there might be an efficient and clean way of inserting an extra bullet point between the last two

  • automatically search the entire project for all preexisting Sliders and apply the new look

but I hope you can see that any real implementation of this isn’t desirable in most cases. When you create or change the appearance of a Slider after you’ve set the look and feel then you pick up the new appearance. There’s no inconsistency, just an unfortunate timing.


#9

The reason I ran into this (I’ve been using JUCE for years) is that I’m
building a plug-in, so I have no main function, so I can’t set the look and
feel there. I understand it is a feature of the c++ language and to do with
the order of execution.

I’m just suggesting there is a better way because at present, all my
sliders show the correct look and feel except the ones that have exactly
the default parameters. Whatever the procedural reasons for this, and
however logical they are, this is clearly a counterintuitive result. The
look and feel should affect all my components - particularly when I
implement it at the earliest possible point in my code.

The reason I say it is inconsistent is purely an experience of the library

  • I load my plug-in with a bunch of sliders, the ones with default
    parameters don’t show the look and feel and the others do. That is
    inconsistent and counterintuitive to any programmer, and as far as I am
    aware, in a plug-in I have no option to amend this except to make all my
    sliders pointers and instantiate them after I set the look and feel.

I’m sure there is some workaround whereby the look and feel gets applied
properly.

Thanks,

Adam


#10

Your constructor isn’t the earliest possible point in your code - as evidenced by the Sliders :slight_smile: One option is to do

struct LookAndFeelLoader
{
    LookAndFeelLoader () { LookAndFeel::setDefaultLookAndFeel (&lf); }
    YourLookAndFeel lf;
};
LookAndFeelLoader lfl;

Slider testSlider1;
Slider testSlider2;

Another option is to set a global variable that calls setDefaultLookAndFeel in its constructor.

Thank you for being persistent! If you’ve run into this then there will probably be others that didn’t take the time to share their experience on the forum.


#11

Ok, great, I’ll give that a go. Thanks for your feedback Tom!

Adam :slight_smile:


#12

So many details to pay attention to. This thread was a life saver, I thought I was doing something really stupid for I could never get the colors I set for the sliders. Thanks for both the exposition and the solution.

Also, maybe this situation could make its way into some tutorial.


#13

@t0m surely calling setDefautLookAndFeel should result in lookAndFeelChanged being called in the slider, thus maintaining the expected behaviour.


#14

Not quite. setDefautLookAndFeel starts from the top level and works its way down the component hierarchy. If we’re calling it from the constructor of a component we want to change then we have no chance - the component isn’t even constructed yet, let alone added to a parent component.

Reading this thread again, a simpler solution could be to just call LookAndFeel::setDefaultLookAndFeel (&lf) as the first thing in your main function, if you have a standalone app.