Slider::textBoxTextColourId look-and-feel overridden by properties


#1

I added a LookAndFeel subclass to my project, and everything worked except the Slider TextBox, which kept its original text color. I drilled down in Xcode and discovered that the L&F was not being accessed until I added the “#if 0” block below:

Colour Component::findColour (int colourID, bool inheritFromParent) const
{

#if 0 // JOS HACK
if (auto* v = properties.getVarPointer (ComponentHelpers::getColourPropertyID (colourID)))
return Colour ((uint32) static_cast (*v));
#endif

if (inheritFromParent && parentComponent != nullptr
     && (lookAndFeel == nullptr || ! lookAndFeel->isColourSpecified (colourID)))
    return parentComponent->findColour (colourID, true);

return getLookAndFeel().findColour (colourID);

}

I could also work around this by overriding the label drawing in my L&F class, but placing a call to lookAndFeelChanged() at the end of my MainComponent constructor (per other related threads) did not help. The slider is created by Projucer for a GUI Component, so it is created via new in that component’s constructor, which happens during the construction of my MainComponent.

Shouldn’t the L&F, when it has been set, always take precedence over the Slider’s component-properties?


#2

The GUI editor changes component colours using the Component::setColour() method which, as you mentioned, takes precedence over the LookAndFeel. You can work around it by leaving the default colours for your Slider in the GUI editor and just letting the LookAndFeel handle the colours.


#3

Thanks for letting me know that if the GUI Editor is “reset to defaults”, then it will not emit property-setting code that I don’t need, in which case the property-search (that I disabled) will fail so that the L&F search can win. However, the current behavior is still inconsistent between Slider (specifically its TextBox Label) and another Label added next to the slider (saying what the slider is for).

In my case the Slider only has its TextBoxStyle set in the generated code (nothing about its TextBoxTextColourId), and that unspecified default is NOT overridden by the L&F. On the other hand, my manually created Label for the slider has its TextEditor::textColourId set to black by the Projucer-emitted code, and this DOES get overridden by the L&F! However, I do observe that if I reset my Label “editor text” to default, then its setting is omitted from the generated code, so I’ll start doing that as a code-cleaning thing. However, it doesn’t change the behavior that the Label text-color gets overridden by L&F while the Slider’s TextBox Label does not.

From stepping the code, I see that the Slider’s TextBox is created during the slider’s construction by createSliderTextBox() which was called by Slider::lookAndFeelChanged() during “init()”. Thus, both my Slider’s TextBox Label and the side Label are created in a Projucer GUI Component and everything is being created at the same time in the GUI Component constructor, which is before my call to lookAndFeelChanged(). The puzzle then remains how I can have two Labels, one in the SliderTextBox and the other in my added Label, that behave differently wrt LookAndFeel overrides, even though both were set to defaults in Projucer? Apparently the Slider’s TextBox Label is getting a property set that the Label is not. I haven’t yet found where this is happening, but I can tell you that it’s white (0xFFFFFFFF), which might have come from the default L&F somehow.

This is not important for me because there are so many workarounds, but I would make a pass on this stuff if I were responsible for it. In particular, I would think the Component properties can go away entirely. Why have two different mechanisms for the same function? Just set up a default LookAndFeel for every app and override it as desired. For backward compatibility, perhaps the existing Component property-setting functions could adopt the convention of setting up a local L&F when they are used? I would also deprecate them and print an error warning at compile time. Many possibilities here.

In Projucer for a GUI Component, maybe on the Class tab (or Graphics?), there could be a textfield for naming the desired L&F, and a checkbox could mean to create a new L&F for this component and all its subcomponents. If nothing is named, then the L&F of the GUI Component’s parent would be used, with the default L&F always being defined as a backstop at the top. When the L&F is checked as being created, a button could be offered (again in Projucer) that opens a complete L&F editor, initializable to any of the predefined L&Fs, and containing all the ColourIDs and the current settings for them in a nice hierarchical layout.

Since I am quite new at all this, I’m sure I don’t know all the tradeoffs yet, and I’m sure all this has been considered really carefully by now, but my opening code snippet, in which properties override the L&F in force unless they were never set at all is clearly not a great situation. It means you can’t be confident of an L&F override without going through and eliminating all the properties in every Component.

Brainstorming software design is much more fun than having to implement it all. :slight_smile: . Thanks for reading and considering in any case. In the meantime, I am just happy to be able to single-step anything and quickly work around any problems encountered, and learn some C++ along the way!


#4

This bit me again today, so out of interest I debugged it in Xcode, and found the problem:

Slider’s constructor calls init() which calls Slider::lookAndFeelChanged(), which leads to
valueBox.reset (lf.createSliderTextBox (owner)), which does
auto* l = LookAndFeel_V2::createSliderTextBox (slider);
which does
auto l = new SliderLabelComp();

l->setColour (Label::textColourId, slider.findColour (Slider::textBoxTextColourId));

and the V2 default color is white, so now the white color sits there in a Component property, resisting any future LookAndFeel changes.

It should work to set the LookAndFeel before creating the Slider, and not change it later. It also works of course to override drawLabel() in your LookAndFeel subclass
and place Component properties at a lower priority than LookAndFeel settings (which is what I’m doing, and am happy with so far).