Skip to main content
gamedev.net
🔒 Locked

GUI design question

Started by Thomas M Jan 21, 2005 at 9:03 AM 11 replies 1.8k views
Original Post
Thomas M
Thomas M
hi all, since i need alot of different tools for my new game project (leveleditor, animation editor, etc..) i considered writing an own gui-engine based upon opengl instead of using the crappy winapi or mfc. can anyone give me some hints or a tutorial how i should design the classes and event-handling / message system(button click, window move) in an elegant way? (not like these huge switch / case callback functions in the winapi :P) thanks deloader
Neex
Neex
Hi,

QT's signal/slot mechanism is considered by many to be a nice way of doing things. I'm using the sigslot library in my project, I believe Boost also has an implementation.

I've not put much work into my GUI yet, so I don't really want to comment on my design, I may find it to be horrible at some point down the road :)
Emmanuel Deloget
Emmanuel Deloget
Hi,

I encourage you to dot do this.

A) it is a huge project
B) it is very difficult (you can check the number of code line GTK+)
C) there are tons of other GUI systems out there (GTK+, wxWindows, Borland C++ Builder VCL and so on)

If you want to implement a simple in-game GUI opengl system then you'll see that it is not as easy as it seems.

Anyway, if you need a design information: I like the GTK+ way of doing things. You register callbacks that are called when special events are fired. This is rather easy to do and I believe this is clean. I used to work with GTK+ 1.2.x on my OSS days but I didn't move to v2.x. The whole thing can be translated to C++ (using either pointer to member functions or pointer to static functions or (more difficult) functors).

HTH,
Thomas M
Thomas M
hi,

you're right, i just realized what amount of work is behind a good gui. so i guess i will just stick to the winapi, because there is no sense in writing a gui system which is far more complex than the game i intend to write + the editors together. thank you for pointing out those other gui systems, i will have a close look at them. =)
dave
dave
Im in the process of writing a DirectX gui at the moment called THUD (turbo heads up display) and it is hard work. But it will make games much easier to write in the future.

ace
Koshmaar
Koshmaar
Yes, I agree - it's huge amount of work: I'm writing my own GUI engine so far for 2,5 months, and I think that it won't be finished before 2 weeks :-/ But it's also rewarding - writing good, flexible GUI is rather very complex task so if you do it, you will became much better programmer (at least in terms of code design).

Ok, enough of that, let's go to details:

1. Events/signals/callbacks - I've done one GUI engine which was based on callbacks and it turned to be one, big mess (ok ok, that was my first GUI ever...). Signals sound promising, but I didn't had time to learn them.
I've based my GUI engine on class named TControlHandlers, which is ancestor of TControl, which is in turn ancestor of all other controls: TButton, TCheckbox etc. TControlHandlers in my implementation looks like this:

