Skip to main content
GameDev.net gamedev.net
🔒 Locked

GUI: Seperating GUI data from implementation

Started by MaulingMonkey Nov 23, 2004 at 3:48 AM 2 replies 1.1k views
Original Post
MaulingMonkey
MaulingMonkey
In the past, I have been fustrated in attempting to figure out "the perfect design" for a C++ GUI toolkit, as I had a hard time figuring out a good scheme for attempting to meld both implementation that could work within a multitude of contexts (Windows API, X11, OpenGL, etc) and the data of the GUI, as I followed in the steps of so many GUI kits that do so. However, I have realized the melding of these two is probably exactly what I should be avoiding!! My fustration in attempting to generate the desired level of flexibility (a window class should be useable both to access system windowing, as well as be able to provide a window rendered in OpenGL as a subwindow, or as a fullscreen app) stemmed from the attempting to meld multiple implementations, as well as the data. In my new method, classes such as "window" and "font" are simple data (although I will probably include an appropriately wrapped internal pointers allowing classes to store widget-specific cached data), which are then "handled" by implementations. Instead of "gui::button" having a render() function, there is instead a handler that takes care of all implementation details - the equivilant to button.render() would be something like these functions: opengl_context::gui_handler< gui::button >::render( button ); x11r6_context::gui_handler< gui::button >::render( button ); console_context::gui_handler< gui::button >::render( button ); This has nifty features such as allowing, say, a window to be moved between different contexts seamlessly - if managed right anyways. I'm working on building a prototype of this, with the following constraints: 1) Widgets can only be "mounted" once in the GUI heiarchy. The same button can not appear in two seperate windows. This constraint allows me to have a single "context cache" per widget. 2) The first version will only have an OpenGL + FreeType + CSS implementation to start with. I may attempt to port to SDL as well, for the sake of attempting to make sure my implementation truely seperates API from data. 3) Two seperate child lists will be maintained for widgets that have children: Managed and Unmanaged. Managed will be designed for a lifespan that is completely widget-local (the exit button of a window for example) - which will be created directly through template functions of the widget, aka: my_window.create_child< button >( /*...*/ ); my_window.destroy_child( reference_to_button ); This is done to ensure propper new/delete pairing. The data is considered "owned" by the parent, and will be destroyed when the parent widget is destroyed, unless explicitly destroyed earlier via the destroy_child (or equivilant) function. A transfer_child will likely at least be prototyped, to compare against the functionality provided by the second list: A seperate list of "non-owned" (not tied to parent) "children" will be mantained. They will have similar API: my_window.register_child( reference_to_button ); my_window.unregister_child( reference_to_button ); This is done to allow widgets to be displayed that have no fixed owner. A good example would be a sub-window of a program that can be docked. It would be switched from being registered with the window to being registered with one of the "docking groups" of the window. Feedback, links to similar GUI implementations (of which I am aware of none), and critique appreciated. -Mike
MaulingMonkey
MaulingMonkey
Current prototype class list:

gui::widget |-- gui::container_widget |    |-- gui::context |    |    |-- gui::opengl_context |    |    |    |-- gui::glut_context |    |    |-- gui::system_context |    |-- gui::window |-- gui::test_widget


Descriptions

== gui::widget
Base widget from all to inherit from. Currently contains almost no data, except for a dirty flag.

class widget{	bool m_dirty;public:	bool is_dirty( void ) { return m_dirty; }	void dirty( void ) { m_dirty = true;  }	void clean( void ) { m_dirty = false; }	widget() : m_dirty(false)	{	}	virtual ~widget()	{	}};


== gui::container_widget (inherits from gui::widget)
This superclass mantains a std::set of pointers to managed children, and another of unmanaged.

class container_widget : public widget{	//No, I'm not using namespace std; into the global namespace, I've used it into the namespace "industry" which all this is contained in.	set< widget * > managed_children;	set< widget * > unmanaged_children;public:	virtual ~container_widget()	{		for_each( managed_children.begin() , managed_children.end() , util::deletor<widget *> );		assert( unmanaged_children.empty() );	}	template < typename FunctorT > void for_each_managed_child( FunctorT functor )	{		for_each( managed_children.begin() , managed_children.end() , functor );	}	template < typename FunctorT > void for_each_unmanaged_child( FunctorT functor )	{		std::for_each( unmanaged_children.begin() , unmanaged_children.end() , functor );	}	template < typename FunctorT > void for_each_child( FunctorT functor )	{		for_each_managed_child( functor );		for_each_unmanaged_child( functor );	}	template < typename WidgetT > WidgetT * create( void )	{		WidgetT * w = new WidgetT();		managed_children.insert( static_cast< widget * >(w) );		return w;	}	template < typename WidgetT > void destroy( WidgetT * w_arg )	{		widget * w = static_cast< widget * >( w_arg );		assert( managed_children.find( w ) != managed_children.end() );		managed_children.erase( w );		delete w;	}	void mount( widget * w )	{		assert( managed_children.find( w ) == managed_children.end() );		unmanaged_children.insert( w );	}	void unmount( widget * w )	{		assert( managed_children.find( w ) != managed_children.end() );		unmanaged_children.erase( w );	}};


== gui::context (inherits from gui::container_widget)
Here is were some of the compiler mojo starts to get ugly. It defines a virtual, TEMPLATIZED function, which has no body. It is intentionally not a pure virtual, as not all contexts will be able to render all widgets. I'm worried about wheither or not the compiler will generate sufficient virtual tables. Worst comes to worst I'll probably go over and create my own psuedo-virtual tables.

class context : public container_widget{public:	template < typename WidgetT > virtual void render( const WidgetT & w );};


== gui::opengl_context (inherits from gui::widget)
More compiler mojo. It implements specific widgets. Rendering of widgets in contexts that don't have rendering functions should compile (since It's valid C++), but generate link time errors (since they won't have an instantiation). Hopefully this will be workable such that a user can provide a function like so:

template <> void gui::opengl_context::render< my_widget > render( const my_widget & w );


class opengl_context : public contexet{public:	template < typename WidgetT > virtual void render( const WidgetT & w );};


== gui::glut_context (inherits from gui::opengl_context)
This is the first of the almost-OS level contexts. This is still in the realm of userland data only - this context gets "rendered" by gui::system_context. This isn't implemented yet, but will be shortly.

== gui::system_context (inherits from gui::context)
This is a very special context. User instantiation of this class will be prohibited. This context "renders" other contexts. That is, this is where the mojo to provide a context is provided. It makes the calls to make the glut window sync up with the gui::glut_context data, resizing the window, rendering it, etc, as needed. Unimplemented as of yet.

Feedback appreciated.

-Mike

edit: tabs fixed
edit: Frogot these classes:

== gui::window (inherits from gui::container_widget)
Standarish widget. Pretty self explanitory - it represents a window.

== gui::test_widget (inherits from gui::widget)
This is a widget to be used to aid in the testing of the library. Pretty much, this should be rendered by a context somehow - dosn't matter really. A context could render this as a green square, or an implementation of gl gears.

edit: I've also fixed the classes found in the gui namespace to all have virtual dtors.
Kwizatz
Kwizatz
Damn!

That was me [smile], the zipped code is here

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.