Boost logo

Boost :

From: Felipe Magno de Almeida (felipe.m.almeida_at_[hidden])
Date: 2007-10-20 21:42:50


Hi,

I'm already working on a GUI library for some time (three weeks). And
come up with some designs and code.
I'm sure to have it for review when ready and I would like to ask for
comments here to get it going in the right direction.
The code is in www.sourceforge.net/projects/cppgui

The code is not completely compiling right now, but I'm writing it
with support for win32/64, gtk and qt.

The main design decision I have is that the GUI interface and the
implementation specific code is completely decoupled. That way I can
have a application that executes win32, gtk *and* qt at the same time.
It is indeed what the first example does.

In that, I call the implementations as drivers. And I can have the
driver specified in the create window function. Just as this:

wnd<> w = create<frame_window>( _driver = drivers::win32 );

For this to work, I had to create two hierarchies:

      window window_impl_base
          | |
         / \ / \
        / \ / \
       / \ / \
      / \ / \
frame button frame_impl_base button_impl_base

The first hierarchy is the one exposed to the user and the second must
be derived from the drivers.
The first can be subclassed by the user with this syntax:

class my_frame : gui::subclass<my_frame, gui::frame>
{
  my_frame()
  {
    wnd_lock<my_frame> w = wnd_pointer_cast_lock<my_frame>(wnd_from_this());
    wnd_lock<gui::controls:button> btn = create<gui::controls::button>
      ( _parent = w, _size = std::make_pair(20, 20) );
    btn_ = btn;
  }

  static void info(gui::create_info& i) { i.pos = std::make_pair(100, 100); }

  wnd<> btn_;
};

And then the info function is called before my_frame instantiation
automagically. This way my_frame can override the window creation
properties before it is actually created by the driver.
There's a injection of the window implementation in the window base
class of the hierarchy too, so that calling GUI functions from inside
the constructor would be safe too.

Now, what I think is the most complicated part: wnd<> and wnd_lock<> classes.
I decided to use functors as a way to execute handlers for GUI events.
Therefore, window smart pointers could end up inside functors objects
through boost::bind and others. The problem is: The window should be
destroyable while waiting for user response. But if smart pointers
inside functors were registered within the window, there would be a
cyclic relationship that would prohibit it. And then I created these
two classes. wnd<> being like weak_ptr in shared_ptr and wnd_lock<>
would be the shared_ptr itself.
I honestly do not like this solution and even less the wnd_lock<>
name. It even seem to imply thread mutual exclusion, which it does
*not*.

Also, I found when using asio and win32gui that it is nice to have a
class just for the handlers, which holds all its state.
So I created a way to register event classes to windows, and to which
you can return a functor that returns this event class object to be
used in the handler binder.

So you can do this:

struct event_class
{
  void btn_clicked(wnd_lock<> btn)
  {
    // do something
  }

  int x; // data required for handlers
};

my_frame::my_frame()
{
  wnd_lock<controls::button> btn = create<controls::button>
    ( _parent = wnd_from_this(), _text = "My button!" );
  add_event_class<event_class>();

  // this registers to call event_class::btn_clicked with
  // the event_class object under the window's lifetime management.
  btn->on_click(boost::bind(&event_class::btn_clicked
    , boost::bind(get_event_f<event_class>(), _1 ));
}

That way event classes can be completely defined inside a .cpp file,
and any alteration to it wouldn't need the world to recompile, nor
would be coupling between event handlers and the visual creation code.
In theory, at least.
But I find the syntax horrible for event registration and the event
handler class ends up being referenced *a lot* inside the window's
constructor.

So, any comments would be *really* appreciated. And keep in mind that
the library is in pre-alpha state (and that's why I'm posting this rfc
here, so that I can find a better design to code). Which means that
most parts aren't implemented yet. (Most controls, dialogs, resource
files, etc).

Thanks and best regards,

-- 
Felipe Magno de Almeida

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk