Sure, take your time to think about it!
Just to throw some wild ideas about the subject into your mulling engine, by now I have considered (and implemented, to some degree) these classes that more or less are pertinent to the subject:
/** Classes derived from this can delegate the painting of some components
to a Delegate instance.
This makes it possible to concentrate the painting for different components
into a single method in the delegate, thus avoiding the need to subclass every
Component just to properly implement its paint() method.
*/
class PaintableWithDelegate
{
public:
/** Classes that receive paint requests from PaintableWithDelegate objects
should derive from this class */
class Delegate
{
public:
virtual ~Delegate () { }
/** This method is called when the delegate should paint the given
component in place of the component's own paint() method.
_source_ is the Component to be painted, _g_ is the Graphics context
into which it should be painted */
virtual void paint (Component* /*source*/, Graphics& /*g*/) { }
};
explicit PaintableWithDelegate () : m_delegate (nullptr) { }
virtual ~PaintableWithDelegate () { }
/** Sets _delegate_ as the Delegate to be called when delegatePaint() is invoked. */
void setPaintDelegate (Delegate* delegate) { m_delegate = delegate; }
/** This method is called when a PaintableWithDelegate object asked the
delegate to paint the given _component_ inside the Graphics _g_*/
bool delegatePaint (Component* component, Graphics& g)
{
if (m_delegate == nullptr)
return false;
m_delegate->paint (component, g);
return true;
}
private:
Delegate* m_delegate;
};
This "PaintableWithDelegate" approach could be translated to the hitTest thing creating a similar "HittableWithDelegate" base class.
/** A base class for those widgets that need to have a custom hit region */
class SettableHitArea
{
public:
SettableHitArea () : m_areaToTest (lookAndFeelWhenAvailable) { }
virtual ~SettableHitArea () { }
void resetHitArea (bool queryLookAndFeelWhenAvailable = true);
void setRectangularHitArea (Rectangle <int> area);
void setRoundHitArea (Point <float> centre, float radius);
bool checkHit (int x, int y) const;
private:
enum AreaToTest
{
all,
lookAndFeelWhenAvailable,
rectangular,
round,
};
AreaToTest m_areaToTest;
Rectangle <int> m_rectangularHitArea;
Point <float> m_roundHitAreaCentre;
float m_roundHitAreaRadiusSquared;
};
/** Resets the hit region. If _queryLookAndFeelWhenAvailable_ is true and this
is a Component whose LookAndFeel also derives from SettableHitArea, it queries
that LookAndFeel for hit tests. If false, every test sent to checkHit()
will return true. */
void SettableHitArea::resetHitArea (bool queryLookAndFeelWhenAvailable)
{
m_areaToTest = (queryLookAndFeelWhenAvailable ? lookAndFeelWhenAvailable : all);
}
/** Sets the hit region to be the specified rectangular _area_. */
void SettableHitArea::setRectangularHitArea (Rectangle <int> area)
{
m_rectangularHitArea = area;
m_areaToTest = rectangular;
}
/** Sets the hit region to be a circle with given _centre_ and _radius_ */
void SettableHitArea::setRoundHitArea (Point <float> centre, float radius)
{
m_roundHitAreaCentre = centre;
m_roundHitAreaRadiusSquared = radius * radius;
m_areaToTest = round;
}
/** Checks whether _x_ and _y_ fall within the current hit region */
bool SettableHitArea::checkHit (int x, int y) const
{
switch (m_areaToTest)
{
case all:
return true;
case lookAndFeelWhenAvailable:
{
const Component* c = dynamic_cast <const Component*> (this);
if (c == nullptr)
return true;
const SettableHitArea* lookAndFeelHitArea = dynamic_cast <const SettableHitArea*> (&c->getLookAndFeel());
if (lookAndFeelHitArea == nullptr)
return true;
return lookAndFeelHitArea->checkHit (x, y);
}
case rectangular:
return m_rectangularHitArea.contains (x, y);
case round:
{
const float deltaX = x - m_roundHitAreaCentre.getX ();
const float deltaY = y - m_roundHitAreaCentre.getY ();
return ((deltaX * deltaX) + (deltaY * deltaY) <= m_roundHitAreaRadiusSquared);
}
default:
jassertfalse;
return true;
}
}
This other one is a base class meant to be a base class for either Components or LookAndFeel derived classes.
Feel free to steal ideas or piece of codes from those classes at your will