A basic GUI tutorial with example file
by Gonzo T. Clown · 09/26/2004 (5:01 pm) · 16 comments
Download Code File
Make sure the gui is in your files and will exec when you open your TGE program.
To bring up the demo gui just open your console and type in "Canvas.pushDialog(DemoGui);"
without the quotes and it should pop up on the screen you are currently viewing.
I recommend you run your TGE in window mode to read this and view the GUI.
This simple GUI will demonstrate not only some basic gui techniques and functions
but some advanced ones as well. It's main purpose is to illustrate how to build
certain elements of a GUI and show you how to tie them in with external functions
as well as internal controls.
First lets open the DemoGui in the editor GUI. I've arranged 16 buttons into four groups
of four each. Call them Group 1, Group 2, etc... for now. First thing I would like to
point out is that if you click on the "Show All" button, you will see that it's position
is "19 17". If you click on buttons "2a" and "Hide 2" you will notice that they also
have positions of "19 17" yet button "4a" has a position of "239 96". What gives? How
can 3 buttons share the same position? Two answers, 1. They aren't all showing at the
same time, 2. They aren't really sharing the same position. In this case, 2 is the
correct answer.
The DemoGui is a "GuiControl". It's basically the start of a gui and tells TGE how it
interacts with other current GUI's.. Inside DemoGui we have all types of objects including
additional "GuiControl" controls. The buttons inside Group 2 and Group 3 were actually
copy pasted into new controls from the original Group 1 buttons. Because those groups are
each inside their own controls, they can share the same positions as Group 1, while
not sharing the same space as Group 1. Confused? Understandable. So lets look at how we
got to here. The control named "DemoGui" is basically a layer. When DemoGui is pushed
to the Canvas it will size itself automatically to the size of your screen resolution be it
800 x 600, 1024 x 768, etc... and the DemoGui layer will sieze control of the interface so
you can interact with it. The next piece is a new GuiControl named "MainCTRL". This is
the actual box that surrounds the 16 buttons and sets the position of the buttons on the
DemoGui layer. It's size(extent) is 445 x 158 and has been told to position itself in the
center of the screen both vertically and horizontally. The upper left corner on MainCTRL
is represented by "0 0" so in essence it is basically a mini screen within your screen.
The first control inside the MainCTRL is the "Show All" GuiButtonCtrl with a position of
"19 x 17". This position is in relation to the current control under which it resides
(MainCTRL) and is 19 pixels right and 17 pixels down from the upper left corner. The other
three buttons in the first group are positioned based on the MainCTRL position as well.
Next we have another GuiControl named "DemoCTRLB". Essentially another screen within a
screen. DemoCTRLB's position is "221 x 0" and since it is under the MainCTRL's control it
is spaced 221 pixels right and 0 pixels down from the upper left corner of MainCTRL. Grouped
inside this control are four more buttons which have identical positions as the first four
buttons. But since these buttons are grouped under the DemoCTRLB control, their position is
based from the upper left corner of DemoCTRLB and thus the first button in the group
"NameButton" is positioned at 19 pixels right and 17 pixels down from the "0 0" position
of DemoCTRLB. By now you should have a good understanding that everytime you want to add
a grouped element to a GUI, you most likely will need a new control and that each new
addition to your control will be grouped and positioned according to that control's position.
So why group? Good question. I've also taken the time to include some basic functionality
to this gui to demonstrate the uses of such groupings. The first reason why you would want
to group controls would be ease of change. If you had 16 individual buttons under one
control and wanted to move 8 of them, you would have to juggle a lot of buttons for a while
to get them all where you want them. With groups, you can move them all at once. Lets make
a change. In the tree view at the upper right, click on DemoCTRLB so that the box around the
buttons highlights. Now you can reach into the center of that box(dont get a button), click
and hold, and drag that box around and watch it move. Notice it takes the buttons with it
and that they stay in the correct positions? Very handy. Beats moving 4 buttons to a new
position. You could swap DemoCTRLB's and DemoCTRLC's positions real fast if you wanted to
just to see how easy it is to rearrange the positions of a couple of groups.
Ok, swap back to the GUI you were on before opening the editor(most likely MainMenuGui) and
then open your console and enter "Canvas.pushDialog(DemoGui);" (without quotes) and the GUI
should appear on your screen ready to use. Close your console and lets examine a few
things I've set up for you. If you click the "Hide 2" button you will see the group in the
upper right disappear. This is another reason for grouping certain objects in your GUI
structure. You might want to have just a single page that has 5 different sets of options
that you could turn on and off just like you turned off that section of
the DemoGui. And of course if you hit "Show 2", that GUI element comes back. Now, what caused
it to disappear you might ask. Well, the "Hide 2" button has a command attached to it that
is syntaxed "DemoCTRLB.setVisible(false);". All GUI elements and controls use the setVisible
functionality which lets you hide or display them when on screen. In this case DemoCTRLB was
told to hide itself and because the 4 buttons that are in the DemoCTRLB group are subject to
inherit their functionality from the group control, they automatically set themselves to
"non-visible" as well. As if you had actually asked all four of them to hide also. And
when you asked DemoCTRLB to unhide itself the buttons under it's control followed suit and
they reappeared as well. One important factor is that "DemoCTRLB" is an actual name that has
been assigned to that particular control. By giving a control a name, it can be called by
name to perform functions. In this case "DemoCTRLB.setVisible(false);". If the control did
not have it's own specific name, controlling it would become much more difficult and would
reduce functionality.
Not everything needs a name of course. What needs to be named and
what does not is completely dependant on how you intend to use it. This function is
demonstrated a second time by using "Hide 3" which of course hides the third group of buttons
using the function "DemoCTRLC.setVisible(false);". Since the third groups control is named
"DemoCTRLC" the previous function will of course cause it to hide itself and all objects
grouped under it's hierarchy. To bring it back you would use the command
"DemoCTRLC.setVisible(true);" or hit the "Show 3" button which is already linked to that
command. Now, if you hit the "Hide 2" button, and then the "Hide 3" button you will have
two hidden groups. You can bring both back at the same time by hitting "Show All". The
command that "Show All" is linked to is actually a function call to a scripted function
that I made. The command is "DemoGui.action(SHOW);" and it calls the function
"DemoGui::action(%this, %action)". How did this work? Since TGE can use namespace for
function calls, just giving our GUI the name "DemoGui" gave us the ability to create and call
functions using the DemoGui object as our calling object. Thus TGE reads "DemoGui.action" as
"function DemoGui::action(%this)" and since we included the condition of "SHOW" with the
function call, it will be passed on with the call and the end result would be seen by TGE as
"DemoGui::action(DemoGui, SHOW)" which will be handled appropriately by the scripted function
I included as a guide. Studying the function should be pretty self explanatory as to how it
works when called by each of the respective buttons that address it.
Lets look at some other elements I have included. The "Exit Demo" button will of course "pop"
the current GUI by name "DemoGui", and it can do so by two means. You can either click on it,
or you can push the "Esc" key on your keyboard. I bound "escape" to the exit button by
entering "escape" into the button's "accelerator" field. Any key you name in this field will
bind it to that button as long as that GuiControl is active. Meaning, if you have your
cars turbo-boost button set to "b" then you will not boost if you have a GuiControl on screen
that has an accelerator button that is using "B". As soon as the control is eliminated, "B"
will return to controlling your turbo-boost. Take care not to use the same key twice in a
control, and try not to use keys that have global bindings because it probably wont work right.
Also do not use "enter" as an accelerator button. It will not work because the
correct name of the key is "return". A lot of keyboards are now labled "Enter" so it's a
common mistake to use that command. You CAN use the button, but you must label it "return".
The "Alt Exit" button was just another way to demonstrate the scripted action function using a
different means, and to show there is more than one way to close a GUI. Which way is better?
For the most part, the "Exit Demo" command of "Canvas.popDialog(DemoGui);" is the fastest and
easiest way to go for a simple GUI. But there are times when you will want to do things before
a GUI is closed so you might want to send to a function like "Alt Exit" does so you can make
sure the settings are correct. In "Alt Exit" I set the function to make sure that NameButton
is set back to it's original state, set both button groups to visible, and then close the GUI.
The reason for this is that GUI's will remain in their current state unless told to do otherwise.
If you were to hide the two groups and then close the DemoGui, when you opened the DemoGui back
up it would still have two hidden groups. This is not always desirable so care must be taken
to do some cleanup on GUI's that begin to get complex by using multiple configurations or menus.
You could just as easily convert the "Alt Exit" functionality to another function that is automatic called
"onWake". onWake is called by the engine on any Gui that is pushed to the Canvas. So to make
it work, all you have to do is add it and make sure it's exec'ed. Such a function would look
just like this....
function DemoGui::onWake(%this)
{
}
Now, if I take the "Alt Exit" cleanup commands and stick them here, the end result is the same....
function DemoGui::onWake(%this)
{
NameButton.performClick();
DemoCTRLB.setVisible(true);
DemoCTRLC.setVisible(true);
}
No matter what the state of the GUI was before it was opened, the instant it was opened those
functions reset it back to it's original state. Just in case you are wondering, there is a
similar function for when a GUI is closed called "onSleep". If for some reason you wanted to tell a server that a clients GUI had closed, or you wanted to run a function when the GUI was no longer
active you could include it in the "onSleep" function. Remember, this is an auto function called
by TGE whenever it pops a GUI dialog. If the function doesn't exist, nothing will happen of
course, but if you need to have an action at this time, you can immediately add it with no trouble
at all. TGE GUI's are very easy to get along with. Here is an example....
function DemoGui::onSleep(%this)
{
//Do stuff here....
}
Notice the "NameButton.performClick();" function. All buttons can use the ".performClick()" action
to apply them just as if a user had clicked them. This is great to use with interactive demos that you want to walk a player through. You could demonstrate the GUI's functionality when the user clicked
on help and you wouldn't even have to modify the GUI or create an additional GUI for demo purposes.
But that's for another tutorial coming later. In this case, I used it to make sure that the buttons
text was reset to default no matter what by making sure it gets clicked.
Which brings us to the "setText" function. Clicking the "Swap Name" button will cause button "2a" to
change to a new text, in this case "SHWIINNG!!". This was just to illustrate the possible use of this
function which may not mean much at first, but later on I can explain a more powerful reason to do
such a thing. And of course hitting the "SHWIINNG!!" button will cause it to go back to "2a".
Ok, by this point you should understand why you need a GuiControl, how controls and buttons become
children of their parent group and how they relate in position, how to make a GUI interact with itself
as well as with custom scripts, and why it's important to name some elements of a GUI.
Comments? Suggestions? Requests?
Make sure the gui is in your files and will exec when you open your TGE program.
To bring up the demo gui just open your console and type in "Canvas.pushDialog(DemoGui);"
without the quotes and it should pop up on the screen you are currently viewing.
I recommend you run your TGE in window mode to read this and view the GUI.
This simple GUI will demonstrate not only some basic gui techniques and functions
but some advanced ones as well. It's main purpose is to illustrate how to build
certain elements of a GUI and show you how to tie them in with external functions
as well as internal controls.
First lets open the DemoGui in the editor GUI. I've arranged 16 buttons into four groups
of four each. Call them Group 1, Group 2, etc... for now. First thing I would like to
point out is that if you click on the "Show All" button, you will see that it's position
is "19 17". If you click on buttons "2a" and "Hide 2" you will notice that they also
have positions of "19 17" yet button "4a" has a position of "239 96". What gives? How
can 3 buttons share the same position? Two answers, 1. They aren't all showing at the
same time, 2. They aren't really sharing the same position. In this case, 2 is the
correct answer.
The DemoGui is a "GuiControl". It's basically the start of a gui and tells TGE how it
interacts with other current GUI's.. Inside DemoGui we have all types of objects including
additional "GuiControl" controls. The buttons inside Group 2 and Group 3 were actually
copy pasted into new controls from the original Group 1 buttons. Because those groups are
each inside their own controls, they can share the same positions as Group 1, while
not sharing the same space as Group 1. Confused? Understandable. So lets look at how we
got to here. The control named "DemoGui" is basically a layer. When DemoGui is pushed
to the Canvas it will size itself automatically to the size of your screen resolution be it
800 x 600, 1024 x 768, etc... and the DemoGui layer will sieze control of the interface so
you can interact with it. The next piece is a new GuiControl named "MainCTRL". This is
the actual box that surrounds the 16 buttons and sets the position of the buttons on the
DemoGui layer. It's size(extent) is 445 x 158 and has been told to position itself in the
center of the screen both vertically and horizontally. The upper left corner on MainCTRL
is represented by "0 0" so in essence it is basically a mini screen within your screen.
The first control inside the MainCTRL is the "Show All" GuiButtonCtrl with a position of
"19 x 17". This position is in relation to the current control under which it resides
(MainCTRL) and is 19 pixels right and 17 pixels down from the upper left corner. The other
three buttons in the first group are positioned based on the MainCTRL position as well.
Next we have another GuiControl named "DemoCTRLB". Essentially another screen within a
screen. DemoCTRLB's position is "221 x 0" and since it is under the MainCTRL's control it
is spaced 221 pixels right and 0 pixels down from the upper left corner of MainCTRL. Grouped
inside this control are four more buttons which have identical positions as the first four
buttons. But since these buttons are grouped under the DemoCTRLB control, their position is
based from the upper left corner of DemoCTRLB and thus the first button in the group
"NameButton" is positioned at 19 pixels right and 17 pixels down from the "0 0" position
of DemoCTRLB. By now you should have a good understanding that everytime you want to add
a grouped element to a GUI, you most likely will need a new control and that each new
addition to your control will be grouped and positioned according to that control's position.
So why group? Good question. I've also taken the time to include some basic functionality
to this gui to demonstrate the uses of such groupings. The first reason why you would want
to group controls would be ease of change. If you had 16 individual buttons under one
control and wanted to move 8 of them, you would have to juggle a lot of buttons for a while
to get them all where you want them. With groups, you can move them all at once. Lets make
a change. In the tree view at the upper right, click on DemoCTRLB so that the box around the
buttons highlights. Now you can reach into the center of that box(dont get a button), click
and hold, and drag that box around and watch it move. Notice it takes the buttons with it
and that they stay in the correct positions? Very handy. Beats moving 4 buttons to a new
position. You could swap DemoCTRLB's and DemoCTRLC's positions real fast if you wanted to
just to see how easy it is to rearrange the positions of a couple of groups.
Ok, swap back to the GUI you were on before opening the editor(most likely MainMenuGui) and
then open your console and enter "Canvas.pushDialog(DemoGui);" (without quotes) and the GUI
should appear on your screen ready to use. Close your console and lets examine a few
things I've set up for you. If you click the "Hide 2" button you will see the group in the
upper right disappear. This is another reason for grouping certain objects in your GUI
structure. You might want to have just a single page that has 5 different sets of options
that you could turn on and off just like you turned off that section of
the DemoGui. And of course if you hit "Show 2", that GUI element comes back. Now, what caused
it to disappear you might ask. Well, the "Hide 2" button has a command attached to it that
is syntaxed "DemoCTRLB.setVisible(false);". All GUI elements and controls use the setVisible
functionality which lets you hide or display them when on screen. In this case DemoCTRLB was
told to hide itself and because the 4 buttons that are in the DemoCTRLB group are subject to
inherit their functionality from the group control, they automatically set themselves to
"non-visible" as well. As if you had actually asked all four of them to hide also. And
when you asked DemoCTRLB to unhide itself the buttons under it's control followed suit and
they reappeared as well. One important factor is that "DemoCTRLB" is an actual name that has
been assigned to that particular control. By giving a control a name, it can be called by
name to perform functions. In this case "DemoCTRLB.setVisible(false);". If the control did
not have it's own specific name, controlling it would become much more difficult and would
reduce functionality.
Not everything needs a name of course. What needs to be named and
what does not is completely dependant on how you intend to use it. This function is
demonstrated a second time by using "Hide 3" which of course hides the third group of buttons
using the function "DemoCTRLC.setVisible(false);". Since the third groups control is named
"DemoCTRLC" the previous function will of course cause it to hide itself and all objects
grouped under it's hierarchy. To bring it back you would use the command
"DemoCTRLC.setVisible(true);" or hit the "Show 3" button which is already linked to that
command. Now, if you hit the "Hide 2" button, and then the "Hide 3" button you will have
two hidden groups. You can bring both back at the same time by hitting "Show All". The
command that "Show All" is linked to is actually a function call to a scripted function
that I made. The command is "DemoGui.action(SHOW);" and it calls the function
"DemoGui::action(%this, %action)". How did this work? Since TGE can use namespace for
function calls, just giving our GUI the name "DemoGui" gave us the ability to create and call
functions using the DemoGui object as our calling object. Thus TGE reads "DemoGui.action" as
"function DemoGui::action(%this)" and since we included the condition of "SHOW" with the
function call, it will be passed on with the call and the end result would be seen by TGE as
"DemoGui::action(DemoGui, SHOW)" which will be handled appropriately by the scripted function
I included as a guide. Studying the function should be pretty self explanatory as to how it
works when called by each of the respective buttons that address it.
Lets look at some other elements I have included. The "Exit Demo" button will of course "pop"
the current GUI by name "DemoGui", and it can do so by two means. You can either click on it,
or you can push the "Esc" key on your keyboard. I bound "escape" to the exit button by
entering "escape" into the button's "accelerator" field. Any key you name in this field will
bind it to that button as long as that GuiControl is active. Meaning, if you have your
cars turbo-boost button set to "b" then you will not boost if you have a GuiControl on screen
that has an accelerator button that is using "B". As soon as the control is eliminated, "B"
will return to controlling your turbo-boost. Take care not to use the same key twice in a
control, and try not to use keys that have global bindings because it probably wont work right.
Also do not use "enter" as an accelerator button. It will not work because the
correct name of the key is "return". A lot of keyboards are now labled "Enter" so it's a
common mistake to use that command. You CAN use the button, but you must label it "return".
The "Alt Exit" button was just another way to demonstrate the scripted action function using a
different means, and to show there is more than one way to close a GUI. Which way is better?
For the most part, the "Exit Demo" command of "Canvas.popDialog(DemoGui);" is the fastest and
easiest way to go for a simple GUI. But there are times when you will want to do things before
a GUI is closed so you might want to send to a function like "Alt Exit" does so you can make
sure the settings are correct. In "Alt Exit" I set the function to make sure that NameButton
is set back to it's original state, set both button groups to visible, and then close the GUI.
The reason for this is that GUI's will remain in their current state unless told to do otherwise.
If you were to hide the two groups and then close the DemoGui, when you opened the DemoGui back
up it would still have two hidden groups. This is not always desirable so care must be taken
to do some cleanup on GUI's that begin to get complex by using multiple configurations or menus.
You could just as easily convert the "Alt Exit" functionality to another function that is automatic called
"onWake". onWake is called by the engine on any Gui that is pushed to the Canvas. So to make
it work, all you have to do is add it and make sure it's exec'ed. Such a function would look
just like this....
function DemoGui::onWake(%this)
{
}
Now, if I take the "Alt Exit" cleanup commands and stick them here, the end result is the same....
function DemoGui::onWake(%this)
{
NameButton.performClick();
DemoCTRLB.setVisible(true);
DemoCTRLC.setVisible(true);
}
No matter what the state of the GUI was before it was opened, the instant it was opened those
functions reset it back to it's original state. Just in case you are wondering, there is a
similar function for when a GUI is closed called "onSleep". If for some reason you wanted to tell a server that a clients GUI had closed, or you wanted to run a function when the GUI was no longer
active you could include it in the "onSleep" function. Remember, this is an auto function called
by TGE whenever it pops a GUI dialog. If the function doesn't exist, nothing will happen of
course, but if you need to have an action at this time, you can immediately add it with no trouble
at all. TGE GUI's are very easy to get along with. Here is an example....
function DemoGui::onSleep(%this)
{
//Do stuff here....
}
Notice the "NameButton.performClick();" function. All buttons can use the ".performClick()" action
to apply them just as if a user had clicked them. This is great to use with interactive demos that you want to walk a player through. You could demonstrate the GUI's functionality when the user clicked
on help and you wouldn't even have to modify the GUI or create an additional GUI for demo purposes.
But that's for another tutorial coming later. In this case, I used it to make sure that the buttons
text was reset to default no matter what by making sure it gets clicked.
Which brings us to the "setText" function. Clicking the "Swap Name" button will cause button "2a" to
change to a new text, in this case "SHWIINNG!!". This was just to illustrate the possible use of this
function which may not mean much at first, but later on I can explain a more powerful reason to do
such a thing. And of course hitting the "SHWIINNG!!" button will cause it to go back to "2a".
Ok, by this point you should understand why you need a GuiControl, how controls and buttons become
children of their parent group and how they relate in position, how to make a GUI interact with itself
as well as with custom scripts, and why it's important to name some elements of a GUI.
Comments? Suggestions? Requests?
About the author
#2
09/26/2004 (6:55 pm)
Donwload link still dont work. :)
#3
09/26/2004 (7:20 pm)
Try it now, it's working
#4
09/27/2004 (4:57 am)
Grats och a nice tutorial Gonzo! :)
#5
09/27/2004 (6:22 am)
Thanks, and please, if you have any suggestions, questions, or requests, let me know. I'm thinking of writing a series of these GUI tuts to help clear some of the confusion of using the GUI's and help people get more functionality from them.
#6
11/23/2004 (7:04 pm)
Gonzo I'm quite the noob here. Trying to get your demo gui to run and having no luck. I put the demo files in the folder "example\starter.fps\client\ui". When I run Canvas.pushDialog("DemoGui") I get the error "Invalid Control: DemoGui". Any help would be appreciated.
#7
Most likely that is the cause of your problems.
11/25/2004 (5:17 am)
Did you make sure to put in the lines to exec the files?Most likely that is the cause of your problems.
#8
03/03/2005 (6:00 pm)
Another noob here. "Make sure to put in to exec the file..." What lines, and put in where? Thanks.
#9
05/04/2005 (2:27 am)
Put DemoGui.gui file to "example\starter.fps\client\ui" and then go open "example\starter.fps\client\init.cs" then try add this line on the // Load up the shell GUIs section. "exec("./ui/DemoGui.gui");"...it will works ^^
#10
05/10/2005 (1:49 am)
Oops, can't access the zip file since since the new webserver has gone live.
#11
05/10/2005 (1:12 pm)
Try it again. It worked for me and the funny things is, nothing else does, lol.
#12
04/01/2006 (12:32 pm)
I also get the EM "Invalid Control: DemoGui" when entering the cmd in the console. I have also done as Zufa suggested.
#13
04/02/2006 (12:57 pm)
It works now: changed
#14
05/13/2007 (9:40 am)
This is great, thanks a lot!
#15
about the console is it open run & write cmd then what the path to write Canvas.pushDialog("DemoGui")
Or the console in the menus of torque tot base
thanx
07/03/2007 (12:54 am)
I also get the EM "Invalid Control: DemoGui" when entering the cmd in the console. I have also done as Zufa suggested.about the console is it open run & write cmd then what the path to write Canvas.pushDialog("DemoGui")
Or the console in the menus of torque tot base
thanx
#16
I'd like to read more tutorials like this, and maybe even about customizing the look of some gui controls.
Thanks
03/12/2011 (10:58 am)
This is very good tutorial!I'd like to read more tutorials like this, and maybe even about customizing the look of some gui controls.
Thanks

Torque Owner StormEc
Reality Transitions
The link doesnt work though.
SW