Tutorial: CSQC GUI Menus

Discuss CSQC related programming.
avirox
Posts: 137
Joined: Wed Aug 16, 2006 3:25 pm

Tutorial: CSQC GUI Menus

Post by avirox »

Hey guys! I'm here with a tutorial to implement basic GUI menus into your mod via CSQC. The best part about this tutorial is that the work is practically all done for you, so you should be able to get this up and running quickly and easily by just adding a few .qc files and tweaking a couple functions. Now isn't that fun?


Background info: While working on Fortress Live I decided to try and update the interfaces for quake Team Fortress to make them appear more "modern" to attract new players. My goal was to "mimic" some of the appearance and functionality of Half-Life's VGUI. The result, however, is a pretty dynamic and highly modable GUI system.

Half-Life's VGUI:
Image

My GUI:
Image
Image

The Code:
Before we begin, I assume you have a working/compiling CSQC mod. If you are new to this or have never heard of CSQC, I suggest you take a look at these tutorials:
http://qexpo.tastyspleen.net/booth.php?id=165&page=308
http://qexpo.tastyspleen.net/booth.php?id=165&page=317
Please note that this tutorial was designed for FTE's CSQC. The CSQC implementation in Darkplaces differs in some areas, but those areas should be easily tweakable.
Also please note that I will not be giving lengthy details about every bit of code we're adding here because the code is heavily commented to supply you with the specifics.

To start off, we need to make sure all the defs are in place. Go in your defs.qc (or system.qc) all the way at the bottom and add these lines:

Code: Select all

// CSQC GUI
float in_menu, menu_selected, menu_impulse, mouse_branch;
float global_bt_no, global_menu_flags;
vector mousepos, testsize_one;
.string bt_text, bt_cmd, bt_img;
.float bt_flags, bt_no;
float vid_realwidth, vid_realheight; // the *real* width and height of the 2d rendered scene

float () MouseInBox;
void (string menu_name, float menu_flags) InitMenu;
void (string button_text, string button_cmd, vector button_spawnpos, string button_image, vector button_size, float button_flags) CreateButton;

#define MENU_TEST1			666

// global menu flags
#define MFG_NOCURSOR			1

