That being said, I think that the approach presented by @DKDiveDude is useful for different use cases.
For example, I follow a similar approach in my bitmap-based LnF for Sliders to be drawn as knobs: if the LnF finds a certain property of the Slider set to true, it will paint the current frame index in a corner of the Slider itself, which comes handy for debugging purposes
But not only that, there are other aspects there like the slider style and the size of the label compared to the slider which you might want to change without re-writing the entire paint function.
Not to mention, if you do end up having 50 sliders that are all with the same style, you could just have the one wrapper that sets them up it in the constructor, instead of writing:
The fact I omitted for sake of brevity is that that LnF class of mine, does the same for all bitmap-based widget it draws, not just knobs, and since it’s the LnF that knows exactly what frame it has to paint (it serves also as a contained for them) it just made sense to draw that info in paint() considering the fact that it’s only available in DEBUG builds.
It’s not meant for release.
Besides, I often found a real good use of LnF instances that only draw a specific kind of widget, e.g. one for buttons, one for sliders, etc.
Using one single LnF throughout your whole UI as a “skin” works well 90% of the time, but the remaining 10% you really need that widget to be “different” from those in your “default” LnF, and then spawning a new LnF class is easier than customising the existing one based on Component properties etc.
I think the levelKnob[module] here should be better replaced by simply using slider, right?
that looks like something left over after a refactoring.
Also, you probably gain in clarity by passing a Colour (which data-wise is only an uint32) rather than 3 separate args for red, green and blue, also in consideration of the fact that you use those just to obtain a Colour object inside the function
You might consider using a lambda for createKnob. If it is only called from the one place all the knobs are created, then, from an scoping/architectural standpoint, it doesn’t need to be an additional member of the class, but could instead be a lambda in the function where you use it. It’s always good practice to keep your scoping to the limits required by usage.
I’m going to argue against this, exclusively on xcode, because Xcode doesn’t show you the parameter placeholders for lambdas, but it does for constructors and functions. Maybe Visual Studio is different, but it’s an annoyance in Xcode to not see Lambda parameter placeholders.