Jump to content
  • Advertisement
Sign in to follow this  
Funkiemonkey

GUI Polymorphism.. Grr!

This topic is 4250 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm kinda stuck and have a question regarding GUI class design. I'll give you the header file to my class, but before you read it, I know it probably isnt the most elegant/beautiful/efficent, but it works!... Kinda! :) The problem I have with it however, is that I'm storing all the derived widget classes in one stl container (for neatness and speed). The problem with this though, it for all the derived classes to fit in, the container must be the same type as the base class (cWidget). But this means if I return the pointer to an object I cannot access the derived class functions, such as SetFramePosition() on timer bar. So what can I do about it? I've been racking my brains over it for a few days now but nothing springs to mind. I guess maybe it calls for a whole new design?...... I hope not! lol Any help is appreciated! :)

#include <Windows.h>   
#include <d3dx9.h>     
#include <vector>
#include <iostream>
#include <iterator>
#include <map>

using namespace std;

class cWidget
{
	public :

		cWidget(){}

		void Create(int x, int y, const char* widget_text, int widget_ID);
		void SetPosition(int x, int y);

		virtual void SetText(const char* widget_text)=0;
		virtual void Render(LPD3DXSPRITE sprite, LPD3DXFONT font, LPDIRECT3DTEXTURE9 user_interface)=0;
		virtual void Update(int x, int y, int state)=0;

		RECT text_position;
		LPCTSTR text;

		D3DXVECTOR3 position;
		D3DXVECTOR3 color;

		bool b_mouse_over;
		bool b_mouse_down;

		int ID;
};

class cButton : public cWidget
{
	public :

		cButton();

		virtual void SetText(const char* widget_text);
		virtual void Render(LPD3DXSPRITE sprite, LPD3DXFONT font, LPDIRECT3DTEXTURE9 user_interface);
		virtual void Update(int x, int y, int state);

		RECT mouse_over;
		RECT mouse_out;
};

class cTimerBar : public cWidget
{
	public :

		cTimerBar();

		virtual void SetText(const char* widget_text);
		virtual void Render(LPD3DXSPRITE sprite, LPD3DXFONT font, LPDIRECT3DTEXTURE9 user_interface);
		virtual void Update(int x, int y, int state);

		void SetFramePosition(int x);
		int GetFramePosition();
		void SetNoOfFrames(int frames);

		int previous_state;
		int previous_x;

		int no_of_frames;
		int current_frame;

		float frame_inc;
		float mouse_drag_distance;

		vector <float> frame_coords;

		RECT bar;
		RECT marker_up;
		RECT marker_down;

		D3DXVECTOR3 marker_position;
};

class cCheckBox : public cWidget
{
	public :

		cCheckBox();

		virtual void SetText(const char* widget_text);
		virtual void Render(LPD3DXSPRITE sprite, LPD3DXFONT font, LPDIRECT3DTEXTURE9 user_interface);
		virtual void Update(int x, int y, int state);

	bool b_checked;

	RECT checked;
	RECT unchecked;
};

class cGUI 
{
	public :	

		cGUI(){}	
		~cGUI();

		HRESULT Create(LPDIRECT3DDEVICE9 pDevice);

		void AddButton(int x, int y, const char* widget_text, int ID);
		void AddCheckBox(int x, int y, const char* widget_text, int ID);
		void AddTimerBar(int x, int y, const char* widget_text, int no_of_frames, int ID);

		void DeleteWidget(int ID);
		void Refresh(int mouse_x, int mouse_y, int state);
		void Render();

		cButton* Button(int ID);
		cTimerBar* TimerBar(int ID);
		cCheckBox* CheckBox(int ID);

	private:

		LPD3DXSPRITE sprite;
		LPD3DXFONT font;

		LPDIRECT3DTEXTURE9 interface_texture;

		map <int, cWidget*> widgets;
		map <int, cWidget*> :: iterator i;

		int absMouse_x;
		int absMouse_y;
};

Share this post


Link to post
Share on other sites
Advertisement
You can create separate containers to store each type of widget. To implement the method to get a button you'd get the button from a container that contains only buttons.

With your design, it might be better to not store all the widgets in one container. Most GUI systems store everything in one container because they are designed to be extensible and so they won't know all the widget types that will be added anyway. Your system is not designed to be extensible, and so there's no reason to not store things in separate containers based on type. You could also eliminate the virtual function calls. Unless of course you're planning to add some room for extensibility.

