[Help] Metro GUI

Started by
3 comments, last by SFloyd 13 years ago
First off, here is a picture (I will explain in a sec)

2011-04-10_1404.png
(Note: This picture is concept only, I made the 2D graphics, and the 3D character shown.)

Ok, so obviously you can tell I am working on a GUI for a game (Derena). Well, basically this is going to be a Zune like menu system, and I have been researching for days, working on my own GUI system (and failing). So I figured I may as well try a GUI lib. I could not find any libs that were not completly bloated (CEGUI; which happens to be the most popular). [size="1"]pm me if you want more info on the game.


My question is... Are there any GUI libs for OpenGL that would be ideal for this style GUI? Preferably Multiplatform (If it isn't still post).



Thanks,

SFloyd
Hi.
Advertisement
Most important question to make the difference "easy GUIs are easy to do yourself" and "just ignore the bloat". Do you need your elements to have complex scaling like you get in regular GUIs by using lots of spacers and sizers or is it fixed at one size (or at least scales 1:1 with the window size)?

Do you prefer actual events to process in your main loop or direct callbacks? How special will some of your elements get? Will it be limited to labels, pictures, buttons or will you need sliders, check boxes, radio buttons and the like?
f@dzhttp://festini.device-zero.de

Most important question to make the difference "easy GUIs are easy to do yourself" and "just ignore the bloat". Do you need your elements to have complex scaling like you get in regular GUIs by using lots of spacers and sizers or is it fixed at one size (or at least scales 1:1 with the window size)?

Do you prefer actual events to process in your main loop or direct callbacks? How special will some of your elements get? Will it be limited to labels, pictures, buttons or will you need sliders, check boxes, radio buttons and the like?


I guess I will just ignore the bloat for now, my GUI will be a dynamic UI with shuffling and scaling text. I will be utilizing check boxes, radio buttons, etc. But no GUI window management(moving windows with mouse, etc).



Thanks for the response!
Hi.

[quote name='Trienco' timestamp='1302582007' post='4797375']
Most important question to make the difference "easy GUIs are easy to do yourself" and "just ignore the bloat". Do you need your elements to have complex scaling like you get in regular GUIs by using lots of spacers and sizers or is it fixed at one size (or at least scales 1:1 with the window size)?

Do you prefer actual events to process in your main loop or direct callbacks? How special will some of your elements get? Will it be limited to labels, pictures, buttons or will you need sliders, check boxes, radio buttons and the like?


I guess I will just ignore the bloat for now, my GUI will be a dynamic UI with shuffling and scaling text. I will be utilizing check boxes, radio buttons, etc. But no GUI window management(moving windows with mouse, etc).



Thanks for the response!
[/quote]

A simple static tree based GUI system is very simple to implement; do it a few times and it sticks with you forever. Ive done this several times in the past; in different languages, or just for fun. Currently im using one I wrote for C#. For a university assignment I wrote a very simple one consisting of button only for WinBGI. For another university assignment I did a different one in C#, which is designed for XNA, but still supports the mouse (for testing) and keyboard (in a very crude way).

This system consists of a statically build tree of widgets. An alternative to this is an immediate mode GUI which is a different matter entirely; i have no experience with this.

0) Your base class of widget should have a list of child widgets and an API for adding them. Children are linked with their parent and parents to their children - in c++ you will need to use shared_ptr for parent->child relationships and weak_ptr for child->parent. Children have a position which is relative to their parent widget.

For simplicity of implementation, but less flexibility and a lot of annoyance when laying out your GUI, you can just have every widget exist in global coordinates.

1) Special widgets extend the base class and change the rendering behaviour. THey may use a different set of coordiantes when being skinned using a texture.

A more complex option seperates state from rendering but this is not necessary - For a simple implementaion, I just override BaseWidget::draw. More complex implementations use a renderable component. Dont do this if its your first time.

