I’ve always found it nicer to have dedicated handlers for widgets instead of the if/else if/else if… construct, and with JUCE now having Listener subclasses of most widgets, I made these template classes for handling callbacks from them, hopefully you’ll find them usable. Also attached is a templated message handling construct so you can have dedicated handler functions for different message types (using RTTI for dispatching).
template<typename Type, typename Widget>
class ListenerBase : public Widget::Listener
{
protected:
typedef void (Type::*TListenerCallback)(void);
typedef std::map<Widget*, TListenerCallback> cb_map_type;
cb_map_type m_callbacks;
void notify(Widget* key)
{
typename cb_map_type::iterator it = m_callbacks.find(key);
if (it != m_callbacks.end())
{
(((Type*)this)->*(it->second))();
}
}
public:
void setHandler(Widget* key, TListenerCallback callback)
{
removeHandler(key);
key->addListener(this);
m_callbacks[key] = callback;
}
void removeHandler(Widget* key)
{
typename cb_map_type::iterator it = m_callbacks.find(key);
if (it != m_callbacks.end())
{
key->removeListener(this);
m_callbacks.erase(it);
}
}
};
template<typename Type>
class ButtonListenerHandler : public ListenerBase<Type, Button>
{
protected:
typedef ListenerBase<Type, Button> ButtonListenerBase;
virtual void buttonClicked (Button* button)
{
ButtonListenerBase::notify(button);
}
};
template<typename Type>
class ComboBoxListenerHandler : public ListenerBase<Type, ComboBox>
{
protected:
typedef ListenerBase<Type, ComboBox> ComboBoxListenerBase;
virtual void comboBoxChanged (ComboBox* comboBoxThatHasChanged)
{
ComboBoxListenerBase::notify(comboBoxThatHasChanged);
}
};
template<typename Type>
class LabelListenerHandler : public ListenerBase<Type, Label>
{
protected:
typedef ListenerBase<Type, Label> LabelListenerBase;
virtual void labelTextChanged(Label* labelThatHasChanged)
{
LabelListenerBase::notify(labelThatHasChanged);
}
};
//////////////////////////////////////////////////////////////////////////
// Message listener helper
class MessageListenerHandlerBase
{
protected:
virtual void onMessage(const Message& message) = 0;
friend class MessageListenerBase;
};
class MessageListenerBase : public MessageListener
{
protected:
template<class MessageType>
void registerListener(MessageListenerHandlerBase* listener)
{
m_listenerMap[typeid(MessageType).name()] = listener;
}
private:
virtual void handleMessage (const Message& message)
{
message_listener_t::iterator it = m_listenerMap.find(typeid(message).name());
if (it != m_listenerMap.end())
{
(it->second)->onMessage(message);
}
}
typedef std::map<const char*, MessageListenerHandlerBase*> message_listener_t;
message_listener_t m_listenerMap;
};
template<class MessageType>
class MessageListenerHandler : public virtual MessageListenerBase
, public MessageListenerHandlerBase
{
protected:
MessageListenerHandler()
{
registerListener<MessageType>(this);
}
virtual void onMessage(const MessageType& message) = 0;
private:
virtual void onMessage(const Message& message)
{
// static_cast would be nicer, but it requires full knowledge of MessageType (which excludes fwd declarations)
onMessage(reinterpret_cast<const MessageType&>(message));
}
};
Usage of the widget stuff:
class MyClass : public ButtonListenerHandler<MyClass>
, public ComboBoxListenerHandler<MyClass>
{
...
using ButtonListenerHandler<MyClass>::setHandler;
using ComboBoxListenerHandler<MyClass>::setHandler;
void onButtonClicked();
void onComboBoxChanged();
};
MyClass::MyClass()
{
...
setHandler(m_button, &MyClass::onButtonClicked);
setHandler(m_combobox, &MyClass::onComboBoxChanged);
}
Note the using declarations which are needed if you use more than one of the widget callback handlers.
Or, as per Jules idea:
class MyClass
{
private:
Button* m_button1;
Button* m_button2;
ButtonListenerHandler<MyClass> m_btnHandler;
void onButton1Clicked();
void onButton2Clicked();
};
MyClass::MyClass()
{
...
m_btnHandler.setHandler(m_button1, &MyClass::onButton1Clicked);
m_btnHandler.setHandler(m_button2, &MyClass::onButton2Clicked);
}
Usage of the templated message callback handler:
class MessageClass : public Message
{
public:
MessageClass() {;}
};
// Note: MessageClass could also be fwd declared, and defined in the cpp file instead
class MyClass : public MessageListenerHandler<MessageClass>
{
public:
...
protected:
virtual void onMessage(const MessageClass& message);
};