GUI Management Design

Sage
Posts: 1,066
Joined: 2004.07
Post: #1
I'm beginning to work on my own GUI system for my game and couldn't think of a real efficient way to handle moving inputs from keyboard and mouse to the widgets and then handling responses. I'm thinking that widgets will all have action listeners that will be called whenever the widget takes any input, but I wasn't sure that would work for all widget types. For buttons it would be great, but sliders don't have much need for it. Also, filtering input down the GUI system is something I'm also having trouble with. Should all widgets simply be global input listeners and get all the events or should I filter it down from the top of my hierarchy to whichever widget has focus or is being clicked? Any ideas or comments?
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #2
You shouldn't just try to reinvent the wheel; take a look at how existing GUI toolkits handle this stuff, eg. Cocoa (excellent, particularly with Cocoa Bindings), Swing (Mixed -- JTable is excellent, JTextField is awful), Qt (awful), GTK+ (haven't used it enough to form an opinion).

You should also consider that you may not have the same requirements as these systems -- eg. they tend to go to great lengths to avoid re-drawing a widget when the underlying data hasn't changed, where in a game, you're probably re-drawing the whole UI every frame anyway, so such considerations are irrelevant.

Ultimately, whatever you do, make sure that you support the MVC design pattern.
Quote this message in a reply
Member
Posts: 161
Joined: 2005.07
Post: #3
Quote:You shouldn't just try to reinvent the wheel
He isn't reinventing the wheel, he's changing the look of the wheel. Apple does this with every practically every app they've ever released, as do practically all games ever. The only thing consistent between games and apps are the way the widgets look.
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #4
Myabe you should read what I wrote, and what Nick wrote, before jumping at my throat...

All I'm saying is, this is not a simple problem, many clever people have tried and failed to do this well before now, and a few clever people have succeeded in doing it well. He should learn from all these people.
Quote this message in a reply
Jones
Unregistered
 
Post: #5
This is something I have been pondering myself, though I have not yet written any code yet.

I was thinking of making every item declarable as a class. Things like buttons could be stapled to windows and panels, like this:

Code:
WINDOW *myWin = new WINDOW();
BUTTON *myBtn = new BUTTON();

myWin->AttachButton(myBtn);
myBtn->SetPosition(x,y); // this position is relative to the window!

Each object (on creation) would supply a pointer of itself to an internal array. This array would be updated each cycle with some sort of sync command. The ui under-layer would check for events and stuff. Each object could respond to an event with a function the user defined. Like ObjC's 'on push button' type style.

Code:
void stuff()
{
   num++;
}

myBtn->OnEvent(BUTTON_PUSH_DOWN, stuff);

Each item would keep track of it's parent item, so they could interact. The question I was wondering was, how would one keep track of the depth of the windows? In a global array, or in the class itself? What I mean by this is, a window would have an x, a y and a z. The z would define behind which windows the window would be hidden and such.

Hop I don't hijack your thread nick. Smile
Quote this message in a reply
Member
Posts: 144
Joined: 2004.07
Post: #6
imikedaman Wrote:He isn't reinventing the wheel, he's changing the look of the wheel. Apple does this with every practically every app they've ever released, as do practically all games ever.

He's not using an existing GUI layer and changing the theme on it - he's making his own GUI layer. Apple doesn't rebuild their GUI layer with each app.
Quote this message in a reply
Sage
Posts: 1,066
Joined: 2004.07
Post: #7
Jones Wrote:Each object (on creation) would supply a pointer of itself to an internal array. This array would be updated each cycle with some sort of sync command. The ui under-layer would check for events and stuff. Each object could respond to an event with a function the user defined. Like ObjC's 'on push button' type style.

Code:
void stuff()
{
   num++;
}

myBtn->OnEvent(BUTTON_PUSH_DOWN, stuff);

This is kind of how my first attempt went, but it wound up getting very complicated when trying to make specific widget types. It's similar to how Java handles GUI with JButtons and the likes.

To OSC: Why is the JTextField a bad example whereas the JTable is good?
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #8
Nick Wrote:To OSC: Why is the JTextField a bad example whereas the JTable is good?

Because TableModel & TableCellRenderer provide a good way to separate data from presentation -- your TableModel doesn't have to know anything about presentation, only which model object is at a given row and column. The presentation is all handled by the TableCellRenderer, which can change any graphical property based on the value object. TableModel is also very easy to implement (if you extend AbstractTableModel you need only supply 3 methods for a non-editable table, and four or five for an editable one).