// menu flags
#define MENUFLAG_DISABLED		1
#define MENUFLAG_BRANCH			2
Now that those are added, make sure your engine supports and has defined the following extensions (these may be different than DP's):

Code: Select all

void(...) localcmd = #46;
float(string s) tokenize = #441;
string(float argnum) argv = #442;
float(string s) cvar = #45;						// return cvar.value
float(string s) stof	= #81;
string(float f) ftos				= #26;
float(string st) strlen = #114;
entity() spawn						= #14;
string(string s) cvar_string = #448;
string (vector v) vtos = #27; 
string(string s, float start, float length) substring = #116;
entity (entity start, .string fld, string match) find = #18; 
void (entity e) remove = #15; 
Note that the above don't need to be added to your defs/system.qc if they are already defined somewhere. Just add the ones that may not be there already.

Now that the definitions are in, create the following 2 files and add them in this order after defs.qc in your progs.src:
gui_intercept.qc
gui_draw.qc

Now open gui_intercept.qc and lets get biz-ay. Copy and paste the following into that one QC file:

Code: Select all

// Handle GUI events in menu-specific manners

// A button is clicked..
void ( entity t_button ) CSQC_GUI_ButtonClick =
{
	switch(in_menu) {
		default:
			if (!menu_impulse) {			// if the menu didn't already send the reply to the server regarding the menu..
				menu_impulse = TRUE;
				if (t_button.bt_flags & MENUFLAG_BRANCH) 
					mouse_branch = TRUE;
				if (t_button.bt_cmd == "default")
					localcmd("impulse ", ftos(t_button.bt_no), "\n");
				else
					localcmd(t_button.bt_cmd, "\n");
			}
	}
};

// When a mouse hovers over a button, apply this effect..
void (string button_image, vector button_position, vector button_size, string button_text) CSQC_GUI_MouseOverButton =
{
	local float selected_button, stl;
	selected_button = MouseInBox();
	
	switch(in_menu) {
		default:
			drawpic(button_position, button_image, button_size, '1 1 1', .4);
			drawstring(button_position + '10 12 0', button_text, '8 8 0', '1 1 1', 1);
			break;
	}
};

// Called before the menu buttons are rendered
void () CSQC_GUI_MenuBackground =
{
	switch(in_menu) {
		default:			// No default background image
			break;
	}
};

// Called after the menu buttons are rendered
void () CSQC_GUI_MenuForeground =
{
	switch(in_menu) {
		default:			// No default foreground image
			break;
	}
};
Save and close. You're done there. Editing that file will be handled perhaps in a later tutorial for advanced modding :)

Now open up gui_draw.qc. This is where we tell quake to draw your images/texts. As before, copy and paste in the following:

Code: Select all

// CSQC GUI Stuff
// draws the selection box with text inside it (only draws one box style for now)
void (vector boxpos, vector boxsize, string boxtext, vector boxrgb, float boxalpha) spawnbox =
{
	drawpic(boxpos, "progs/csqc/csqcguiback.tga", boxsize, boxrgb, boxalpha);
	drawstring(boxpos + '10 12 0', boxtext, '8 8 0', boxrgb, 1);
};

void () CSQCGUI_ClearMenu =
{
	local entity button;
	
	// Remove all buttons
	button = find(world, classname, "gui_button");
	while (button)
	{
		remove(button);
		button = find(button, classname, "gui_button");
	}

	global_bt_no = 0;		// clear globally assigned button #s
	
	menu_selected = 0;
	in_menu = 0;
	menu_impulse = 0;
	
	// reset mouse pos
	if (!mouse_branch) {
		mousepos_x = vid_realwidth*.5;
		mousepos_y = vid_realheight*.5;
	}
	else
		mouse_branch = FALSE;
};

void () CSQCGUI_Render =
{
	local float args, boxno, i;
	entity button;

	if (!in_menu)
		return;

	cprint("\n");			// clear the currently printed menu
	
	CSQC_GUI_MenuBackground ();
	
	button = find(world, classname, "gui_button");
	while (button)
	{
		boxno = button.bt_no;
		if (MouseInBox() == boxno) {
			CSQC_GUI_MouseOverButton( button.bt_img, button.origin, button.size, button.bt_text );
			if (menu_selected) {
				CSQC_GUI_ButtonClick( button );
			}
		}
		else {
			if (button.bt_flags & MENUFLAG_DISABLED)
				spawnbox(button.origin, button.size, button.bt_text, '.4 .4 .4', .2);
			else
				spawnbox(button.origin, button.size, button.bt_text, '1 1 1', .2);
		}
			
		button = find(button, classname, "gui_button");
	}

	CSQC_GUI_MenuForeground ();
	
	if (!(global_menu_flags & MFG_NOCURSOR))
		drawstring(mousepos, "X", '8 8 0', '1 0.91 0.51', .6);		// Draw the "cursor"
};

void (string menu_name, float menu_flags) InitMenu =
{
	localcmd("csqcgui_menu ", menu_name, " ", ftos(menu_flags), "\n");
	global_menu_flags = menu_flags;
};

void (string button_text, string button_cmd, vector button_spawnpos, string button_image, vector button_size, float button_flags) CreateButton =
{
	local entity newbutton;
	
	global_bt_no++;
	
	newbutton = spawn();
	newbutton.classname = "gui_button";
	newbutton.origin = button_spawnpos;
	newbutton.size = button_size;
	newbutton.bt_img = button_image;
	newbutton.bt_text = button_text;
	newbutton.bt_cmd = button_cmd;
	newbutton.bt_flags = button_flags;
	newbutton.bt_no	= global_bt_no;
};

// Which box the mouse is hovering over currently
float () MouseInBox =
{
	float in_box, boxval;
	entity button;
	
	button = find(world, classname, "gui_button");
	while (button)
	{
		if (mousepos_y >= button.origin_y && mousepos_y <= button.origin_y+button.size_y) 
			if (mousepos_x >= button.origin_x && mousepos_x <= button.origin_x+button.size_x) {
				if (!button.bt_flags & MENUFLAG_DISABLED)
					boxval = button.bt_no;
				break;
			}
		button = find(button, classname, "gui_button");
	}
	
	return boxval;
};
Save. Close. The easy part is over :)

