Boost logo

Boost :

From: Reece Dunn (msclrhd_at_[hidden])
Date: 2004-11-20 07:33:53


Aleksey Chernoraenko wrote:
> Hello,
>
> I'm probably too late and what I'd like to say is too general, but maybe
> somebody will find the following thoughts useful.

It is not too late. This is a complex topic, and everybody's input is
welcome.

> There are many GUI frameworks - platform-specific, platform-independent,
> something in between,
> but most of them (I'm afraid to say all of them :) have the same problems.
>
> On the one hand they give a user many really good things: windowing,
> controls, event handling, geometry, drag&drop support, etc...
> On the other hand they are monolithic - you have to use the *whole*
> framework, you cannot take and use only one subsystem.
> My application is based on MapApp and I see nice implementation of
> general event handlers in other framework
> but I cannot use it because I cannot rewrite whole application on new
> framework.

And they also tend to provide their own array, list, string and vector
classes, instead of making use of those in the standard library. Not to
mention, several provide their own bool!

The problem is that the GUI framework needs to use these classes to
provide the apropriate facilities. I have chosen (for the GUI library
under development in the sandbox - still at the experimental stage) to
make use of the STL for containers and Boost.Signals for event handling.
If I were to allow you to use your own signal/slot class, I would need
some mechnism for registering that so my framework can understand and
use it. That means either:

[1] templatize the class -- this would move the implementation code over
to the headers, making it harder to separate the interface from various
platforms and may lead to code bloat.

[2] use macros -- this would make the code harder to understand and lead
to unnecessarily complex code.

> IMO, the best GUI "framework" would be a *set* of separate, specific
> libraries each of which
> would provide good abstractions/algorithms in some specific gui-related
> area:
>
> ** general-purpose 2d lightweight geometry and general algorithms
> ( boost::gui::2d::point, rect, path, ...)

I have designed my core geometry classes (point, size, rect) such that
they derive from the native class (e.g. rect derives from NSRect in
Cocoa). I am not sure on putting these in a separate namespace (BTW, 2d
is an invalid name, it would have to be either _2d, twod, twoD or
geometry/geom).

There are, in general, two types of GUI objects:

[1] struct wrappers -- classes that encapsulate a concept defined in the
OS as a struct. The geometry classes belong to this group. These classes
should derive from the struct and allow access to that struct. This
makes it eay to interoperate with other classes, for example:

    CPoint pt( static_cast< POINT >( gui::point( 5, 7 )));

This is a pointless example, but demonstrates that these classes can
interoperate with other frameworks.

[2] pointer/handle wrappers -- classes that wrap concepts defined by the
OS using handle types. This includes window, canvas, pen, font and other
such objects. I have chosen to provide a conversion to the handle type,
allowing interaction with existing code. For example:

    main_window frame( "Main Frame" );
    ::ShowWindow( frame, SW_SHOW );

> ** painting areas, canvases and painting algorithms

I agree that these should be separate. It should be possible to interact
well with existing graphics wrappers as well, e.g. on Windows, allowing
you to use MFC CDC, GDI+, DirectX, etc..

This would be used something like:

    void main_window::draw( canvas c )
    {
       Graphics g( static_cast< HDC >( c ));
       g.DrawImage( &im, 10, 10 );
    }

> ** events, propagation and handling mechanisms, (event_target, listener,
> subscribers...)
>
> ** windowing (windows, views...)

The event mechanism is tied to the windowing model. That is, the base
window (component) is bound to the chosen event handling mechanism.
Having said that, you only need to provide a few windowing classes:

* component -- provides the basic OS-native window handling (allocates
OS resources for the component). This should provide a mechanism for
hooking the event mechanism (i.e. subclassing in Windows terminology).

* UI-less component -- does not allocate OS resources for the
component. This would be used to implement layout managers, etc.

* form -- a special window that is defined by an external resource,
identified by a specific ID. Note that this covers dialog boxes in
Windows and forms in MacOS.

Those three classes form the "core" windowing model.

> ** layout algorithms (placing visual elements relatively to each other),
> language to express constrains.
>
> ** widgets, controls, text engine and etc...

These form an extension to the base framework. It would be useful to
have a set of standard cross-platform components (buttons, textboxes,
listboxes, etc.) to facilitate portability, however, these do not need
to be a part of the "core" framework.

Advanced components (tree control, list control, grid control, toolbar,
statusbar, etc.) should be separate. The same goes for layout managers
and views (Q: do we classify views as layout managers?).

> In other words GUI "framework" should have good separation and provide
> set of useful abstractions,
> algorithms which I can use separately (more or less) and bind to
> platform API or some
> platform-specific already existent "low-level" framework (MacApp, MFC,
> QT, WxWindows... ).
> I believe it's possible and I'm not the only one who wants it.

See above.

The boost-sandbox code is mainly Win32/64, but contains a mechanism for
switching platforms, as well as basic support for Cocoa and PalmOS (i.e.
the geometry classes). There are still several open issues:

* How do I register a window type in a way that is platform
independant, supports built-in components and "common controls" in
windows (requiring a call to InitCommonControls(Ex).

The problem is that Windows requires a window class to be registered
once before a window can be created. Since I am using the constructor to
create a window, I cannot use a virtual function to get the name of the
class and register it since the vtable is not resolved in the base
window class. I don't want to templatize the class for reasons stated above.

The solution I have arrived at is to use a static instance of a class
that will register itself on construction. E.g.:

class frame: public gui::component
{
    static gui::platf::window_class wc;
    public:
       inline frame( ... ):
          component( wc(), ... )
       {
       }
};

gui::platf::window_class frame::wc( "CppGUI::Frame", COLOR_WINDOW );

* I am still using Windows-specific code in several places (especially
when providing constants). This includes the names of events and
window/component frame styles.

Regards,
Reece


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