In contrast, JTextField only allows you to use a Document, which is *extremely* complicated to implement, and seems only to support text, not arbitrary types (numbers would be handy, for example). It also doesn't provide any way to control the appearance of the field (eg. font, color) based on the value in the model, or validate the input before committing to the model.
Quote this message in a reply
DarkHorizon
Unregistered
 
Post: #9
When I created the windowing system for my game's GUI, I used a basic principle, that all widgets in the GUI system derive from the basic CWindow class. Each one has color, dimensions, positioning, and a message handler.

Then, each subclass has object-specific features. Buttons have idle/mouseover/pressed states which refer to sprites in my sprite system. Text boxes have font and text members. Etc.

The reason for this layout, was to be able to centralize and unify the format in which messages are handled. E.g. every type of object has an OnPaint() function to render it, an OnClicked() to handle mouseclicks.

Then, when a user-event occurs, simply send the message from the 'desktop' (top level) window to each of the children window, see if the mouse cursor intersects, and call the related window's handler function based on the type of event.

It's complex, yet simple if planned accordingly.

Drop me a line if you'd like to discuss more or take a look at my code.

Cheers,
Quote this message in a reply
Sage
Posts: 1,066
Joined: 2004.07
Post: #10
@DarkHorizon: Thanks. I'll see if that idea works for me how I'm thinking right now.

@OSC: How would you suggest researching how Cocoa does the UI? Would just browsing the online API be the best route or is there a more detailed look into how it works somewhere?
Quote this message in a reply
Oldtimer
Posts: 834
Joined: 2002.09
Post: #11
The way I've done it is a simple tree approach. You create a base container, and then create buttons and widgets and other containers inside it. I don't bother with automatic layout, since for a game GUI, I'll just tweak the data files (I use XML, but in a way, just creating everything in code would've been good enough if Galder didn't have such a goddamn tricky UI.)

Then, when an input comes from keyboard or mouse, I just send it to the root container. The mouse coordinate is transformed into the space local to that widget, and then sent to every widget that is intersected. (Typically just one.) They in turn transform the mouse position into their respective coordinate frames and so on down the tree. Eventually, a widget will say that "I handled this!" and in that case, the input is "eaten" and won't affect any other widgets - the evaluation is done.

One thing I really recommend you to do, however, is to create a sort of input layering system. In this case, each window would be considered a layer, as would the game. I then keep a stack of layers around, where the game is at the bottom. When the GUI is up, I push it onto the stack. If that GUI opens a modal window (such as an alert) that alert goes on top of the stack. The trick is to only send input to the top-most element in the stack. When a GUI is "done", it gets popped off. This way, you can easily make sure that the game doesn't handle input that is meant for the GUI, and you easily stop players from messing with your controls when an alert is up.
Quote this message in a reply
Member
Posts: 161
Joined: 2005.07
Post: #12
lightbringer Wrote:He's not using an existing GUI layer and changing the theme on it - he's making his own GUI layer. Apple doesn't rebuild their GUI layer with each app.
He's probably making a cross-platform solution. Making GUI controls isn't terribly hard for the most part, but I guess it depends on how in-depth he plans on going with these controls - it could go anywhere from a few sliders and buttons to entire tables and multiline text fields.
Quote this message in a reply
Sage
Posts: 1,066
Joined: 2004.07
Post: #13
imikedaman Wrote:He's probably making a cross-platform solution. Making GUI controls isn't terribly hard for the most part, but I guess it depends on how in-depth he plans on going with these controls - it could go anywhere from a few sliders and buttons to entire tables and multiline text fields.