Now there are two key functions to any decent CSQC mod. The first, and most important, is CSQC_UpdateView(). CSQC_UpdateView, in a nutshell, runs each frame and renders pretty much everything you see. You need to add 3 lines of code to this function, right after renderscene(); is called:

Code: Select all

vid_realwidth = width;
vid_realheight = height;
CSQCGUI_Render();
This pretty much just makes the CSQC look for CSQCGUI items to render.

Now in this same function, we also want to make sure that the crosshair is not drawn when we're in a menu. If you have a line like:

Code: Select all

setviewprop(VF_DRAWCROSSHAIR, 1);
then you must whip it- er i mean change it to:

Code: Select all

	if (!in_menu || global_menu_flags & MFG_NOCURSOR)
		setviewprop(VF_DRAWCROSSHAIR, 1);
I assume that you already have CSQC_UpdateView in your mod. If not, here's what mine looks like:

Code: Select all

void(float width, float height, float menushown) CSQC_UpdateView =
{
	clearscene();       //wipe the scene, and apply the default rendering values.

	
	setviewprop(VF_MIN_X, 0);	//set the left of the view
	setviewprop(VF_MIN_Y, 0);	//set the top of the view
	setviewprop(VF_SIZE_X, width);	//set how wide the view is (full width)
	setviewprop(VF_SIZE_Y, height); //set how high the view is (full height)
	
	setviewprop(VF_DRAWENGINESBAR, 1);
	if (!in_menu || global_menu_flags & MFG_NOCURSOR)
		setviewprop(VF_DRAWCROSSHAIR, 1);
	
//     	setviewprop(VF_FOV, cvar("fov"));	//this is entirely optional	FIXME: fov is x,y
	//if you use prediction, you'll need the following four lines
//	setviewprop(VF_ORIGIN, myvieworg);	//change where the view is drawn
//	setviewprop(VF_ANGLES, myviewang);	//change the angle the view is drawn at
//	makevectors(myviewang);			//calc the listener directions
//	setlistener(myvieworg, v_forward, v_right, v_up);	//change where sounds come from

	addentities(4|1|2);

	renderscene();
	
   // Set the console size vars
   vid_realwidth = width;
   vid_realheight = height;
	CSQCGUI_Render();
};
Now for the final function we need to edit: CSQC_InputEvent.
CSQC_InputEvent tracks key presses to mouse movements and everything in between. You can do a ton of stuff with this function. For purposes of my GUI mod, it hijacks the mouse so we can select things in menus.

I'm just going to supply the whole function the way I have it, but if you have your own then just add the parts in the brackets from if ( in_menu && !(global_menu_flags & MFG_NOCURSOR) ) { to somewhere accessible in your code.

Code: Select all

