Jump to content
  • Advertisement
Sign in to follow this  

messy problem with my GUI

This topic is 4316 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 made an OpenGL GUI system in C++ that I use in games, but it's a terrible messy piece of code, but highly useful to me. It's also a huge amount of different classes, with many inheritances and dependencies on each other. I'd like to clean it up, especially the interface. The thing I have the most problems with, is the way to make the elements. First, I did it with different constructors, but later I decided I wanted only one constructor per element. So I made one constructor per element, and then made a function called make in which you give a lot of parameters, to give the element the properties you want. Now more about these make functions: -a make function of an element has TONS of parameters. The first 4 parameters are always x and y, the next 2 usually (but not always) sizex and sizey. After that, come parameters with default values, such as, texture(s), color(s), fonts, texts, etc... -every type of GUI element has its own make function. They aren't overloaded. The reason is because every GUI element has its own kind of parameters, e.g. a button can need a text or an image, while a container doesn't. -some GUI element have multiple make functions, for example the button has "makeImage" to make one with an image, but "makeText" to make one with text. -You have to do more than just call a make function to really make the element the way you want. For example for a gui window, you have to call "addTop" to add a blue bar at the top, "addCloseButton" to add a close button, "addResizer" to add a thing with which you can resize it, and so on... Basically, I find this terribly messy. Is there a clean, uniform, strategy, that could allow me to give many different kinds of GUI elements, that all have many different kinds of parameters and options, each their properties like position, size and visuals? I don't like the make function system because each one has a different one, they aren't overloaded, the order in which the parameters with default values are given is arbitrary, and so on... Even worse is the adding of elements to windows: I've added some options to say what behaviour elements in windows should have when the window resizes (such as, should this element resize together with the window or not, should it follow the left, right, bottom, top side, ...), but all these properties have to be given to the element by different C++ function calls, hardcoded. Do there exist better ways for this? A second problem I'm having with this GUI system is that it isn't always the easiest to use. There's a lot to keep in mind. First you have to make a general container, in which the elements are pushed. You have to keep in mind that you can only give the textures to GUI elements after the textures were loaded. Then, of course, you have to call all these main funtions. Very often coordinates have to be given so I have to think a lot about "which coordinates would be best for this or that". For checking things like "pressed()" with the mouse, you have to keep in mind that doing pressed() twice in a row during a single frame will give unexpected results, because the first time you use "pressed()", the second time a variable is set so that it'll return false. Are there cleaner ways for that? And finally, a third problem I have is that you have to keep a LOT in mind when making a new kind of widget: the following functions have to be made: a draw function that draws it on screen, a handle function that controls all interaction every frame, a resize function that controls how it resized (and if this gui widget uses multiple gui widgets internally, like a scrollbar uses some buttons, then you have to specify what happens to all of these elements), a function that sais something about mouse focus for containers (the way the Container handles which elements in it are currently under the mouse pointer works perfectly but looks somewhat clumsy), and so on... Is this normal? Is it in other GUI systems also hard to add a new kind of widget? Basically, I've now spit out some of the frustrations I have with my GUI system, it was designed without much forethought and has became huge, too huge (and too much used by me) to restart from scratch, but I'd really like to improve what there currently is. Even if this thing is only for internal use by me, I still want it to be clean because sometimes it's annoying to work with a clumsy thing! So all suggestions are welcome, and if you have experience with good GUI systems, please let me know what ways they use to solve these problems. Thanks!

Share this post


Link to post
Share on other sites
Advertisement
Perhaps you could put some of this information in files (possibly XML-based). Then you could have a makeFromFile function that will load all of the data from the file. With this, you at least would not have to re-compile your project if you want to adjust the GUI a little, or fill your program with huge make function calls.

You could also make a simple editor that creates these files. This would probably be a bit of work, but it would take the pain out of specifying the endless parameters.

Share this post


Link to post
Share on other sites
What would be needed here is properties. Standard cpp still doesn't have them, but you can emulate them with getN() and setN() methods. A good example could be: getX() and setX(). Most common methods for positioning, resizing and container control can be grouped into a common base class. Also a radio button and a checkbox should come from the same basic button class. All you have to change is the rendering code and the state control code. A window could be composed from multiple classes, one for the window itself, one for the frame (sizable, dialog), one for the title, a couple of state control buttons (min, max, etc) and a client window for the contents.