Share this post


Link to post
Share on other sites
Yeah, your probably right, I had a thought about doing that but I've just learnt about virtual functions and stuff and really like em in my code. Just wondered if there was away around. Ah well! :)

So what happens in GUI which has extensible widget types then?

They all seem so complicated! :s

Ah well

Thanks for the help!

Andy

Share this post


Link to post
Share on other sites
More extensible GUI systems are designed in a way that the user of the system can make their own widgets (in an object oriented language this is usually by subclassing base types or implementing an interface, another way is to give the GUI customized function callbacks to use). The GUI just has a polymorphic list of the base type, or a list of the callbacks it was given, or whatever. The user creates the widgets and passes them to the GUI system. Since the user created the widgets, they can have references to them that are stored in variables of the actual type outside of the GUI system.

This sort of system is clearly more complicated because of issues like widget ownership (who is responsible for owning the memory), how do you safely add, remove, and make changes to widgets that are in the GUI system, etc. Garbage collecting schemes (either built into the language or using smart pointers) can help ensure that you don't have ownership issues, but that still leaves some logical issues like how to notify the widget system to remove or update a widget. Some systems are designed to be inherently multithreaded and that makes things even more complicated (Java's AWT and Swing can be nightmares).

By having only a fixed set of widgets you're making things much simpler for now as long as they meet your needs.

Share this post


Link to post
Share on other sites
There is nothing wrong with the design. What you need is dynamic casting, that is, converting a base class pointer to a derived class pointer. C++ has this built in, it's the dynamic_cast operator and is used like this:

derived_ptr = dynamic_cast <DerivedType> (base_ptr);

It returns 0 if the base_ptr is not a base class for DerivedType and the correct pointer if it is a valid cast.

Usually this is an optional feature which requires a command line option to activate. It does add an overhead to the size and speed of your program. I'm not certain what this overhead actually is.

Alternatively, you can implement your own system quite easily:

class Base
{
public:
virtual void *ConvertToType (TypeID &id)
{
return 0;
}
};

class Derived : public Base
{
public:
static TypeID m_id;
virtual void *ConvertToType (TypeID &id)
{
return id == m_id ? reinterpret_cast <void *> (this) : Base::ConvertToType (id);
}
};

// example use
derived_ptr = reinterpret_cast <DerivedType *> (base_ptr->ConvertToType (DerivedType::m_id);
// or as a template function (haven't tested this)
template <class B, class D>
D *ConvertPointer (B *base)
{
return reinterpret_cast <D *> (base_ptr->ConvertToType (D::m_id);
}


Skizz

Share this post


Link to post
Share on other sites
I ran into the same issue with my own system. What I ended up doing was including in the base class a member variable that indicates what kind of widget it is. Also included in the base class header was an enum that contained a list of all possible widgets. The base class initialized the member variable to an invalid widget type while each derived class's constructor set the widget type for that particular widget.

This allowed me to store all widgets in a single list of base class pointers. Most action was done on the widgets using base class functions like Draw(), or SetPosition(), etc. However, whenever I need to access the functions specific to a particular widget, I simply looked at the widget type member variable and dynamic_cast-ed it to the appropriate type.

Not sure if its the cleanest method, but it definately works for me. Every time I create a new widget type, I simply update the enum in the base class with the new entry and I'm good to go.

Share this post


Link to post
Share on other sites
A way to solve this is, if you can afford it, is to have the cGUI class not make a distinction between widgets. For example, when the user clicks on the widget, the cGUI does not go "hm,let's see...Is it a button? Then I must call the Press() method. Is it a Label? Then I do nothing,etc.". cGUI will just send the cWidgets events, and the cWidget will decide how it will handle it. For example, the cWidget has a virtual LButtonDown() method. When the user clicks the left button over a widget, cGUI calls widget->LButtonDown(). What happens next, is up to the widget. The various widget types will respond differently to the same event.

Share this post


Link to post
Share on other sites
@mikeman: That's exactly what I did as well. The base class for all my widgets has virtual functions for:
mouse events (movement and button presses/releases)
keypresses
focus related functions (tabbing, losing focus because another widget gained it, etc)
drawing

This way, the interface to the widgets are generic enough that you can process most interaction with your widgets via base class pointers. Only when it comes to setting/retreiving values to/from my widgets do I need to dynamic_cast to their actual derived class pointer types.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!