// Catch inputs from the client like pressing a key or using the mouse (used for menus)
float(float eventtype, float param1, float param2) CSQC_InputEvent =
{
	if ( in_menu && !(global_menu_flags & MFG_NOCURSOR) ) {
		if (eventtype == 0)	{//keypress down inputs
			if (param1 == 512) {		// Left Click
				if (MouseInBox())
				{
					menu_selected = MouseInBox();			// Actual menu selection is handled in CSQCGUI_Render() for modability purposes
					//print("You clicked box number ", ftos(MouseInBox()), "\n");
				}
				return TRUE;
			}
			else if (param1 == 513) {			// Right click
				print(vtos(mousepos),"\n");
			}
			else if (param1 == 514) {			// Middle click
				if (testsize_one == '0 0 0')
					testsize_one = mousepos;
				else {
					print(vtos(mousepos-testsize_one),"\n");
					testsize_one = '0 0 0';
				}
			}
		}
		if (eventtype == 2/* && in_menu*/)	//mouse
		{

			mousepos_x += param1;
			mousepos_y += param2;

			if (mousepos_x < 0)
				mousepos_x = 0;
			if (mousepos_y < 0)
				mousepos_y = 0;

			if (mousepos_x > vid_realwidth)
				mousepos_x = vid_realwidth;
			if (mousepos_y > vid_realheight)
				mousepos_y = vid_realheight;				

			return TRUE;
		}
	}
	return FALSE;
};
Congratulations! If that compiled successfully (I'm sure I will need to update this tutorial many times) then you now have my GUI system installed in your mod with minimal intrusion. In my next post I will describe getting your first menus loaded..
Last edited by avirox on Tue May 12, 2009 1:51 am, edited 4 times in total.
avirox
Posts: 137
Joined: Wed Aug 16, 2006 3:25 pm

Post by avirox »

Part II: Adding your own menus with the above system

Alright, now it's time to put this code to use. In this part of the tutorial I'm going to explain a very simple and basic style of menu. At this point I'd like to mention that the menu system I created was designed to interact with SSQC menus which use 1,2,3,etc. button presses. However, the menus can also be used stand-alone (note that this offers somewhat limited modability since it's only interacting with client commands).

The menu we're going to create today will look like this:
Image

Surprisingly or not, I was able to cook that menu up in under a minute's worth of code using my GUI system.

Lets first get some necessary files and add some commands.

Download the button background image here.
Once you've downloaded that, take the file and put it in your mod's progs/csqc/ folder (or create that and put it there).

Next up we're going to precache that file and setup some more functions. Go in your defs.qc/system.qc and add the following to the bottom:

Code: Select all

void () CSQCGUI_ClearMenu;  // clears the screen of the current menu and the mouse position
void () Menu_TestMenu; // Test button menu
In order to precache the file, we want to find the CSQC function that's called the earliest. This function is CSQC_Init(), called when you first load up your csprogs.dat. If you don't have a CSQC_Init(), then make one now - it could be placed anywhere after defs.qc, but I'll leave the specifics up to you. In that function, add the following:

Code: Select all

precache_pic("progs/csqc/csqcguiback.tga"); // GUI Button Background
registercommand("testmenu"); // call up our test menu
Save and close. For purposes of this tutorial, we're done there.

Now what we're going to do is add a new function which sets up the menu and its buttons. I suggest adding specific menus into their own .qc file(s) located after the GUI qc files. For purposes of making sure we're all on the same page, create a gui_menu_test.qc file, and make sure it's added after the previous 2 gui files in your progs.src.

Open gui_menu_test.qc and add the following function:

Code: Select all

void () Menu_TestMenu =
{
	local vector men;						// position of the first menu button
	local vector but_size;					// size of all the buttons
	local string buttonback;				// Button image
	
	but_size = '160 35 0';
	men_x = (vid_realheight*0.5) - (but_size_x*0.5);;		// first button will be displayed in the dead center of the horizontal area
	men_y = vid_realheight*0.2;							// first button will be displayed 20% of the vertical value from the top of the screen
	buttonback = "progs/csqc/csqcguiback.tga";
	
	
	InitMenu( "menu_testme", 0 );
	CreateButton( "Fire!", 				"+attack\n;testmenu",  									men, buttonback, but_size, 0); men_y+=but_size_y;
	CreateButton( "Jump!", 				"+jump\n;testmenu",		 								men, buttonback, but_size, 0); men_y+=but_size_y;
	CreateButton( "Say Cheese!", 		"say cheese\n;testmenu", 			 					men, buttonback, but_size, 0); men_y+=but_size_y;
	CreateButton( "Go Apes**t!", 		"cl_yawspeed 2000\n;+right\n;testmenu", 				men, buttonback, but_size, 0); men_y+=but_size_y;
	CreateButton( "Back to normal!!", 	"cl_yawspeed 5\n;-right\n;-jump\n;-attack\n;testmenu", 	men, buttonback, but_size, 0); men_y+=but_size_y;
}
Let me briefly explain the way InitMenu and CreateButton work.