So you can make:
-gui object (root class)
-window (container class, doesn't render anything and uses all kinds of widgets to draw itself)
-frame (base class)
-frame fixed
-frame resizable
-frame dialog
-button (base class)
-push button
-check button
-radio button
-title button (min, max, etc)
-label (base class)
-label text
-label window (aka. window title)
-input (uses a label for text display and a frame)
-scrollbar (base class, uses 2 buttons)
-scrollbar horizontal
-scrollbar vertical

The code of a base class can be reused by its child classes with inheritence.

ps:
For texture control, just add a texture class, that keeps track of a texture. You can create it by specifying the name of the texture and it can load it on demand when there is a need. (It can even return a white or black blank texture until the load is finished, many mmorpgs use this.)

Share this post


Link to post
Share on other sites
I'm not terribly experienced with UI systems, and I'm not sure my particular setup is 'good', but I've used/improved it a bit.

Quote:

So I made one constructor per element, and then made a function called make in which you give a lot of parameters, to give the element the properties you want.


Why only one function? Wouldn't it be cleaner to make individual functions, possibly via interface design, and possibly with chaining if you don't like multiple lines of configuration?


Interfaces:

class isColorable{
public:
virtual isColorable *Color(colorType SetTo)=0;
virtual colorType Color()=0;
};

class BigUIObject: isColorable, ...{
public:
isColorable *Color(colorType SetTo){
// Do object specific coloring.
return(this);
}
// ...
}

Chaining:
BigUIObject->Color(0xffffffff)->Size(...)->Position(...);


There's probably a nicer way to do such things with references, but it's been some time since I've used C++.


Quote:

-every type of GUI element has its own make function. They aren't overloaded. The reason is because every GUI element has its own kind of parameters, e.g. a button can need a text or an image, while a container doesn't.
-some GUI element have multiple make functions, for example the button has "makeImage" to make one with an image, but "makeText" to make one with text.


In my setup, this is allayed somewhat via a factory. Instead of passing in a text or an image, you pass in a ResourceDefinition. The button then creates a new text or image from the ResourceManager based on the Definition. Still, images and text will have different properties/interfaces so I'd probably make Button into two different concrete classes for them:

ImageInterface
-> ImageButton [with image member]
ButtonBase -<
TextButton [with text member]
/
TextInterface


Quote:

-You have to do more than just call a make function to really make the element the way you want. For example for a gui window, you have to call "addTop" to add a blue bar at the top, "addCloseButton" to add a close button, "addResizer" to add a thing with which you can resize it, and so on...


nothing much wrong with that. If you find a particular combination being used, it's still fairly trivial to move that into a function out of sight.


Quote:

[re: hardcoding behavior]
Do there exist better ways for this?


Most I've heard of use relational coordinates rather than absolute coords. A widget would be assigned to (0,0,.5,.25) which meant 0% of the width to 50% of the width, and 0% of the height to 25% of the height of its container.

Personally, I use generators. Various functors that return a rectangle defining the widget. The behavior then becomes which generator is assigned. Some return the same rectangle as another widget, some keep the widget 'next' to another. It was a major pain to get working, and new generators have to be made carefully since mis-design made them awkward or incompatable with others. Now that the system is mature, it's fairly easy to use and provides auto-arrangement and resizing. Still, it seems pretty awkward at times.


Quote:

[re: containers, texture loading, creation, coordinate assignment, input handling]
Are there cleaner ways for that?


[containers]
Eh, having a general composite pattern for elements seems quite clean and natural to me.

[texture loading]
Once again, using your resource manager as a factory helps here. Images aren't assigned textures by the user, the user requests a image with a specified texture and makes do with what they get. This allows for the resource manager to substitute another image on failure, change resources without modifying UI code, or delay texture assignment until the texture is loaded or video mem frees up.

[coordinate assignment]
This isn't so bad if you remember to incrementally build the UI. Making common 'arrangement' objects like lists and menus simplifies the larger objects into less elements to specify; meaning less coordinates to specify. Also, relational coords might help here.

[input handling]
eh? Not exactly sure what you're talking about.

I've a few problems that certain operations cannot be done in the render loop since they'll invalidate iterators. Having a function registry to do them before, after, or otherwise outside the loop is a simple, albeit hackish solution.


Quote:

Is this normal? Is it in other GUI systems also hard to add a new kind of widget?


Depending on the complexity of the widget. Incrementally building UI objects helps. Providing default mechanisms for things (do nothing on mouseover, render all children, etc) helps.

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.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!