Boost logo

Boost :

From: Reece Dunn (msclrhd_at_[hidden])
Date: 2004-12-24 05:18:56

Christopher D. Russell wrote:
> Some random thoughts on GUI libraries:
> Message filtering, dispatch:
> Somewhere buried in Joel de Guzman's Spirit or Phoenix docs I recall mention
> of functional programming techniques being useful for working with graphical
> user interface library organization.

This sounds interesting. I am not sure how it would look, though.

> This comment stuck in my head as I
> generally dislike the organization of message dispatch and filtering code in
> libraries like the MFC and the WTL.

I have never liked their approach either. I like the Win32GUI approach
and have adapted it into my Boost.GUI library.

Windows has 4 types of message flow: the normal processing of messages
(e.g. WM_MOVE); processing WM_COMMAND notifications (e.g. menus or
button presses); processing WM_NOTIFY notifications (e.g. virtual list
data requests); events sent by controls to parents (and possibly
reflected back to the original sender). Win32GUI uses a std::map< UINT,
handler > to process events and has one for the normal flow and one each
for the notification flows; I am not sure how it handles
reflection/reflected events. The problem with this is that it has 3 maps
for event handling with the notification handlers being Windows-specific.

The solution I have devised is to have a single std::map< event_id,
handler > that processes all events sent to onevent( event * ). The
event_id type is OS/Platform-specific, so for GTK+ it could be
std::string. In Windows, this is
    struct event_id
       unsigned int id;
       unsigned int code;
where id is the event type (WM_PAINT, etc.) and code is normally 0, but
for WM_COMMAND and WM_NOTIFY messages, it is 0 (for generic handlers)
and is then set to the notification code (e.g. BN_PRESSED for button
presses). The onevent function for Windows will then process reflected

The handler is currently defined as boost::signal< bool ( event * ev )
>, such that ev is the OS-specific event structure and the return value
indicates is the message has been processed (and processing should be
stopped). Since the signal has a non-member signature, you need a
    make_handler( Object * ob, bool ( Object::* h )( event * ev ))
helper that constructs a function object that will call the member
function. This is far more powerful than binding specifically to a
component member function as it allows:
* an application class to respond to close events;
* the radio_group class to process a group of radio buttons to keep
mutual exclusivity, handling onpressed() events;
* layout managers to respond to resize events without you having to add
the handlers manually, or write specific code to interact with the
layout manager (NOTE: this is currently in code on my machine, and I
will post it after Christmas).

There is a defect with my code at present: returning true from a handler
will stop all event processing. This affects filtering handlers such as
radio_group and cascading handlers like layout managers and will result
in undefined behaviour. I am thinking of binding a handler-type to the
event handler:
    handler | filter | cascading
which dictates how the events are processed.

The event processor allows you to attach a handler to a component using:
    signal_type handler_for( event_id );
for example:
    frame.handler_for( event::onclose )
         .connect( &terminate_gui );

Buttons define a
    signal_type onpressed()
method that is an alias for the OS-specific:
    handler_for( button-pressed-event )
which in Windows is:
    handler_for( event_id( WM_COMMAND, BN_PRESSED ))

I am also considering returning a proxy class to a signal object to
allow for:

       .attach( handler_type::handler, this, &mainfrm::onnew );
       .attach( handler_type::handler, &terminate_gui );

> In essence, this is just another way to encode complex state machines. But I
> think it would be cool to encode the message filtering and dispatch logic in

This sounds cool. Could you give an example?

It should be possible to build the state machine logic on top of the
event processing outlined above. For example:

    button quit;
       // handle button presses
       << state::pressed[ &terminate_gui ]
       // convert pressing the spacebar into pressing the button
       << state::keypress( ' ' ).fire( state::pressed )

Is this what you are aiming at? I am not sure on the implementability of
the above, but it will most likely be built on top of the event
processing/binding that is in place by my library. This would be done my
constructing the appropriate function objects and the appropriate calls
to handler_for.

The state machine should allow for successive slots, e.g.:

       // save the state of the application (e.g. position)
       << state::close[ state::bind( frame, &mainfrm::onsavestate )]
       // terminate the application
       << state::close[ &terminate_gui ]

> Message routing:
> signals / slots?

I am using signals/slots for event handling (see above for details).


Boost list run by bdawes at, gregod at, cpdaniel at, john at