InitMenu is mainly used for debugging purposes. If something goes wrong somewhere, it allows me to know which menu I'm looking at using the cvar "csqcgui_menu". The second field of InitMenu allows you to specify whether this is a mouse-driven menu or not. If I called InitMenu using, say:
InitMenu( "menu_testme", MFG_NOCURSOR );
Then the menu would be displayed, but your mouse would still control the player's view. This is only useful when you have SSQC running in the background to interpret "impulse X" or other client commands. If you wanted to replace an SSQC text menu but didn't need or want mouse support with it, then this is what you would use.

CreateButton is fairly straight forward, and is easy to use as well as highly tweakable:

Field #1 is the button's text. If this is left blank then only the button itself will be displayed.

Field #2 is the command that gets executed when the button is clicked. You will note that in the above code, "testmenu" is the last thing being called in this field. The reason for this is that with the current GUI system, once you click a button, the menu will send a single function and then cease to take any more clicks. Again, this is because the menu is meant to interact with SSQC and be updated by the server on what to display next.

Field #3 specifies the X,Y(,Z) position where your button will spawn on the screen. For debugging purposes, I've left it so that when you right-click it will tell you the X,Y position that your cursor currently occupies.

Field #4 is the button image. Always make sure this image is precache'd first when used, else other connecting clients won't be able to download it, and it will generally work slower.

Field #5 is the button size. No big brainer here, but before you set a button size make sure you're scaling it properly to the 2D resolution your menus are aiming for. If you want to make buttons the same size regardless of 2d resolution, set sizes based on con_realheight/con_realwidth.

Finally, field #6 allows you to specify certain flag properties. Currently there are 2:
#define MENUFLAG_DISABLED 1
#define MENUFLAG_BRANCH 2
MENUFLAG_DISABLED means that the button is disabled and therefore unable to be used/clicked.
MENUFLAG_BRANCH just means that this selection leads to another selection. For example:
Image

So now we have the menus, the commands, and the images. The next part is how to get all this to display in-game.

There are many ways to parse server/client information in CSQC, and how you do it in your mod is really up to you, but to keep this tutorial simple I'm just going to use CSQC_ConsoleCommand. This function picks up any console/alias command from the client, IE: +showscores, +attack, etc. In this function, add the following lines of code:

Code: Select all

	local float args;
	args = tokenize(strMessage);
	
	if (argv(0) == "testmenu") {
		if (in_menu) {
			CSQCGUI_ClearMenu();
			return TRUE;
		}
		CSQCGUI_ClearMenu();		// Clear the current menu
		in_menu = MENU_TEST1;
		Menu_TestMenu();
		
	}
Now load up your mod and go in the console. There, type (or bind to a key) "testmenu". If all goes well, you should see a menu which resembles the first image in this post, and you should be able to use your mouse to click and use the menu items.


EDIT: Small bonus tutorial based on the above!
Let me just go over a quick and easy way to make the menu look less bland.

Adding a background image can make your menus pop out more and look much cooler. Go in the CSQC_GUI_MenuBackground function in gui_parse.qc and change it to the following:

Code: Select all

// Called before the menu buttons are rendered
void () CSQC_GUI_MenuBackground =
{
	local vector menback_pos, menback_size;
	
	switch(in_menu) {
		case MENU_TEST1:
			// individual button size: '160 35 0'
			menback_pos_x = ((vid_realwidth*0.5) - (160*0.5)) - 20; 
			menback_pos_y = (vid_realheight*0.2) - 20;
			menback_size_x = 200;				// Since we're overlapping 20 on each side, add 40 to 160.
			menback_size_y = (35*5)+40;			// There are 5 buttons in a row vertically, so compensate for that and add 40
			drawpic(menback_pos, "progs/csqc/csqcguiback.tga", menback_size, '1 1 .5', .2);			// Large text box (class info)
			break;
		default:			// No default background image
			break;
	}
};
Basically what I did here was draw the same box we used for the button images, and enlarged it. I also changed the RGB value to give it a different color. After adding this code, your menu should now look like this:

Image

