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

Opensource OpenGL GUI

Started by Prozak Jan 30, 2004 at 5:58 PM 11 replies 1k views
Original Post
Prozak
Prozak
What open source opengl guis are there? Are they purely graphical or do they rely on mechanism similar to the win32 api? Thanx for any tips/links on this... Jester Says: Visit Positronic Dreams!
[Hugo Ferreira][Positronic Dreams][Colibri 3D Engine][Entropy HL2 MOD]
[Yann L.][Enginuity] [Penny Arcade] [MSDN][VS RoadMap][Humus][BSPs][UGP][NeHe]
The most irrefutable evidence that there is intelligent life in the Universe is that they haven''t contacted us!
Promit
Promit
The project I'm actually working on right now is a (not yet released) GPL game which has, a very nice GUI system (which I designed and wrote ).

When you make a window the code looks something like this (real code!):

class WootWindow : public CBaseForm
{
protected:
CBaseForm* frmChild;

CFormLabel* lblWoot;

public:

void lblWoot_MouseDown( CGuiMouseDownEvent* e )
{
Debug( "Woot was clicked!\n" );
}

WootWindow()
{
Caption( "Inventory" );
Font( &BmpFont );
Picture( &WndBG );
X( 10.0f );
Y( 20.0f );
Width( 30.0f );
Height( 15.0f );
BackColor( 0x888888aa );
TitleColor( 0x000088aa );

lblWoot = new CFormLabel();
lblWoot->Name( "lblWoot" );
lblWoot->BackColor( 0x00 );
lblWoot->BorderColor( 0x00 );
lblWoot->ForeColor( 0xffffffff );
lblWoot->Caption( "Money: 5500" );
lblWoot->X( 0.0f );
lblWoot->Y( 2.0f );
lblWoot->ScaleX( 2.0f );
lblWoot->ScaleY( 3.0f );
lblWoot->Font( &BmpFont );
lblWoot->CalcSize();
lblWoot->MouseDownHandler = (MouseDownEventProc) lblWoot_MouseDown;

AddItem( lblWoot );

frmChild = new CBaseForm();
frmChild->Name( "frmChild" );
frmChild->BackColor( 0x44 );
frmChild->BorderColor( 0xffffffff );
frmChild->ForeColor( 0xffffffff );
frmChild->TitleColor( 0x000088aa );
frmChild->Caption( "Child Window" );
frmChild->X( 5.0f );
frmChild->Y( 15.0f );
frmChild->Width( 30.0f );
frmChild->Height( 27.0f );
frmChild->Font( &BmpFont );

//AddItem( frmChild );

}

~WootWindow()
{
//CBaseForm::~CBaseForm deals with destruction of all child controls

}
};

WootWindow* WootMk2;


It's built very much to imitate C#. There are a couple problems with this sytem though:
1) The actual rendering part of things is really, really simple. Or in other words, it doesn't look so hot right now.
2) The window manager isn't properly implemented yet and doesn't work that well with the windows. I'm working on that right now.


It's really not such a great GUI system, except for the actual design; I love the C# Windows.Forms business and I tried to write something similar in C++.

[edited by - Promit on January 31, 2004 10:36:21 AM]
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
Trienco
Trienco
hm, are buttons handled the same? it would mean every button thats doing something else would have to be its own derived class. exactly what i wanted to avoid, but the result isnt much prettier (a callback class with a few different and overloaded function pointers.. wasting more memory than a union, but a little safer to make sure the arguments are matching the pointer).

i thought of gui, but it doesnt seem to be open source, so all i could offer would be search results from google.

you could consider this open source:
http://festini.device-zero.de/downloads/srcc.zip (bin)
http://festini.device-zero.de/downloads/ui.zip (src)

but i dont feel its clean and flexible enough to be of use for others. at least there arent any licenses and you can do with it whatever you feel like ,-)
f@dzhttp://festini.device-zero.de
Jan Wassenberg
Jan Wassenberg
STFW
Wow, the first hit on Google..

// *sigh* must close link tag..

[edited by - Jan Wassenberg on January 31, 2004 9:14:37 AM]
E8 17 00 42 CE DC D2 DC E4 EA C4 40 CA DA C2 D8 CC 40 CA D0 E8 40E0 CA CA 96 5B B0 16 50 D7 D4 02 B2 02 86 E2 CD 21 58 48 79 F2 C3
Deranged
Deranged
edit: hes there first

