LookAndFeel tightly coupled to relevant Component subclasses


#1

Many LookAndFeel members require a reference or pointer to the relevant Component-derived class. For example:

    virtual void drawComboBox (Graphics& g, int width, int height,
                               bool isButtonDown,
                               int buttonX, int buttonY,
                               int buttonW, int buttonH,
                               ComboBox& box);

I know that the drawables is going to eventually replace LookAndFeel but still, I wanted to point out this coupling because it quite frankly stinks, since you can’t re-use the code unless you have an actual ComboBox. It would be great if the parameters could be packaged into a structure or the argument list, so that the code can be re-used.


#2

It doesn’t stink - it’s the only possible way to do it!

How on earth could I create a parameter list that contains every possible detail about a ComboBox that every L+F subclass could ever need? Not only would it involve dozens of parameters (most of which would end up being unused), but when new features were added to the ComboBox in future, everybody’s L+F classes would need rewriting!


#3

Actually, you could fix it right now by changing one word, without breaking any existing code. Just modify the function signature:

void LookAndFeel::drawComboBox (Graphics& g, int width, int height,
                                const bool isButtonDown,
                                int buttonX, int buttonY,
                                int buttonW, int buttonH,
                                Component& box) // instead of ComboBox

#4

The whole point of the function is to render a ComboBox object - it was never designed to do anything more generic than that! If I had made all the L+F calls take Component& parameters, the class would be bloated with dynamic_casts and all the exception-catching code to handle cases like yours where people feed it unusual types. That’d be a real mess.

If you’re writing a component that’s so similar to a ComboBox that it can use the same renderer, then why not just derive the damn thing from combobox?


#5

Yeah it would be a mess but I wasn’t suggesting use of dynamic_cast. What I’m saying is that for the particular case of a ComboBox, the draw function doesn’t use any information specific to combo boxes. It could just use the information available in the Component portion.

ComboBox::showPopup() isn’t virtual, and showPopup() doesn’t call a virtual member to build the menu. This came up from a user who wanted to put custom components in the menu displayed by the ComboBox:

http://rawmaterialsoftware.com/viewtopic.php?f=2&t=7335

But also I had a case in my own code where I wanted to use the existing L+F code without having the object in question.

This isn’t a priority but I wanted to point it out.


#6

But LookAndFeel is a base class - other people will write implementations of that method that may need all sorts of ComboBox-specific details.

I do understand your point though, and I’m not totally against what you’re saying. But as you mentioned above, the whole LookAndFeel system needs an overhaul, so I’m kind of avoiding changing it until then.

I saw the thing about the menu builder - I’ll try to find time to have a look soon. (Please nag me if I forget)


#7

That’s true about L+F being a base class but even in that case we could keep the existing virtual function signature as ComboBox, but move the draw code into the function that takes the Component, and have the former call the latter:

virtual void LookAndFeel::drawComboBox (
  Graphics& g, int width, int height, const bool isButtonDown,
  int buttonX, int buttonY, int buttonW, int buttonH,
  Component& box)
{
  // existing combobox draw code goes here
}

ComboBox::paint() would call this:

virtual void LookAndFeel::drawComboBox (
  Graphics& g, int width, int height, const bool isButtonDown,
  int buttonX, int buttonY, int buttonW, int buttonH,
  ComboBox& box)
{
  drawComboBox (g, width, height, isButtonDown, buttonx, buttonY, buttonW, buttonH,
    static_cast <Component&> (box));
}

I do something similar in my own code. My custom slider knobs share the same appearance as my custom button faces so I put that drawing code into its own routine that just sees the Component portion of the control. Then from the slider and button specific drawing functions I call the common code, then add in the control’s type-specific drawing.