This same code can be applied for CSQC_GUI_MenuForeground, but in that case the new box will cover the menu items rather than be behind them. Tinker around with this and create different background images - it can really help the over-all look and feel!
Last edited by avirox on Tue May 12, 2009 1:20 pm, edited 9 times in total.
Error
InsideQC Staff
Posts: 865
Joined: Fri Nov 05, 2004 5:15 am
Location: VA, USA
Contact:

Post by Error »

oh my god I love you
leileilol
Posts: 2783
Joined: Fri Oct 15, 2004 3:23 am

Post by leileilol »

i like this, together with dp's loadfont you can make a very convincing ripoff
i should not be here
avirox
Posts: 137
Joined: Wed Aug 16, 2006 3:25 pm

Post by avirox »

It's not meant to be a ripoff, just a way to mimic the functionality of more modern in-game GUI's. You can go well above and beyond the simple box/branch system, but for simplicity I've kept it to that for the first 2 parts of this tutorial. Maybe later I'll write a third part on SSQC=>CSQC GUI interaction, because that's where the sweet spot of these menus really lies.
r00k
Posts: 1111
Joined: Sat Nov 13, 2004 10:39 pm

Post by r00k »

Nice! Is that using FTE??
GiffE
Posts: 170
Joined: Sun Oct 08, 2006 3:39 pm
Location: USA, CT
Contact:

Post by GiffE »

Very nice work!
Something like this will certainly save me some work!
avirox
Posts: 137
Joined: Wed Aug 16, 2006 3:25 pm

Post by avirox »

Thanks guys! Anyways, I noticed that in the first post the code for the MENUFLAG_DISABLED flag was not added. This has been remedied. The two functions I changed were:

MouseInBox()
CSQCGUI_Render()

I'm also cooking up a new quick tutorial for drop-down menus.
Chip
Posts: 575
Joined: Wed Jan 21, 2009 9:12 am
Location: Dublin, Ireland
Contact:

What about Darkplaces?

Post by Chip »

So, if I want to use this approach using Darkplaces, what should the changes be? It could really change the appearance of my upcoming Quantum Engine.
QuakeWiki
getButterfly - WordPress Support Services
Roo Holidays

Fear not the dark, but what the dark hides.
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Post by Spike »

I see nothing mentioned in that tut that will fail with DP.

Although you will need FTEQCC to compile it (thanks to 'switch' and '#define'), but you could recode those parts if you want to use FrikQCC.
CocoT
Posts: 695
Joined: Tue Dec 14, 2004 5:39 pm
Location: Belly-Gum
Contact:

Post by CocoT »

Wonderful tutorial, Avirox! I can't wait to try it out :)
Neurotic Conversions - New location: Update your bookmarks!
avirox
Posts: 137
Joined: Wed Aug 16, 2006 3:25 pm

Post by avirox »

Part III: Basic Menus II

So I thought I might dedicate today's GUI tutorial to a basic drop-down/branch menu. This tutorial requires that you've done the steps in Part I and II, as this code borrows some of the same resources from the first menu described above.

The result of this menu will look something like this:
Image

Lets open up our definitions qc file and add a new def for our drop-down menu:

Code: Select all

#define MENU_TEST_DROPDOWN		1337
That's all we need there. Next we gotta make sure the menu can be called up. For purposes of keeping things simple, lets add a new client command which can be called at will to summon up the menu. In CSQC_Init() add this somewhere:

Code: Select all

registercommand("testmenu2");
Now that that's defined, the command can be intercepted by the CSQC_ConsoleCommand function. Thus, like in the last tutorial, that's where we're going to spawn our new menu from.

Add the following lines to CSQC_ConsoleCommand:

Code: Select all

if (argv(0) == "testmenu2") {
		CSQCGUI_ClearMenu();		// Clear the current menu
		in_menu = MENU_TEST_DROPDOWN;
		//print(argv(1)," eyyy\n");
		Menu_DropdownMenu(stof(argv(1)));
		
	}
Now wait! We didn't add our menu yet! Preferably we should add this menu function somewhere before CSQC_ConsoleCommand, but if that's not the case in your code then just make sure you have void (float mn_flags) Menu_DropdownMenu; defined somewhere in your definitions file.