[Mercury Software] [Google!] [ Look I DONT Follow Trends ]

[edited by - DerAngeD on January 31, 2004 9:15:45 AM]
Promit
Promit
quote:
Original post by Trienco
hm, are buttons handled the same? it would mean every button thats doing something else would have to be its own derived class. exactly what i wanted to avoid, but the result isnt much prettier (a callback class with a few different and overloaded function pointers.. wasting more memory than a union, but a little safer to make sure the arguments are matching the pointer).



I wrote a fairly fully functional button class (CClicky) that does most everything buttons need to do. If you really needed to add something to it, it''d be trivial to derive a new specialized sort of button from CClicky.
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
Trienco
Trienco
how are you going about adding functions? say buttons to start a new game, load a game, quite the game. is there a mechanism to call your own functions via the general button class or would that mean deriving one? i think i should have a look at what delphi and c++ builder are doing. deriving a new class would cause less trouble but the idea of deriving a new class for half the buttons somehow scared me off.
f@dzhttp://festini.device-zero.de
Promit
Promit
Oh no you don''t have to derive something for every single button...there are callbacks for each button to actually do stuff.
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
Megahertz
Megahertz
Where do these callbacks fit into the programs design from an OOP standpoint? I think it would fairly simple to just have them as functions outside of main (like the WndProc for WinMain) but that''s not all the OO friendly.

-=[ Megahertz ]=-
-=[Megahertz]=-
Trienco
Trienco
cant talk for him, because id still like to know how he did get around some limitations (making EVERY callback function look the same like they did in windows with lparam und wparam would probably work well enough for most cases).. what i did is:

first, because that method pointer stuff is pretty ugly:

#define METHOD(x) (void(UIBaseObject::*)(void*))&x

class Callback {
private:
UIBaseObject* obj;
void(UIBaseObject::*func)(void*);
public:
Callback() : obj(0), func(0) {}
void Set(UIBaseObject* o, void(UIBaseObject::*f)(void*)) {obj=o; func=f;}

so we can call it and pretend its an ordinary function
operator()(void* p) {if (obj && func) (obj->*func)(p);}
};

that class is shortened, because the ugly part is that its overloaded like hell with all different variations of the function pointer, depending on argument number and type. limiting that to one common version should be fine.

in a dialogue/form constructor i do this for a button (at least dialogues still all need to be derived).

"this" is the dialogue and used as parent in the button. needed so the components can automatically register with their parents (not too clean, adding the button to the child list in the constructor would be more obvious.. though more work)

SaveBtn = new UIButton(this);
SaveBtn->SetDimensions(100, 250, 80, 30);

Save is a function thats part of the dialogue class we're just writing
SaveBtn->Clicked.Set(this, METHOD(LoadSaveDialogue::Save));

so there arent any global functions flying around, if thats what you expect from an oo approach. the price is that method pointers need a pointer to the object to be passed as "this" and the function itself, which is making it more ugly and causes the callback class.

once its done though we can simply decent through our gui hierachy and once we found the thing we clicked on just call
thing->Clicked()

i tend to replace that overloading nonsense with default parameters and a bunch of helper functions to extract things like mouse position or other parameters from them (youd still have to be careful to extract the _right_ things instead of interpreting the address of the sender as mouse coords).

[edited by - Trienco on February 1, 2004 4:47:07 AM]
f@dzhttp://festini.device-zero.de
Null and Void
Null and Void
quote:
Original post by Trienco
cant talk for him, because id still like to know how he did get around some limitations (making EVERY callback function look the same like they did in windows with lparam und wparam would probably work well enough for most cases).

Take a look at Boost''s function and/or signals libraries. Boost provides a huge number of useful things that I kept ending up having to write myself (for larger projects) before I started using it, I highly recommend the parts of it I''ve used. An alternative C++ slots and signals library is libsigc++ (I think I like Boost''s a bit better; the Boost documentation lists some of the differences).