I was going to try and go the full length with tables and all the fancy widgets. I'm planning on devoting a few months for it (at least to start. possibly more when I get more into it). And yes, I'm going for cross platform (currently I pass SDL_Events down to widgets, but I'm going to make my own structures for all the input when I get the structure of the GUI layer more fully developed).

Anyone know how TomorrowPlusX does his GUI? It's probably the nicest looking GUI I've seen in a game and has lots of nice features (I noticed in one demo the bottom panel actually animated while resizing). Just thought it was a nice model to use.
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #14
Nick Wrote:Anyone know how TomorrowPlusX does his GUI? It's probably the nicest looking GUI I've seen in a game and has lots of nice features (I noticed in one demo the bottom panel actually animated while resizing). Just thought it was a nice model to use.

I had just written a long post, but safari crashed. I can't believe it, Safari never crashes for me.

Anyway, two things. One, I've been polishing my basecode for public release. I might as well do it today, since I could polish it forever. I don't expect people to use it whole-hog, but there's ( I hope ) some decent stuff in there. It's not an engine -- it has no scene graphs, no model loading. Just the kind of stuff GLUT provides but in a nice C++ manner, with support for a gui, glsl, textures ( from disk, from FBO, etc ), a bunch of useful math, frustum stuff, camera stuff, C++ classloading from the app itself or from plugins ( neat! hacky! ) and so on.

Regarding the gui, mine's mostly similar to fenris', since it's also just a tree, with events trickling from leaf to root. I use layout management, since I don't want to specify layout in XML -- I'd much prefer to be able to robustly layout my gui in code. Also, for events which dispatch externally from the framework to the user, such as what happens when you drag a slider, enter text in a field, click a button, or whatever, I use signals and slots. I love signals and slots, and as such I use a great open source typesafe templated c++ implementation slotsig ( slotsig.sf.net ).

Layout management & signals/slots make for exceptionally clean and readable code.

Code:
void MyController::makeGUI
{
    // set up the root
    ui::RootPane *root = new ui::RootPane( ... );
    root->setLayout( new VBoxLayout() )
    
    // row of stuff across top
    ui::HBox *row = new ui::HBox( root, ... )
    
    ( new ui::Button( row, "Foo" ))->onPress.connect( this, &MyController::onFoo );
    ( new ui::Slider( row, "Slider" ))->onChange.connect( this, &MyController::onSliderChange );
    
    _fpsLabel = new ui::Label( row, Font::MonospaceBold ); // themes can change what Font::MonospaceBold maps to
    _fpsLabel->setSprings( false, true ); //spring for width
    
    // spacer to keep the rest of the screen clear
    Spacer::forHeightAndWidth( root );
    
    // add a timer to update fps label, updates every second
    Timer::continous( 1 )->onTimout.connect( this, &MyController::onFPSUpdate );
    
    // add a key combo to go fullscreen
    Key fullscreenToggle( 'f', keycode::Key_Command );
    fullscreenToggle.onPress.connect( this, &MyController::toggleFullscreen );
    app->addKey( fullscreenToggle );
    
    // add root pane to hud at top level
    app->hud()->push( root );
}

void MyController::onFoo( ui:Button *src )
{
    printf( "%s clicked", src->name().c_str() );
}

void MyController::onSliderChange( ui::Slider *src, float value )
{
    printf( "%s changed value to: %.2f", src->name().c_str(), value );
}

void MyController::onFPSTimeout( Timer *timer )
{
    _fpsLabel->setLabel( stirnglib::format( "FPS: %0f SPS: %0f ", app->currentFPS(), app->currentSPS() ));
}

void MyController::toggleFullscreen()
{
    app->setFullscreen( !app->fullscreen() );
}

That's a lot of demo code, but it demonstrates -- in my opinion at least -- why layout management and signals/slots are awesome. I've found that it's really easy to whip up simple and complex apps using my framework. As such, I've got 8 or 9 demo apps bundled with the framework ( full source of the framework and demo apps of course ) which demonstrate most of its features. It's also reasonably well documented via doxygen.

When I get a chance today I'll post the whole angry thing.
Quote this message in a reply
Member
Posts: 26
Joined: 2006.09
Post: #15
Unless you are going to do more complex GUI, I'd suggest using IMGUI. It is very quick and easy method and suits games really nicely.

The idea in a nutshell is this:

for(each frame)
{
if(DoButton("Press Me!", 10, 10, 150, 30))
LOG("Button pressed");
}

That will handle the button logic, rendering and returns true when the button is pressed.

IMGUI is not a silver bullet, but it is rally nice solution for certain kind of GUIs.

See this forum for more information:
http://www.mollyrocket.com/forums/viewforum.php?f=10
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Importance of memory management johncmurphy 11 6,402 Aug 27, 2009 02:54 PM
Last Post: AnotherJake
  Objective C memory management Madrayken 3 2,808 Jul 10, 2009 10:13 AM
Last Post: DoG
  Input Management bmantzey 9 3,857 Sep 24, 2008 06:26 AM
Last Post: bmantzey
  Input Management chainsawmcgraw 4 3,503 Nov 5, 2003 09:00 AM
Last Post: DoG