Here's the code for our simple drop-down menu:

Code: Select all

void (float mn_flags) Menu_DropdownMenu =
{
	local vector men;						// position of the first menu button
	local vector but_size;					// size of all the buttons
	local string buttonback;				// Button image
	local float button_flag;
	
	// if mn_flags is 1, we will display the drop-down menu
	if (mn_flags > 0)
		button_flag = MENUFLAG_DISABLED;		// disable the other buttons when the drop-down menu is active

	but_size = '145 35 0';
	men_x = vid_realwidth*0.1;
	men_y = vid_realheight*0.2;							// first button will be displayed 20% of the vertical value from the top of the screen
	buttonback = "progs/csqc/csqcguiback.tga";
	
	InitMenu( "menu_dropdown", 0 );
	CreateButton( "Say This!", 			"say this!\n;clearmenu",men, buttonback, but_size, 				button_flag); 		men_x+=but_size_x;
	CreateButton( "Say That!", 			"say that!\n;clearmenu",men, buttonback, but_size, 				button_flag); 		men_x+=but_size_x;
	CreateButton( "More Options", 		"nothing", 				men, buttonback, but_size - '25 0 0', 	MENUFLAG_DISABLED); men_x+=but_size_x - 25;
	if (mn_flags > 0)
		CreateButton( "-",			"testmenu2",			men, buttonback, '25 35 0',				MENUFLAG_BRANCH);
	else
		CreateButton( "+",			"testmenu2 1",			men, buttonback, '25 35 0',				MENUFLAG_BRANCH);	

	if (mn_flags > 0) {
		men_x-=(but_size_x - 25);	//
		men_y+=but_size_y;			// draw new menu items below the "More Options" button
	}
	else
		men = -2*but_size;		// spawn menus off the screen
		
	CreateButton( "Fire!", 	"+attack\n;testmenu",  	men, buttonback, but_size, 0); men_y+=but_size_y;
	CreateButton( "Jump!", 	"+jump\n;testmenu",		men, buttonback, but_size, 0); men_y+=but_size_y;
}
What this menu will basically do, when loaded up without any value for "mn_flags", is display 3 buttons:
Say This!
Say That!
More Options!
and a fourth button which will act as our little list expander ("+").

The "More Options!" Button is permanently flagged with MENUFLAG_DISABLED, which was discussed in Part II. It cannot be clicked or inter-acted with - it's just there to act as a title per-se to the drop-down list.

When the "+" button is clicked, the menu sends the client a command to parse, adding a value to "mn_flags". This will disable the other 2 buttons, and display the 2 new menus under the third button. If this button is clicked again (now displayed as a "-"), it will move the 2 new buttons away and re-enable the first 2.

Also note that the "+"/"-" buttons use the MENUFLAG_BRANCH flag. That just means that when you click this button your mouse position will not reset to the center of the screen, and just stay where it is.

I highly suggest reading through the code comments, as they provide more specifics on what's going on.

And that's it! Once you're in the game just call "testmenu2" from the console and your shiny new menu should pop up!

Next up (when I get the time) I'll explain the GUI intercept events and give yall some cool things to do with them!
avirox
Posts: 137
Joined: Wed Aug 16, 2006 3:25 pm

Post by avirox »

Good news for lazy people! I've compiled a little "clean" version of the CSQC GUI which acts as a standalone CSQC mod. You can download it here. This is a CSQC mod with the GUI already implemented + the first menu tutorial above. The above tutorials are for implementing the menus into your existing CSQC mod, but this source is only for those starting a new one. Needs FTEQCC/GUI to compile.
Last edited by avirox on Thu May 21, 2009 1:58 pm, edited 1 time in total.
qbism
Posts: 1236
Joined: Thu Nov 04, 2004 5:51 am
Contact:

Post by qbism »

leileilol
Posts: 2783
Joined: Fri Oct 15, 2004 3:23 am

Post by leileilol »

qbism wrote:*
you!
i should not be here
Post Reply