class TControlHandlers {   public:        TControlHandlers() : me(0) { }      //! Used to bind this handler class to existing control   TControlHandlers(const char * _bindTo);      virtual ~TControlHandlers() { }   // -------------------------------------------------    //! Called (once) when mouse cursor changes from being over one control to being over second control  virtual void GainFocus() { }    //! Send to control that has lost focus  virtual void LostFocus() { }   // -------------------------------------------------  //! Called when user presess keyboard key, if user handler returns false then this _key will be skipped.  //! Also, when TControl's.Keypressed returns false when SDLK_RETURN was passed, LeftMouseButtonPressed  virtual bool Keypressed(SDLKey _key) { return true; }// -------------------------------------------------  // Called when user has pressed appropriate mouse button  virtual void LeftMouseButtonDown(const TScrPoint & _point) { }  virtual void CenterMouseButtonDown(const TScrPoint & _point) { }  virtual void RightMouseButtonDown(const TScrPoint & _point) { }  // -------------------------------------------------    // called when user has released appropriate mouse button  virtual void LeftMouseButtonUp() { }  virtual void CenterMouseButtonUp() { }  virtual void RightMouseButtonUp() { }   // -------------------------------------------------       // called when user released appropriate mouse button, but called only when it happend over control  virtual void LeftMouseButtonPressed(const TScrPoint & _point) { }  virtual void CenterMouseButtonPressed(const TScrPoint & _point) { }  virtual void RightMouseButtonOPressed(const TScrPoint & _point) { }   // -------------------------------------------------  //! User handler is called when Checkboxes state changes to being 'checked'  virtual void Checked() { }    //! User handler is called when Checkboxes state changes to being 'unchecked'  virtual void Unchecked() { }// -------------------------------------------------  protected:         TControl * me;  };


You wonder what's that TControl * me? It's needed to simplify calling "callbacks", you see: TControlHandlers is not only ancestor of all TControl childrens; it's also ancestor of every "callback class"... ie. when you want to call function X when user pressed button, you do sth like this:

class MyButton : public TControlHandlers {  public:     MyButton(const char * _bindTo) : TControlHandlers(_bindTo) {}            void LeftMouseButtonPressed() { X(); }   void RightMouseButtonPressed() { Y(); }   void SthElse() { // ... }   void Foo() { // et } };int main() {  ...  GUIEngine.LoadUI("gui.xml"); // controls must already exist  MyButton button1("NewGame.PlayerScreen.Foo");  ... }


When MyButton will be created, it will bind itself using contructor with const char* which searches for destination control in controls forest (multiple trees of controls) and sets its TControlHandlers * userHandlers to this.
Then, if user clicks on a button, it checks whether userHandlers != 0, if yes, calls userHandlers->LeftMouseButtonPressed(), which will in turn call X().

This method has its pros and cons, but I've done it this way and so far, it's working for me.

Ok, now will be shorter... (sometimes I just don't know when to shut up)


2. Use XML for storing your controls on disk; it's easy to do (at least with TinyXML) and very flexible.

3. Treat UI as tree, where every node can contain many children: let no control live on it's own; in my implementation TScreen was ultimate parent of every other control (but not in terms of code design, rather that TScreen was logical container for controls in XML file); TControl implemented std::vector which is list of immediate childrens of this control. Getting access to control is accomplished by calling GUIEngine::GetControl(const char*) which returns pointer TControl* (or 0) to appropriate control;
GUIEngine is singleton which contains vector of TScreens (+ pointer to active one) and calls GetControl() for one of them, which compares its name with sended name and it recursively calls GetControl() for every child - if one of them returns !0, it's passed to caller.
Using this system getting access to control which is stored even very deep, won't be hard, ie.: TControl * ptr = GUIEngine.GetControl("NewGame.Options.Video.Advanced.Resolution");
ptr->DoSth();


Ups! too long :->

4. Render each children relative it its parent's position - it simplifies *many* things, won't even mention that you will get moving of controls contained inside a window for free ;-)

5. Plan your GUI with flexibility in mind and do it before you manage to write it; espacially when you create sth as important as TControl.

6. Read this series of articles: http://www.gamedev.net/reference/articles/article994.asp, but remember to be critical to it - some solutions are moderately good.

HTH
MGB
MGB
No, not a trivial task at all.
As advised, try to use someone elses or the Windows one.

From experience of writing my own;
1. First fully design the basic mouse/keyboard events and how they are to be handled, and make sure this design will handle everything you want to do in the GUI!
2. Consider using virtual coordinates rather than screen coords.
3. Consider how you will handle the concept of a 'focused control'!
4. Google for 'observer design pattern'.
dave
dave
As difficult as it is said to be i think it is something well worth learning how to do and if you could demonstrate to an employer that u are capable of doing this
i am sure they would be most interested.

ace
Holy Fuzz
Holy Fuzz
Definately make your own gui. It's a great learning experience. It teaches you a huge amount about effective object-oriented design, not to mention dealing with graphics and input.
x_gamer_x
x_gamer_x
Linked lists shouldn't be a problem, unless you have a ton of controls on screen at once iterating through a few *next will be extremly quick.

In reference to std::vector's:
They are resizable (as you add items, the vector will grow) and have constant access time. The drawback (one of them) is slow removal (just as with a typical array).

If you don't know much about the Standard Template Library I highly recommend doing some research.
Stevieboy
Stevieboy
Thanks x_gamer, I was the AP from the previous post. I'm thinking of using a linked list to read in a gui (stored in a directx xfile), but then making pointers in the data part of the nodes to separate each top level item (and their children), into separate trees. I just thought if you use one linked list for the whole gui wouldn't you still have to scan the whole list, if you wanted the last item for instance? I also thought about using an array for each top leval item, but then although they could grow (if you set them up like vectors etc), each leval would still need its own array and you still might not know how much depth you wanted. Other things needed I think are reusibility, ie, being able to make different gui's for different things, -so being flexible with how many of each would be important, & also some sort of editor, even if it's just a console text thing to build a gui (which would then be saved in a file), or maybe just being able to manuelly do it in a text editor.

oh ratings++ to you for being helpful.

Topic Locked

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

Sign in to reply to this topic.