Boost logo

Boost :

From: Jarrad Waterloo (jwaterloo_at_[hidden])
Date: 2007-10-22 09:41:10


Please keep the MVC, model view controller, pattern in mind from the
beginning so that we the users can map our existing data to the gui controls
rather than having to add them. This pattern is also used in Java's Swing
(not AWT) and somewhat in .NET's windows form. Though I will say that .NET
is more like a wrapper around the Win32/64 api and doesn't use MVC
extensively. Though in their defense the common IList, ICollection and
IEnumerable interfaces works more consistently over all their controls than
Java's multitude of control specific model interfaces. So a balance needs to
be reached using C++ idioms.

As an example, when working on a list control it could be associated with a
range of random access iterators. The control than would not add all the
items to the control but would rather use delay loading to ask the
collection what the data when it needs it. Since STL collections doesn't
provide change notifications nor classes by default than such a sublibrary
would also need to be considered.

-----Original Message-----
From: boost-bounces_at_[hidden] [mailto:boost-bounces_at_[hidden]]
On Behalf Of Felipe Magno de Almeida
Sent: Saturday, October 20, 2007 9:43 PM
To: boost_at_[hidden]
Subject: [boost] [rfc] Gui library

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
_______________________________________________
Unsubscribe & other changes:
http://lists.boost.org/mailman/listinfo.cgi/boost

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