2) Your base class of widget has a series of callbacks for getting events *out* of the GUI - at a bare minimum you need mouseUp for detecting clicking. Each widget will first test itself; if the event is within the bounds of the widget, but no callback was bound, it will then test its children. In C# I use delegates, in C / C++ I use plain old fashioned function pointers, or boost.function / boost.bind.

You can replace this system with a series of direct queries, so that you can do code such as

someButton.wouldBeClicked(int mouse_x, int mouse_y, int mouse_button);

This brings you some benefits of an immediate mode GUI system. If your widgets are all in global coordinates this is just a bounding box test. If they are relative to their parents, you need to calculate the absolute position by following the tree back up through the parents of parents etc.If your widgets do not change depending on whether the mouse is over them, you can use this system exclusively and totally forget about point 3) but I usually have both systems implemented.

3) Your base class of widget will have a series of event *input* functions - these go in the opposite direction, starting on the outer widget and moving down through children, depth first.
At a bare minimum you need onMouseUp(int mouse_xpos, int mouse_ypos, int mouseButton) and onMouseMove(int old_x, int old_y, int new_x, int new_y).

This is necessary for maintaining the state of each widget in a system where things display themselves differently depending on the mouse state. If you drop that feature, you can forget about this.

4) Skinning is relatively easy if you use a basic texture coordinate based system. Each widget is a 3x3 grid of quads, with texture coordinates aligning to a square texture. Each corner is mapped to a corner of the texture, and does not scale when the widget changes size. The left and right edges scale vertically. The top and bottom edges scale horizontally. The centre scales in both directions.

If you use semitranslucent textures and multiplicative blending, this can look very, very cool, for minmimal effort. I tend to use a single vert / frag shader and a single texture for each skin. Ive used several other methods of skinning, including having a mesh for each widget, but this gives the best balance of simplicity and flexibility.

5) Usually your base class of widget would be invisible. You can then make a set of extended widgets like so:
a) A container - extends base and draws itself the same way regardless of the state of the widget. Does nothing else interesting.
b) A label - draws some text. Thats it really.
c) A button - draws itself one way when the mouse is over it and another when the mouse is not. Then draws its text on top.
d) A check box - has an "unchecked" property and a "checked" property, draws itself as a text check box. returns one or the other when you ask it for its value.
e) Radio button - the same as the check box, but is also linked to a list of the other radio buttons it is associated with via weak pointers (c++)
f) Slider - so long as the LMB is down, wherever you move the mouse to within the widget, thats where the slider goes.

Beware of having too deep an inhertiance tree here; i tend to only have one level of inheritance. I can implement all of this in a few hours if the feature set is known at the start. There are of course many far more advanced ways of doing this stuff. I tend to have my Base widget unusually large, but have it uses a series of public instances of more dedicated member classes, so instead of having a billion method calls one will do code such as
widget.components.addComponent(x, y, button);
or
widget.callbacks.addCallback(butnFoo_onClick);

6) When drawing, you need to do the following:
set up an orthographic projection
disable depth testing and depth sorting
iterate the tree, drawing each widget followed by its children.

I tend to have a dedicated GUIRenderer class; so, my draw function typically looks like this:

void Widget::Draw(shared_ptr<GUIRenderer> renderer)
{
drawSelf(renderer);
drawChildren(renderer);
}

I break it up to increase the flexibility of extended classes of widget, as some will override one function or the other, or even both.

This is what I recommend you aim for if you are trying to implement your own GUI from scratch. Obviously, you will get a lot more functionality if you use an existing GUI framework; its that age old decision about whether or not to spend time implementing ones own system.
Don't thank me, thank the moon's gravitation pull! Post in My Journal and help me to not procrastinate!
Thank you speciesUknown, this is just what I was looking for! Thanks for spending the time to write that, this will really be beneficial to me.

Thanks,
SFloyd
Hi.

This topic is closed to new replies.

Advertisement