Jump to content
  • Advertisement

Archived

This topic is now archived and is closed to further replies.

Falagard

User Interface - Opinions and Ideas (long)

This topic is 6909 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''ve been looking at how to create a full featured user interface for a game that requires a good GUI. (Roleplaying Game). I''ve read over the few tutorials here at GameDev, as well as looked at source code for several different game GUI implementations, including Allegro''s GUI code, and LibUTA and ClanLib (which use the same GUI code... one of them has copied and pasted the other, I''m certain). Allegro''s is very procedural and written for C coders, not C++. ClanLib is more interesting - basically using the approach suggested on the tutorials found here. Now the problem I had with the tutorials here, was that although they helped you figure out how to program the GUI, they didn''t delve into how to implement the GUI. What I mean by this is ... they explained how to create GUI elements that can have children, and the basics about sending events down from parents to children, such as mouse clicks, but they didn''t explain how you would take these GUI controls you''ve created and throw them into a game. Picture it this way - you''ve created a nice library of controls. You have a basic GUIWindow class that all other classes are derived from, and then a GUIButton class, a GUITextBox class, etc. In addition, you have the code that handles mouse clicks and fires off the GUIButton::Clicked function whenever a mouse clicks inside the boundaries of the control - but - where do you put the code that executes when that button is clicked? You might be thinking "that''s easy - you need to subclass the control and overload the clicked function". Yes - that''s option number one. You would have code that would look something like this: class QuitButton : GUIButton { virtual void Clicked(int x, int y); }; QuitButton::Clicked(int x, int y) { //do some clicked code here }; Then somewhere in your app you could create a new instance of the QuitButton... and whenever it is clicked it fires off the code. Option # 2 is to use function pointers to tell a button what to do when it is clicked. For example, you wouldn''t subclass a GUIButton, you''d create a GUIButton and tell it what function to fire when it is clicked. It''s a little complicated, but apparently works... it''s the method used by the ClanLib libraries, and LibUTA, and a few other GUI implementations. Basically, it goes like this: GUIButton Button1; connect(Button1::Clicked, QuitFunction) Where the connect function does some nifty method to make it so any time the Button1 fires its Clicked function, the QuitFunction is fired also. Now, I personally think the first approach is better, but I think there may be problems with it that I haven''t thought of. Any suggestions/comments? Clay Larabie

Share this post


Link to post
Share on other sites
Advertisement
Guest Anonymous Poster
The beauty of function pointers is that they can be changed at run time. But I also agree with you and think they are GENERALLY a bad programming practice.

Share this post


Link to post
Share on other sites
I kind of handled this the good ol'' windows way. Every UI widget is a window or a combination of windows. They all get messages from the OS or other windows through my interface manager.

As defined, each window type (buttons, edit boxes, regular windows, etc...) have a default static message handler. For buttons, when a button is clicked, it will send a BTN_CLICKED message to it''s parent, specifying a couple things like which button it is, etc...

In order to handle the messages, you can specify a custom message handler (via a function pointer) during the initialization of the window (or button or whatever). So I guess I''m just using a different version of option 2.

One problem I see with option 1 is that you would have to create a new class for each button that has a different function so if you used twenty buttons in your project, you would probably have twenty different classes (on for each different button) and the base button class, that is if the code for doing whatever the button does is in the Button::Clicked() function.

You could,instead, put the code for handling the buttons in the parent window, but the buttons would have to know what function to call in their Button::Clicked() function, but then you''d end up using a function pointer anyway.

Mark Fassett
Laughing Dragon Entertainment
http://www.laughing-dragon.com

Share this post


Link to post
Share on other sites
Well, yes, that''s a third option. All GUI controls could have an identifier that is set during creation that would let the application know what was clicked.

GUIButton Button1(BUTTON_QUIT);
GUIButton Button2(BUTTON_OKAY);

Then each button, when clicked, could call a generic function and pass the ID. Example

GUIButton::Clicked(x, y)
{
Parent.HandleButtonClick(m_ButtonID, x, y);
};

And the parent window would have a select case that would handle each different circumstance.

Now, the reason that subclassing all GUI elements is nice is that you can then make GUI controls that are properties of other gui controls. Umm..for example, imagine I have created a GUIWindow class... and now I want to make a QuitDialog window, with an Okay and Cancel button.

class OkayButton : GUIButton
{
//stuff here
};

class QuitButton : GUIButton
{
//stuff here
};

class QuitDialog : GUIDialog
{
QuitButton quitButton;
OkayButton okayButton;
};

Now, the application creates an instance of QuitDialog...

void main()
{
QuitDialog quitDialog;
quitDialog.quitButton.setFocus();
quitDialog.okayButton.hide();
};

///////////////////////////

It would be more difficult to refer directly to controls like that if you aren''t subclassing.

See what I mean?

If you had a generic Widget class, and all it had a pointer to its Parent widget, and an array of Child Widgets, how do you refer directly to one of the children and call a specific function of that child? Especially if it is a function that is specific to that type of GUI control. Like... a scrollbar. You could use dynamic_cast<> but who would want to?

Clay


Share this post


Link to post
Share on other sites
The thing that bothers me with that approach (subclassing your generic control class to get each individual control) is that you end up with lot''s of different classes that are fundamentally the same. I think it makes the project more complex for very little gain.

If instead, the generic button class can be told where to send it''s message that it''s been clicked (to the parent window), you can have that one section of code determine what happens based on the buttons ID. If all the controls send their action messages to the parent window, then you have their actions grouped into a single place by your parent window, and it is much less taxing on the brain cells.

It is a little more difficult, perhaps, to set up, but not much, and I think the benefit (limiting the code complexity) is far greater than the initial work outlay.




Mark Fassett
Laughing Dragon Entertainment
http://www.laughing-dragon.com

Share this post


Link to post
Share on other sites
Even if you use the function pointer method of handling events, nothing prevents you from subclassing the container classes so that you can still refer to individual components within the container. And instead of using separate classes, the constructor of the container simply assigns the proper function pointers to it''s child components. After all, you would never have references to simple base widget classes. You would have references to subclasses of the widget class that specify events that are handled. i.e. Your UIButton class will handle click events and your UIScrollBar class. So your QuitDialog class will create two separate UIButtons and assign different function pointers to each of the buttons. In practice when refering to GUI components in code is unimportant to know the specific behavior of the component in response to the event, just which events the component handles.

An alternate to function pointers is function objects. The advantage to this, aside from elminating the use of actual function pointers, is that you can also assign state to the function objects. This allows you to do many of the things that would normally require subclassing. Ex: a function object assigned the the event handler of a Button could count the number of times the button is clicked, and pop up a message on the fifth click. The disadvantage is, of course, the additional level of indirection.

Share this post


Link to post
Share on other sites
The funny thing I keep hearing is you guys saying stuff like "it''s a little more complex" to do it with function pointers, and doing it this way or that "allows you to do many of the things that would normally require subclassing".

That''s the other thing I had been thinking of was maintaining state for a button... which could use function objects, but that seems more complex to me.

MFC and Visual Basic are using subclassing for all their GUI stuff aren''t they? If it''s good enough for them...

Clay

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
uhm, there''s actually a fourth option..
create a button class which sends an event to it''s parent that it''s been pressed, but along with this event it sends an ID code with which the parent can determine which button has send the event..

so you could have button A (quit button) and button B (okay button)
and in the parent you could have some code (maybe using a pointer to a function) which handles those events and executes the right code by determining which button was pressed.

Share this post


Link to post
Share on other sites
Prefer composition to inheritance! The only difference between a Quit button and a DoSomethingElse button is the action to be taken when it''s clicked. So, your buttons should take some sort of function pointer, or function object, etc, and the OnClick() function calls whatever function is assigned to that button. This allows you to change functionality at runtime, which might be useful if you ever want the user to be able to customize a toolbar, for instance. It also means you don''t have lots and lots of little classes which are almost exactly the same. The only thing that changes is the action: so they should be the parts that are duplicated and altered, not the buttons.

Share this post


Link to post
Share on other sites
Explain the function object idea?

Basically, the problem with a function pointer, as has already been mentioned, is the idea of a button having special properties - special states. You know, a static variable that might be specific to the button.

For example, a typical list box might have properties like SelectedIndex, ListCount, etc. These things are default to the ListBox class. BUT... what if you create a ListBox and you want to store things like the last item that was selected, how many times the user has used the box, etc. Basically special variables that are not part of the class itself. These things are very common when programming GUI. I believe the function object solves this, but I don''t understand the concept fully.

Clay

Share this post


Link to post
Share on other sites

  • Advertisement
×

Important Information

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

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!