It would be nice if events could be delegated to classes that do not inherit from wxEvtHandler. It would also be nice to be able to omit the event argument if desired. It would also be nice if we could connect events to free functions and function objects.
I've been playing around with ways to make this possible...
Whether or not this is really of any use will depend upon what the following comment in event.h really means!:
Code: Select all
// m_callbackUserData is for internal usage only
It would have been possible to do this more effectively if wxDynamicEventTableEntry had a virtual destructor and maybe an event dispatching virtual function. Clearly this would reduce performance a little in the normal case, but it would make my generic method faster!
This should really be put into separate files, with the EventProxy::instance being put into a single cpp file to prevent multiple instances. Also should really have private copy/assign on the proxy. And a few more comments!
Code: Select all
class EventDelegate : public wxObject
{
public:
virtual ~EventDelegate() = 0;
virtual void processEvent(wxEvent& e) = 0;
};
inline EventDelegate::~EventDelegate() {}
template <typename E, typename F>
class EventDelegateImpl1 : public EventDelegate
{
public:
EventDelegateImpl1(F f) : f_(f) {}
virtual void processEvent(wxEvent& e) {
f_(static_cast<E&>(e));
}
private:
F f_;
};
// Eats the event when invoked.
template <typename F>
class EventDelegateImpl0eat : public EventDelegate
{
public:
EventDelegateImpl0eat(F f) : f_(f) {}
virtual void processEvent(wxEvent& e) {
f_();
}
private:
F f_;
};
// Skips the event when invoked.
template <typename F>
class EventDelegateImpl0skip : public EventDelegate
{
public:
EventDelegateImpl0skip(F f) : f_(f) {}
virtual void processEvent(wxEvent& e) {
e.Skip();
f_();
}
private:
F f_;
};
class EventProxy : public wxEvtHandler
{
public:
static EventProxy& instance();
void onEvent(wxEvent& e) {
static_cast<EventDelegate*>(e.m_callbackUserData)->processEvent(e);
}
};
EventProxy&
EventProxy::instance()
{
static EventProxy i;
return i;
}
class EventConnection
{
public:
EventConnection(
wxEvtHandler* src,
wxEventType evt_id,
wxObject* user,
int win_id,
int win_id_last
)
: src_(src)
, evt_id_(evt_id)
, user_(user)
, win_id_(win_id)
, win_id_last_(win_id_last)
{}
void disconnect() const
{
src_->Disconnect(
win_id_,
win_id_last_,
evt_id_,
wxObjectEventFunction(&EventProxy::onEvent),
user_,
&EventProxy::instance()
);
}
private:
wxEvtHandler* src_;
wxEventType evt_id_;
wxObject* user_;
int win_id_;
int win_id_last_;
};
template <typename F>
EventConnection ConnectEvent0eat(
wxEvtHandler* src,
wxEventType evt_id,
F functor,
int win_id = wxID_ANY,
int win_id_last = wxID_ANY
) {
std::auto_ptr<wxObject> p(new EventDelegateImpl0eat<F>(functor));
src->Connect(win_id, win_id_last, evt_id,
wxObjectEventFunction(&EventProxy::onEvent), p.get(), &EventProxy::instance());
wxObject* user = p.release();
return EventConnection(src, evt_id, user, win_id, win_id_last);
}
template <typename F>
EventConnection ConnectEvent0skip(
wxEvtHandler* src,
wxEventType evt_id,
F functor,
int win_id = wxID_ANY,
int win_id_last = wxID_ANY
) {
std::auto_ptr<wxObject> p(new EventDelegateImpl0skip<F>(functor));
src->Connect(win_id, win_id_last, evt_id,
wxObjectEventFunction(&EventProxy::onEvent), p.get(), &EventProxy::instance());
wxObject* user = p.release();
return EventConnection(src, evt_id, user, win_id, win_id_last);
}
template <typename E, typename F>
EventConnection ConnectEvent1(
wxEvtHandler* src,
wxEventType evt_id,
F functor,
int win_id = wxID_ANY,
int win_id_last = wxID_ANY
) {
std::auto_ptr<wxObject> p(new EventDelegateImpl1<E, F>(functor));
src->Connect(win_id, win_id_last, evt_id,
wxObjectEventFunction(&EventProxy::onEvent), p.get(), &EventProxy::instance());
wxObject* user = p.release();
return EventConnection(src, evt_id, user, win_id, win_id_last);
}
To use the above is fairly simple.
You can also use FastDelegate (google it) to connect to any object method you desire.
Code: Select all
void AStaticFunction()
{
wxLogMessage(wxT("AStaticFunction()"));
}
void AStaticFunction2(wxCommandEvent& e)
{
e.Skip();
wxLogMessage(wxT("AStaticFunction2(event)"));
}
struct AFunctor {
AFunctor() {}
void operator()() const {
wxLogMessage(wxT("AFunctor()"));
}
};
// In some initialization code:
wxMenuItem* item = ...
ConnectEvent0skip(this, wxEVT_COMMAND_MENU_SELECTED, AFunctor(), item->GetId());
ConnectEvent0skip(this, wxEVT_COMMAND_MENU_SELECTED, &AStaticFunction, item->GetId());
ConnectEvent1<wxCommandEvent>(this, wxEVT_COMMAND_MENU_SELECTED, &AStaticFunction2, item->GetId());
You would need to explicilty disconnect if you destroy the object being delegated to.