[wx-dev] RFC improved event handling (more minor changes)
Brian Vanderburg II
BrianVanderburg2 at aim.com
Wed Jul 30 07:45:18 PDT 2008
I forgot the attachment
> Brian Vanderburg II wrote:
>> I've made some minor changes to your code to get a few cases working:
>>
>> 1. Two different connects for members. The first one expects the =
>> handler object to be the same class as the handler function. The =
>> second allows the handler object to be derived from the class =
>> containing the handler function.
>>
>> 2. Type safety is only guaranteed if a handler object is passed. If =
>> you do window.Connect(wxEVT_BLUE, &MyEventHandler::onHandleColor ), =
>> type casting is done converting 'this' to MyEventHandler but window =
>> is only wxEvtHandler, so MyEventHandler::onHandleColor will get =
>> called with a 'this' pointer only pointing to wxEvtHandler, not =
>> MyEventHandler.
>>
>> I've attached the changes to this file, with some comments where I've =
>> made changes.
>>
>> Brian Vanderburg II
>>
> I've made a few more changes.
>
> 1. A small problem that may crop up in some compilers. Using the =
> last version:
>
> window.Connect(wxEVT_PAINT, &MyHandler::OnPaint) would only call the =
> first member version since no handler object is passed.
>
> window.Connect(wxEVT_PAINT, &MyHandler::OnPaint, &derived) would only =
> call the second member version since the handler object is not of the =
> same type but derived from the type the member function is in
>
> window.Connect(wxEVT_PAINT, &MyHandler::OnPaint, &myHandler) would =
> possibly match either the first or the second version, which may =
> produce ambiguity errors on some compilers.
>
> Change:
>
> The first member version does not take any handler object. This =
> version is used as when no handler object is specified and uses 'this' =
> as the handler. The second member version is as is, taking Derived as =
> a handler object. This may be of the same class or of a derived class =
> of the handler member function. There would be no ambiguity for the =
> the third situation above.
>
> 2. Simulate wxWidgets connect - I've changed the Connect functions to =
> have the same arguments that wxWidgets currently has. For each of the =
> three main connects I've also created the shortcut versions that take =
> only one ID and no ID as well.
>
>
> If this change is incorporated into wxWidgets, the static event tables =
> could eventually be completely eliminated. It would no longer be the =
> easy way:
>
> BEGIN_EVENT_TABLE(MyWindow, wxFrame)
> EVT_MENU(ID_MENU1, MyWindow::OnMenu1)
> EVT_MENU(ID_MENU2, MyWindow::OnMenu2)
> EVT_MENU(ID_MENU3, MyWindow::OnMenu3)
> END_EVENT_TABLE()
>
> vs:
>
> Connect(ID_MENU1, wxEVT_MENU, &MyWindow::OnMenu1);
> Connect(ID_MENU2, wxEVT_MENU, &MyWindow::OnMenu2);
> Connect(ID_MENU3, wxEVT_MENU, &MyWindow::OnMenu3);
>
> The second is fewer lines of code, and no longer need to use the =
> conversion macros. However it may still be needed for parent =
> propogation of events to work:
>
> If MyWindow uses only Connect and no BEGIN_EVENT_TABLE/etc exists any =
> more, events not handled in MyWindow will not get processed by the =
> event tables of wxFrame.
>
> One solution: since there is only one event table for each object, =
> unlike the static class event tables, base classes could use dynamic =
> connections as well. When the base is created, it would Connect =
> whatever it needed, then the derived class could Connect whatever it =
> needed. For this to work, the dynamic event table must be processed =
> backwards, so event handlers set in MyWindow would handled first and =
> if not found those set in wxFrame. Also, the event handler for this =
> window must be installed after creating the parent window (after =
> calling Parent::Create()
>
> // For single-step construction
> class MyFrame : public wxFrame
> {
> public
> MyFrame()
> {
> wxFrame::Create(...) // Could also do MyFrame() : wxFrame(...)
> // Connect event handlers after Parent::Create()
> }
> }
>
> // For two-step construction
> class MyFrame : public wxFrame
> {
> public:
> MyFrame()
> {
> // no connections
> }
>
> MyFrame(...)
> {
> Create(...)
> }
>
> bool Create(...)
> {
> wxFrame::Create(...)
> // Connect here after Parent::Create
> }
> }
>
> Another issue would be how to handle Disconnect?
>
> Brian Vanderburg II
> _______________________________________________
> wx-dev mailing list
> wx-dev at lists.wxwidgets.org
> http://lists.wxwidgets.org/mailman/listinfo/wx-dev
-------------- next part --------------
// Self contained prototype for new/improved event handling
// (c) by Peter Most
//
#include <list>
#include <utility>
using namespace std;
// This is the same function as the one in src/event.cpp:
int wxNewEventType()
{
static int s_lastUsedEventType =3D 10000;
return s_lastUsedEventType++;
}
class wxEventType {
public:
wxEventType()
{
m_value =3D wxNewEventType();
}
bool operator =3D=3D ( const wxEventType &other ) const
{
return ( m_value =3D=3D other.m_value );
}
private:
int m_value;
};
// The templatized event type:
template < typename Event >
class wxEventTypeTemplate : public wxEventType {
public:
typedef Event AssociatedEvent;
};
// The new macro which associates the event type with the event it delivers:
#define DEFINE_NEW_EVENT_TYPE( name, event ) \
const wxEventTypeTemplate< event > name;
// The 'old' macro for backward compatibility:
#define DEFINE_EVENT_TYPE( name ) \
DEFINE_NEW_EVENT_TYPE( name, wxEvent )
// Base class for all events from include/wx/event.h:
=
class wxEvent {
public:
wxEvent( wxEventType type )
{
m_type =3D type;
}
bool operator =3D=3D ( const wxEvent &other ) const
{
return ( m_type =3D=3D other.m_type );
}
private:
wxEventType m_type;
};
// A legacy event from include/wx/timer.h:
class wxTimerEvent : public wxEvent {
public:
wxTimerEvent( wxEventType eventType )
: wxEvent( eventType )
{ }
};
DEFINE_EVENT_TYPE( wxEVT_TIMER )
// If the folling line is activated and the line above deactivated, then the
// 'old' way of defining event handler doesn't compile anymore, which IMO i=
s a
// good thing.
//
// DEFINE_NEW_EVENT_TYPE( wxEVT_TIMER, wxTimerEvent )
// A 'new' type event:
=
class wxColorEvent : public wxEvent {
public:
wxColorEvent( wxEventType eventType )
: wxEvent( eventType )
{ }
};
DEFINE_NEW_EVENT_TYPE( wxEVT_BLUE, wxColorEvent )
DEFINE_NEW_EVENT_TYPE( wxEVT_WHITE, wxColorEvent )
class wxEvtFunctor {
public:
virtual ~wxEvtFunctor()
{ }
virtual void operator()( wxEvent & ) =3D 0;
};
template < typename EventType >
class wxEvtFunction : public wxEvtFunctor {
public:
wxEvtFunction( void ( *handler )( typename EventType::Associate=
dEvent & ))
{
m_handler =3D handler;
}
virtual void operator()( wxEvent &event )
{
( *m_handler )( static_cast< typename EventType::Associated=
Event & >( event ));
}
private:
void ( *m_handler )( typename EventType::AssociatedEvent & );
};
=
template < typename EventType, typename Class >
class wxEvtMethod : public wxEvtFunctor {
public:
wxEvtMethod( Class *handler, void ( Class::*method )( typename =
EventType::AssociatedEvent & ))
{
m_handler =3D handler;
m_method =3D method;
}
virtual void operator()( wxEvent &event )
{
( m_handler->*m_method )( static_cast< typename EventType::=
AssociatedEvent & >( event ));
}
private:
Class *m_handler;
void ( Class::*m_method )( typename EventType::AssociatedEvent =
& );
};
class wxObject;
#define wxID_ANY -1
class wxEvtHandler {
public:
// Connect method for connecting normal functions to an event:
template < typename EventType >
void Connect( int winid,
int lastid,
const EventType &event,
void ( *handler )( typename EventType::Associated=
Event & ),
wxObject* userData =3D 0)
{
wxEvtFunctor *functor =3D new wxEvtFunction< EventType >( h=
andler );
m_handlers.push_back( make_pair( event, functor ));
}
template < typename EventType >
void Connect( int winid,
const EventType &event,
void ( *handler )( typename EventType::Associated=
Event & ),
wxObject* userData =3D 0)
{
Connect( winid, wxID_ANY, event, handler, userData );
}
=
template < typename EventType >
void Connect( const EventType &event,
void ( *handler )( typename EventType::Associated=
Event & ),
wxObject* userData =3D 0)
{
Connect( wxID_ANY, wxID_ANY, event, handler, userData );
}
// Connect methods for connecting methods to an event:
// this method is used if no handler is passed
=
template < typename EventType, typename Class >
void Connect( int winid,
int lastid,
const EventType &event,
void ( Class::*method )( typename EventType::Asso=
ciatedEvent & ),
wxObject* userData =3D 0 )
{
wxEvtFunctor *functor =3D new wxEvtMethod< EventType, Class=
>( static_cast< Class * >( this ), method );
m_handlers.push_back( make_pair( event, functor ));
}
=
template < typename EventType, typename Class >
void Connect( int winid,
const EventType &event,
void ( Class::*method )( typename EventType::Asso=
ciatedEvent & ),
wxObject* userData =3D 0 )
{
Connect( winid, wxID_ANY, event, method, userData );
}
=
template < typename EventType, typename Class >
void Connect( const EventType &event,
void ( Class::*method )( typename EventType::Asso=
ciatedEvent & ),
wxObject* userData =3D 0 )
{
Connect( wxID_ANY, wxID_ANY, event, method, userData );
}
// this method is used if the passed handler is of type Class or de=
rived from Class
template < typename EventType, typename Class, typename Derived >
void Connect( int winid,
int lastid,
const EventType &event,
void ( Class::*method )( typename EventType::Asso=
ciatedEvent & ),
wxObject* userData,
Derived *handler )
{
wxEvtFunctor *functor =3D new wxEvtMethod< EventType, Class=
>( handler, method );
m_handlers.push_back( make_pair( event, functor ));
}
=
template < typename EventType, typename Class, typename Derived >
void Connect( int winid,
const EventType &event,
void ( Class::*method )( typename EventType::Asso=
ciatedEvent & ),
wxObject* userData,
Derived *handler )
{
Connect( winid, wxID_ANY, event, method, userData, handler =
);
}
=
template < typename EventType, typename Class, typename Derived >
void Connect( const EventType &event,
void ( Class::*method )( typename EventType::Asso=
ciatedEvent & ),
wxObject* userData,
Derived *handler )
{
Connect( wxID_ANY, wxID_ANY, event, method, userData, handl=
er );
}
bool ProcessEvent( wxEvent& event )
{
for ( Handlers::iterator handler =3D m_handlers.begin(); handle=
r !=3D m_handlers.end(); ++handler ) {
// Is this a handler for the event (first=3DwxEventType, se=
cond=3DwxEvtFunctor)
if ( event =3D=3D handler->first )
( *handler->second )( event );
}
}
private:
typedef list< pair< wxEventType, wxEvtFunctor * > > Handlers;
Handlers m_handlers;
};
// Duplicated typedefs from include/wx/event.h and include/wx/timer.h
typedef void (wxEvtHandler::*wxEventFunction)(wxEvent&);
typedef wxEventFunction wxObjectEventFunction;
typedef void (wxEvtHandler::*wxTimerEventFunction)(wxTimerEvent&);
#define wxTimerEventHandler(func) \
(wxObjectEventFunction)(wxEventFunction)static_cast< wxTimerEventFuncti=
on >( &func )
void handleColorEvent( wxColorEvent &event )
{
}
void handleTimerEvent( wxTimerEvent &event )
{
}
class MyEventHandler : public wxEvtHandler {
public:
MyEventHandler()
{
Connect( wxEVT_BLUE, &MyEventHandler::handleColorEvent );
Connect( wxEVT_WHITE, &MyEventHandler::handleColorEvent );
Connect( wxEVT_TIMER, wxTimerEventHandler( MyEventHandler::hand=
leTimerEvent ));
// If wxEVT_TIMER becomes a 'new' event, then this line has to =
be used:
// Connect( wxEVT_TIMER, &MyEventHandler::handleTimerEvent );
}
void handleColorEvent( wxColorEvent &event )
{
}
void handleTimerEvent( wxTimerEvent &event )
{
}
};
class DerivedEventHandler : public MyEventHandler
{
public:
void handleRealColorEvent( wxColorEvent& evt )
{
}
};
int main( void )
{
//
// should compile:
//
wxEvtHandler window;
window.Connect( wxEVT_WHITE, &handleColorEvent );
window.Connect( wxEVT_BLUE, &handleColorEvent );
window.Connect( wxEVT_TIMER, reinterpret_cast< void ( * )( wxEvent & ) =
>( &handleTimerEvent ));
//
// Would be nice if these would compile, but don't see how, without sac=
rificing type safety:
//
// window.Connect( wxEVT_TIMER, &handleTimerEvent );
// window.Connect( wxEVT_TIMER, &MyEventHandler::handleTimerEvent, &rec=
eiver );
MyEventHandler receiver;
window.Connect( wxEVT_BLUE, &MyEventHandler::handleColorEvent, 0, &rec=
eiver );
window.Connect( wxEVT_WHITE, &MyEventHandler::handleColorEvent, 0, &rec=
eiver );
window.Connect( wxEVT_TIMER, reinterpret_cast< void ( MyEventHandler::*=
)( wxEvent & ) >( &MyEventHandler::handleTimerEvent ), 0, &receiver );
// This line doesn't compile, probably because it tries to cast to wxEv=
tHandler
// It doesn't compile because no template matches. It expects Class to=
be wxEvtHandler, but
// receiver is MyEventHandler so the first Connect for members does not=
match.
// Change: This line now compiles because of the second Connect for mem=
bers
window.Connect( wxEVT_TIMER, wxTimerEventHandler( MyEventHandler::handl=
eTimerEvent ), 0, &receiver );
//
// must not compile:
//
=
// window.Connect( wxEVT_TIMER, &handleColorEvent );
// window.Connect( wxEVT_TIMER, &MyEventHandler::handleColorEvent, &rec=
eiver );
//
// Send an event:
//
wxColorEvent colorEvent( wxEVT_BLUE );
window.ProcessEvent( colorEvent );
// This originally will not compile without the second Connect for memb=
ers
DerivedEventHandler receiver2;
window.Connect( wxEVT_BLUE, &MyEventHandler::handleColorEvent, 0, &rece=
iver2 );
// other tests
window.Connect( wxEVT_BLUE, &DerivedEventHandler::handleRealColorEvent,=
0, &receiver2 );
receiver2.Connect( wxEVT_BLUE, &DerivedEventHandler::handleRealColorEve=
nt );
receiver2.Connect( wxEVT_BLUE, &MyEventHandler::handleColorEvent );
// type safety test
// Problem: casting wxEvtHandler to DerivedEventHandler
// this is pointer to wxEvtHandler but cast converts to DerivedEventHan=
dler so
// DerivedEventHandler::handleRealColorEvent will get a 'this' pointer =
to
// a wxEvtHandler object
window.Connect( wxEVT_BLUE, &DerivedEventHandler::handleRealColorEvent =
);
receiver.Connect( wxEVT_BLUE, &DerivedEventHandler::handleRealColorEven=
t );
// Should be written as
window.Connect( wxEVT_BLUE, &DerivedEventHandler::handleRealColorEvent,=
0, &receiver2 );
receiver.Connect( wxEVT_BLUE, &DerivedEventHandler::handleRealColorEven=
t, 0, &receiver2 );
}
More information about the wx-dev
mailing list