Useless halfway-on-topic rambling: My last few in-game GUI libraries have been heavily integrated into the scenegraph structures, so they wouldn''t be much use outside of the engines they were designed for. I keep following GTK+ "style of GUI" because I like it a lot and am quite familiar with it for the most part. I advise that you study a GUI library that you really like (and understand the internals of) to base your own GUI libraries on when you write them. If I ever get one of my engines far enough along where it''s completely usable I''ll release it openly (instead of just to people who request it like I''ve done in the past); I just started a "test game" for my newest toy-engine (which has been in development for quite a while), which, of course, is the "real" test to find out what features are truly lacking or misdesigned...

Promit
Promit
quote:
Original post by Megahertz
Where do these callbacks fit into the programs design from an OOP standpoint? I think it would fairly simple to just have them as functions outside of main (like the WndProc for WinMain) but that's not all the OO friendly.

-=[ Megahertz ]=-


It works like C#, VB6, etc. All of the callback event handler functions are actually member functions of the form object in question (look at lblWoot_MouseDown in the code I posted earlier). Then in the init code you simply set the handler to be that function. Some rather complicated vtable and casting work make sure the right messages get sent to the right places.

A little later I'll post a more detailed description of how the event thing works over the whole inheritance hierarchy while still maintaining a 100% OO design.

[edited by - Promit on February 1, 2004 4:06:11 PM]
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
Promit
Promit
The basic premise of an event handler looks like this:
typedef int (CBaseControl::*EventHandler) ( CGuiEvent* e );   

A CBaseControl serves as the base class for all objects in this hierarchy, including forms, containers, everything.

A CGuiEvent looks like this:

class CGuiEvent
{
public:
int hWnd;
string WndName;

//defined by each derived child
virtual int MesgType() = 0;
virtual string MesgDesc() = 0;
};


So from these base definitions, the rest of the event types grow:

//Mouse Down event

class CGuiMouseDownEvent : public CGuiEvent
{
public:
int MesgType() { return GM_MOUSEDOWN; }
string MesgDesc() { return "Mouse button down"; }

float x;
float y;
char Button;
};


//Mouse Up event

class CGuiMouseUpEvent : public CGuiEvent
{
public:
int MesgType() { return GM_MOUSEUP; }
string MesgDesc() { return "Mouse button up"; }

float x;
float y;
char Button;
};

typedef void (CBaseControl::*MouseDownEventProc) ( CGuiMouseDownEvent* e );
typedef void (CBaseControl::*MouseUpEventProc) ( CGuiMouseUpEvent* e );


These definitions are in CBaseControl:

virtual void OnMouseDown( float x, float y, char Button ) { return; }
virtual void OnMouseUp( float x, float y, char Button ) { return; }

MouseDownEventProc MouseDownHandler;
MouseUpEventProc MouseUpHandler;


These are the actual event function pointers (via the earlier typedefs) and base functions which you can override.

Perhaps the most important part is the function that raises an actual event. Here's the RaiseEvent function and sample event handlers in base classes:

//send a message to our handler

int CBaseControl::RaiseEvent( CGuiEvent* e, EventHandler p )
{
if( p == NULL )
return -1;

(this->*p) ( e );

return 0;
}

//events for text labels on a form

void CFormLabel::OnMouseDown( float x, float y, char Button )
{
CGuiMouseDownEvent e;
e.x = x;
e.y = y;
e.Button = Button;

RaiseEvent( &e, (EventHandler) MouseDownHandler );
}

void CFormLabel::OnMouseUp( float x, float y, char Button )
{
CGuiMouseUpEvent e;
e.x = x;
e.y = y;
e.Button = Button;

RaiseEvent( &e, (EventHandler) MouseUpHandler );
}


So basically an event decriptor is created in the derived generic event handlers like OnMouseDown, which is initialized and then sent via RaiseEvent. RaiseEvent invokes the given member function with the "actual" callback sent as a member function with the message descriptor. This causes a couple of casts to base classes. On the other side, the base classes are cast back down to specific event handlers and handled as fit.

//lblWoot_MouseDown is a member of WootWindow, which derives

//from CBaseForm. lblWoot_MouseDown is thus indirectly a member

//of CBaseForm.

void lblWoot_MouseDown( CGuiMouseDownEvent* e )
{
Debug( "Woot was clicked!\n" );
}



And to top it all off, this works with g++ 3.2 or greater and Visual Studio 6.0 or greater.

[EDIT] Source tags got eaten.

[edited by - Promit on February 1, 2004 5:11:42 PM]
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.

Topic Locked

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

Sign in to reply to this topic.