VGUI2: Creating a panel: Difference between revisions
m (the latest version doesn't declare gameParent in vgiu_int.cpp so I added it) |
(Rewrite Template:Lang to Template:LanguageBar. This action was performed by a bot.) |
||
(50 intermediate revisions by 17 users not shown) | |||
Line 1: | Line 1: | ||
[[Category:Programming]][[Category:Tutorials]][[Category:VGUI]] | {{LanguageBar|VGUI2: Creating a panel}} | ||
=Requirements= | |||
[[Category:Programming]][[Category:Tutorials]][[Category:VGUI|C]] | |||
==Requirements== | |||
Have read and understood (or understand):<br> | Have read and understood (or understand):<br> | ||
*[[VGUI Documentation]] | *[[VGUI Documentation]] | ||
*[[HUD Elements]] | *[[HUD Elements]] | ||
Can code: | Can code: | ||
*C++ | *C++ | ||
*Script | *Script | ||
This tutorial is about creating a simple interactive interface | This tutorial is about creating a simple interactive interface | ||
=Understanding how VGUI2 works= | ==Understanding how VGUI2 works== | ||
Every VGUI2 dialog that you see while using source based games is basically called a Panel. | Every VGUI2 dialog that you see while using source based games is basically called a Panel. | ||
Every Panel consists of three components: | Every Panel consists of three components: | ||
* Scheme | * Scheme | ||
* Control Settings | * Control Settings | ||
* Code | * Code | ||
==The Scheme== | ===The Scheme=== | ||
The scheme is a general configuration file which stores information about the colors of certain elements such as buttons, combo boxes, labels, etc. A typical scheme file is SourceScheme.res, for example. If you plan to create a panel which looks like the other panels in the menu you should use the same scheme file as they do. | The scheme is a general configuration file which stores information about the colors of certain elements such as buttons, combo boxes, labels, etc. A typical scheme file is SourceScheme.res, for example. If you plan to create a panel which looks like the other panels in the menu you should use the same scheme file as they do. | ||
==Control Settings== | ===Control Settings=== | ||
The control settings file stores information about the relative position of your panel and its elements. | The control settings file stores information about the relative position of your panel and its elements. | ||
Line 28: | Line 30: | ||
Every panel has a very own resource file. To create a resource file, there are two ways: Either you create one on your own using an editor like notepad, or you use Valves InGame Resource Editor. | Every panel has a very own resource file. To create a resource file, there are two ways: Either you create one on your own using an editor like notepad, or you use Valves InGame Resource Editor. | ||
==Code== | ===Code=== | ||
The code is the most important part of a panel, since the code decides what to do if the user clicks a button. To create and destroy the panel, you use the code. Fortunately you can set a lot more things than in the resource file(s). Code is the most important thing in this tutorial. | The code is the most important part of a panel, since the code decides what to do if the user clicks a button. To create and destroy the panel, you use the code. Fortunately you can set a lot more things than in the resource file(s). Code is the most important thing in this tutorial. | ||
=Creating a panel= | ==Creating a panel== | ||
Ok, let us assume we want to create a door, for real this time. Since we are not able to create a door from scratch, the first thing we do is to step by at the building centre. What we ask for, is a basic door. It works, but we have still plans to customize it. | Ok, let us assume we want to create a door, for real this time. Since we are not able to create a door from scratch, the first thing we do is to step by at the building centre. What we ask for, is a basic door. It works, but we have still plans to customize it. | ||
==Starting Off== | ===Starting Off=== | ||
The panel class is the base class of all VGUI2 elements. To get a rough overview about all the VGUI2 | The panel class is the base class of all VGUI2 elements. To get a rough overview about all the VGUI2 | ||
Line 44: | Line 47: | ||
You can create a new file ''MyPanel.cpp'' underneath the Source Files inside the '''client''' project. | You can create a new file ''MyPanel.cpp'' underneath the Source Files inside the '''client''' project. | ||
==MyPanel.cpp== | ===MyPanel.cpp=== | ||
<source lang="cpp"> | |||
//The following include files are necessary to allow your MyPanel.cpp to compile. | |||
#include "cbase.h" | |||
#include "IMyPanel.h" | |||
using namespace vgui; | |||
#include <vgui/IVGui.h> | |||
#include <vgui_controls/Frame.h> | |||
//CMyPanel class: Tutorial example class | |||
class CMyPanel : public vgui::Frame | |||
{ | |||
DECLARE_CLASS_SIMPLE(CMyPanel, vgui::Frame); | |||
//CMyPanel : This Class / vgui::Frame : BaseClass | |||
CMyPanel(vgui::VPANEL parent); // Constructor | |||
~CMyPanel(){}; // Destructor | |||
protected: | |||
//VGUI overrides: | |||
virtual void OnTick(); | |||
virtual void OnCommand(const char* pcCommand); | |||
private: | |||
//Other used VGUI control Elements: | |||
}; | |||
</source> | |||
The constructor: | The constructor: | ||
The argument is vgui::VPANEL parent. After reading the [[VGUI Documentation]], you should know that and why | The argument is vgui::VPANEL parent. After reading the [[VGUI Documentation]], you should know that every panel has a parent and why it has a parent. | ||
Underneath the above code, add: | Underneath the above code, add: | ||
< | <source lang="cpp"> | ||
// Constuctor: Initializes the Panel | // Constuctor: Initializes the Panel | ||
CMyPanel::CMyPanel(vgui::VPANEL parent) | CMyPanel::CMyPanel(vgui::VPANEL parent) | ||
Line 89: | Line 91: | ||
SetMouseInputEnabled( true ); | SetMouseInputEnabled( true ); | ||
SetProportional( | SetProportional( false ); | ||
SetTitleBarVisible( true ); | SetTitleBarVisible( true ); | ||
SetMinimizeButtonVisible( false ); | SetMinimizeButtonVisible( false ); | ||
Line 101: | Line 103: | ||
SetScheme(vgui::scheme()->LoadSchemeFromFile("resource/SourceScheme.res", "SourceScheme")); | SetScheme(vgui::scheme()->LoadSchemeFromFile("resource/SourceScheme.res", "SourceScheme")); | ||
LoadControlSettings("resource/UI/ | LoadControlSettings("resource/UI/mypanel.res"); | ||
vgui::ivgui()->AddTickSignal( GetVPanel(), 100 ); | vgui::ivgui()->AddTickSignal( GetVPanel(), 100 ); | ||
Line 107: | Line 109: | ||
DevMsg("MyPanel has been constructed\n"); | DevMsg("MyPanel has been constructed\n"); | ||
} | } | ||
</ | </source> | ||
The first lines are pretty easy to understand. SetScheme is used to set the source scheme, which is the standard scheme for Half-Life 2. We get a pointer to the scheme by calling LoadSchemeFromFile(...). The LoadControlSettings function is used to load the control settings resolution file. The last line is explained in the VGUI2 documentation. | The first lines are pretty easy to understand. SetScheme is used to set the source scheme, which is the standard scheme for Half-Life 2. We get a pointer to the scheme by calling LoadSchemeFromFile(...). The LoadControlSettings function is used to load the control settings resolution file. The last line is explained in the VGUI2 documentation. | ||
The SetProportional( false ); function decides to make the panel big or small, having it set to true will cause your panel to have big fonts, controls etc. | |||
Finally, if you're going to edit it with the VGUI Build Mode Editor, you will need precreate the directory it goes into, and posibly give your resource an all-lower-case name [See also the Discussion page] | |||
Underneath the above code, add: | Underneath the above code, add: | ||
< | <source lang="cpp"> | ||
//Class: CMyPanelInterface Class. Used for construction. | //Class: CMyPanelInterface Class. Used for construction. | ||
class CMyPanelInterface : public IMyPanel | class CMyPanelInterface : public IMyPanel | ||
Line 139: | Line 145: | ||
IMyPanel* mypanel = (IMyPanel*)&g_MyPanel; | IMyPanel* mypanel = (IMyPanel*)&g_MyPanel; | ||
</ | void CMyPanel::OnTick() | ||
{ | |||
BaseClass::OnTick(); | |||
} | |||
void CMyPanel::OnCommand(const char* pcCommand) | |||
{ | |||
BaseClass::OnCommand(pcCommand); | |||
} | |||
</source> | |||
Next you can create ''IMyPanel.h'' in the same folder. | Next you can create ''IMyPanel.h'' in the same folder. | ||
==IMyPanel.h== | ===IMyPanel.h=== | ||
< | <source lang="cpp"> | ||
// IMyPanel.h | // IMyPanel.h | ||
Line 155: | Line 170: | ||
extern IMyPanel* mypanel; | extern IMyPanel* mypanel; | ||
</ | </source> | ||
=Calling the panel= | ==Calling the panel== | ||
To call the panel, we add a few lines to the '''vgui_int.cpp''' file. Vgui_int.cpp includes functions which call all the panels. The VGui_CreateGlobalPanels() function is where the addition takes place. | To call the panel, we add a few lines to the '''vgui_int.cpp''' file. Vgui_int.cpp includes functions which call all the panels. The VGui_CreateGlobalPanels() function is where the addition takes place. | ||
Line 165: | Line 180: | ||
I assume that you want to create a panel for the game. | I assume that you want to create a panel for the game. | ||
So, after including the panel, you should add | So, after including the panel, you should add at VGui_CreateGlobalPanels() the following: | ||
< | <source lang="cpp">mypanel->Create(gameParent);</source> | ||
and check to see if gameParent has been declared at the top of the function, if it hasn't then add | and check to see if gameParent has been declared at the top of the function, if it hasn't then add | ||
< | <source lang="cpp">VPANEL gameParent = enginevgui->GetPanel( PANEL_CLIENTDLL );</source> | ||
''Note: To have your screen appear in-game like the Counter-Strike buy menus or team selection menus, change '' '''PANEL_CLIENTDLL''' ''to'' '''PANEL_INGAMESCREENS''' '', otherwise it will be visible only when you press Escape to go to the game menu.'' | |||
''Note: Dont forget to add | |||
< | <source lang="cpp">#include "IMyPanel.h"</source> | ||
to vgui_int.cpp file. | |||
Then add this line into the VGui_Shutdown() function. | |||
<source lang="cpp">mypanel->Destroy();</source> | |||
If you plan to create a panel for the main menu, you need to put this into the construction function (''VGui_CreateGlobalPanels()''): | If you plan to create a panel for the main menu, you need to put this into the construction function (''VGui_CreateGlobalPanels()''): | ||
< | <source lang="cpp">VPANEL GameUiDll = enginevgui->GetPanel( PANEL_GAMEUIDLL); | ||
mypanel->Create(GameUiDll); | mypanel->Create(GameUiDll); | ||
</ | </source> | ||
This will cause the panel to appear when you start your mod. | This will cause the panel to appear when you start your mod. | ||
=Adding elements= | ==Adding elements== | ||
There are two ways to add new elements. | There are two ways to add new elements. One is to use the VGUI2 Builder (Press CTRL+SHIFT+ALT+B to open it). Since the VGUI2 Builder doesn’t come with all the essential elements, you should add elements in your code. Therefore, you have the choice in-between 50 elements, individually stored in the vgui_controls folder. Indeed, even this is pretty easy. You add a pointer into the class declaration. Here is an example: | ||
< | <source lang="cpp">vgui::TextEntry* m_pTime; // Panel class declaration, private section</source> | ||
Add this to the panels constructor: | Add this to the panels constructor: | ||
< | <source lang="cpp"> | ||
m_pTime = new vgui::TextEntry(this, "MyTextEntry"); | m_pTime = new vgui::TextEntry(this, "MyTextEntry"); | ||
m_pTime->SetPos(15, 310); | m_pTime->SetPos(15, 310); | ||
m_pTime->SetSize(50, 20); | m_pTime->SetSize(50, 20); | ||
</ | </source> | ||
You'll also have to add the following include to get the constructors for TextEntry: | |||
<source lang="cpp">#include <vgui_controls/TextEntry.h></source> | |||
==Making it open with pressing a new option at the main menu== | |||
It is time to make it appear after pressing a new option that we will add to the main menu. On this example, we will use a variable which allows us to set the state of our panel, combined with a button. Here is the example: | |||
===MyPanel.cpp=== | |||
On start, add at the top under <vgui_controls/Frame.h> the following include, so we can add buttons at the code for our panel: | |||
= | <source lang="cpp"> | ||
#include <vgui_controls/Button.h> | |||
</source> | |||
= | Add this variable into the private declaration. It will allows you to create later a button for the panel so it can close it: | ||
<source lang="cpp"> | |||
private: | |||
//Other used VGUI control Elements: | |||
Button *m_pCloseButton; | |||
</source> | |||
Underneath all of the other code, add: | Underneath all of the other code, add: | ||
< | <source lang="cpp"> | ||
ConVar cl_showmypanel("cl_showmypanel", " | ConVar cl_showmypanel("cl_showmypanel", "0", FCVAR_CLIENTDLL, "Sets the state of myPanel <state>");</source> | ||
This | This line defines a new CVAR that it will make show or hide our new panel. Now continue by adding the following line at OnTick(): | ||
< | <source lang="cpp"> | ||
void CMyPanel::OnTick() | |||
{ | { | ||
BaseClass::OnTick(); | BaseClass::OnTick(); | ||
SetVisible(cl_showmypanel.GetBool()); //CL_SHOWMYPANEL / 1 BY DEFAULT | SetVisible(cl_showmypanel.GetBool()); //CL_SHOWMYPANEL / 1 BY DEFAULT | ||
}</ | }</source> | ||
On next, add a command to toggle the panel on or off: | |||
< | |||
<source lang="cpp"> | |||
CON_COMMAND(ToggleMyPanel, "Toggles myPanel on or off") | CON_COMMAND(ToggleMyPanel, "Toggles myPanel on or off") | ||
{ | { | ||
cl_showmypanel.SetValue(!cl_showmypanel.GetBool()); | cl_showmypanel.SetValue(!cl_showmypanel.GetBool()); | ||
}; | }; | ||
</ | </source> | ||
In the last part of this tutorial we will add a button that it will make close the panel on pressing it. Add the following at the OnCommand() part: | |||
In the last part of this tutorial we will add | |||
< | <source lang="cpp"> | ||
void CMyPanel::OnCommand(const char* pcCommand) | void CMyPanel::OnCommand(const char* pcCommand) | ||
{ | { | ||
BaseClass::OnCommand(pcCommand); | |||
if(!Q_stricmp(pcCommand, "turnoff")) | if(!Q_stricmp(pcCommand, "turnoff")) | ||
cl_showmypanel.SetValue(0); | cl_showmypanel.SetValue(0); | ||
} | } | ||
</ | </source> | ||
This will recieve the command "turnoff" that gets sent when we press the button, and on next, it will set cl_showmypanel to 0, closing the panel. | |||
Create the button itself at the panels constructor (you can do it too at the RES file, but coding it will allow you to have more customization options): | |||
<source lang="cpp"> | |||
m_pCloseButton = new Button(this, "Button", "Close", this, "turnoff"); | |||
m_pCloseButton->SetPos(433, 472); | |||
m_pCloseButton->SetDepressedSound("common/bugreporter_succeeded.wav"); | |||
m_pCloseButton->SetReleasedSound("ui/buttonclick.wav"); | |||
</source> | |||
We are creating a button that is parented with the panel, is called "Button", it has written on it "Close", it sends the command "turnoff", it is set to appear at botton right of our panel, and it will make two sounds, one when pressed, and another when released. | |||
Now add <source lang="cpp">mypanel->Activate();</source> underneath <source lang="cpp">cl_showmypanel.SetValue(!cl_showmypanel.GetBool());</source>. This will | |||
focus on your panel. To be able | focus (bring it at the front of the screen, over all the panels) on your panel. To be able use the Activate() command, you will need to add in '''MyPanel.cpp'''.: | ||
< | <source lang="cpp"> | ||
void Activate( void ) | void Activate( void ) | ||
{ | { | ||
Line 273: | Line 302: | ||
} | } | ||
} | } | ||
</ | </source> | ||
Underneath: | Underneath: | ||
< | <source lang="cpp"> | ||
void Destroy( void ) | void Destroy( void ) | ||
{ | { | ||
Line 286: | Line 315: | ||
} | } | ||
} | } | ||
</ | </source> | ||
And add: | And add in '''IMyPanel.h'''.: | ||
< | <source lang="cpp"> | ||
virtual void Activate( void ) = 0; | virtual void Activate( void ) = 0; | ||
</ | </source> | ||
Underneath: | Underneath: | ||
<source lang="cpp"> | |||
virtual void Destroy( void ) = 0; | |||
</source> | |||
==Main Menu and the panel res file== | |||
To open to your new panel from the main menu, open up '''GameMenu.res''' in your ''/resource/'' folder, and add: | |||
<pre> | <pre> | ||
"5"//Actually this number must be in order with the rest of elements | |||
{ | |||
"label" "My Panel" | |||
"command" "engine ToggleMyPanel" | |||
"notmulti" "1" //Add this only if you don't want to show this option while on a multiplayer game ("notsingle" "1" for singleplayer) | |||
} | |||
</pre> | </pre> | ||
(There are more examples of the possible variables [[GameMenu|here]]). | |||
This adds on the position 5 a new option called My Panel, that on press, it will send the command "ToggleMyPanel". | |||
And for the last step, create your panel RES design (this tutorial creates one at the ui folder inside resource). You can use the following as example: | |||
<div style="max-height:30em; overflow:auto;"><pre> | |||
"resource/ui/mypanel.res" | |||
{ | |||
"MyPanel" | |||
{ | |||
"ControlName" "CMyPanel" | |||
"fieldName" "MyPanel" | |||
"xpos" "797" | |||
"ypos" "301" | |||
"wide" "512" | |||
"tall" "512" | |||
"autoResize" "0" | |||
"pinCorner" "0" | |||
"RoundedCorners" "15" | |||
"pin_corner_to_sibling" "0" | |||
"pin_to_sibling_corner" "0" | |||
"visible" "1" | |||
"enabled" "1" | |||
"tabPosition" "0" | |||
"settitlebarvisible" "1" | |||
"title" "MOD OPTIONS" | |||
} | |||
"frame_topGrip" | |||
{ | |||
"ControlName" "Panel" | |||
"fieldName" "frame_topGrip" | |||
"xpos" "8" | |||
"ypos" "0" | |||
"wide" "496" | |||
"tall" "5" | |||
"autoResize" "0" | |||
"pinCorner" "0" | |||
"RoundedCorners" "15" | |||
"pin_corner_to_sibling" "0" | |||
"pin_to_sibling_corner" "0" | |||
"visible" "1" | |||
"enabled" "1" | |||
"tabPosition" "0" | |||
} | |||
"frame_bottomGrip" | |||
{ | |||
"ControlName" "Panel" | |||
"fieldName" "frame_bottomGrip" | |||
"xpos" "8" | |||
"ypos" "507" | |||
"wide" "486" | |||
"tall" "5" | |||
"autoResize" "0" | |||
"pinCorner" "0" | |||
"RoundedCorners" "15" | |||
"pin_corner_to_sibling" "0" | |||
"pin_to_sibling_corner" "0" | |||
"visible" "1" | |||
"enabled" "1" | |||
"tabPosition" "0" | |||
} | |||
"frame_leftGrip" | |||
{ | |||
"ControlName" "Panel" | |||
"fieldName" "frame_leftGrip" | |||
"xpos" "0" | |||
"ypos" "8" | |||
"wide" "5" | |||
"tall" "496" | |||
"autoResize" "0" | |||
"pinCorner" "0" | |||
"RoundedCorners" "15" | |||
"pin_corner_to_sibling" "0" | |||
"pin_to_sibling_corner" "0" | |||
"visible" "1" | |||
"enabled" "1" | |||
"tabPosition" "0" | |||
} | |||
"frame_rightGrip" | |||
{ | |||
"ControlName" "Panel" | |||
"fieldName" "frame_rightGrip" | |||
"xpos" "507" | |||
"ypos" "8" | |||
"wide" "5" | |||
"tall" "486" | |||
"autoResize" "0" | |||
"pinCorner" "0" | |||
"RoundedCorners" "15" | |||
"pin_corner_to_sibling" "0" | |||
"pin_to_sibling_corner" "0" | |||
"visible" "1" | |||
"enabled" "1" | |||
"tabPosition" "0" | |||
} | |||
"frame_tlGrip" | |||
{ | |||
"ControlName" "Panel" | |||
"fieldName" "frame_tlGrip" | |||
"xpos" "0" | |||
"ypos" "0" | |||
"wide" "8" | |||
"tall" "8" | |||
"autoResize" "0" | |||
"pinCorner" "0" | |||
"RoundedCorners" "15" | |||
"pin_corner_to_sibling" "0" | |||
"pin_to_sibling_corner" "0" | |||
"visible" "1" | |||
"enabled" "1" | |||
"tabPosition" "0" | |||
} | |||
"frame_trGrip" | |||
{ | |||
"ControlName" "Panel" | |||
"fieldName" "frame_trGrip" | |||
"xpos" "504" | |||
"ypos" "0" | |||
"wide" "8" | |||
"tall" "8" | |||
"autoResize" "0" | |||
"pinCorner" "0" | |||
"RoundedCorners" "15" | |||
"pin_corner_to_sibling" "0" | |||
"pin_to_sibling_corner" "0" | |||
"visible" "1" | |||
"enabled" "1" | |||
"tabPosition" "0" | |||
} | |||
"frame_blGrip" | |||
{ | |||
"ControlName" "Panel" | |||
"fieldName" "frame_blGrip" | |||
"xpos" "0" | |||
"ypos" "504" | |||
"wide" "8" | |||
"tall" "8" | |||
"autoResize" "0" | |||
"pinCorner" "0" | |||
"RoundedCorners" "15" | |||
"pin_corner_to_sibling" "0" | |||
"pin_to_sibling_corner" "0" | |||
"visible" "1" | |||
"enabled" "1" | |||
"tabPosition" "0" | |||
} | |||
"frame_brGrip" | |||
{ | |||
"ControlName" "Panel" | |||
"fieldName" "frame_brGrip" | |||
"xpos" "494" | |||
"ypos" "494" | |||
"wide" "18" | |||
"tall" "18" | |||
"autoResize" "0" | |||
"pinCorner" "0" | |||
"RoundedCorners" "15" | |||
"pin_corner_to_sibling" "0" | |||
"pin_to_sibling_corner" "0" | |||
"visible" "1" | |||
"enabled" "1" | |||
"tabPosition" "0" | |||
} | |||
"frame_caption" | |||
{ | |||
"ControlName" "Panel" | |||
"fieldName" "frame_caption" | |||
"xpos" "3" | |||
"ypos" "-16" | |||
"wide" "502" | |||
"tall" "23" | |||
"autoResize" "0" | |||
"pinCorner" "0" | |||
"RoundedCorners" "15" | |||
"pin_corner_to_sibling" "0" | |||
"pin_to_sibling_corner" "0" | |||
"visible" "1" | |||
"enabled" "1" | |||
"tabPosition" "0" | |||
} | |||
"frame_minimize" | |||
{ | |||
"ControlName" "Button" | |||
"fieldName" "frame_minimize" | |||
"xpos" "0" | |||
"ypos" "0" | |||
"wide" "18" | |||
"tall" "18" | |||
"autoResize" "0" | |||
"pinCorner" "0" | |||
"RoundedCorners" "15" | |||
"pin_corner_to_sibling" "0" | |||
"pin_to_sibling_corner" "0" | |||
"visible" "0" | |||
"enabled" "1" | |||
"tabPosition" "0" | |||
"labelText" "0" | |||
"textAlignment" "north-west" | |||
"dulltext" "0" | |||
"brighttext" "0" | |||
"wrap" "0" | |||
"centerwrap" "0" | |||
"textinsetx" "2" | |||
"textinsety" "1" | |||
"auto_wide_tocontents" "0" | |||
"use_proportional_insets" "0" | |||
"Default" "0" | |||
} | |||
"frame_maximize" | |||
{ | |||
"ControlName" "Button" | |||
"fieldName" "frame_maximize" | |||
"xpos" "0" | |||
"ypos" "0" | |||
"wide" "18" | |||
"tall" "18" | |||
"autoResize" "0" | |||
"pinCorner" "0" | |||
"RoundedCorners" "15" | |||
"pin_corner_to_sibling" "0" | |||
"pin_to_sibling_corner" "0" | |||
"visible" "0" | |||
"enabled" "1" | |||
"tabPosition" "0" | |||
"labelText" "1" | |||
"textAlignment" "north-west" | |||
"dulltext" "0" | |||
"brighttext" "0" | |||
"wrap" "0" | |||
"centerwrap" "0" | |||
"textinsetx" "2" | |||
"textinsety" "1" | |||
"auto_wide_tocontents" "0" | |||
"use_proportional_insets" "0" | |||
"Default" "0" | |||
} | |||
"frame_mintosystray" | |||
{ | |||
"ControlName" "Button" | |||
"fieldName" "frame_mintosystray" | |||
"xpos" "0" | |||
"ypos" "0" | |||
"wide" "18" | |||
"tall" "18" | |||
"autoResize" "0" | |||
"pinCorner" "0" | |||
"RoundedCorners" "15" | |||
"pin_corner_to_sibling" "0" | |||
"pin_to_sibling_corner" "0" | |||
"visible" "0" | |||
"enabled" "1" | |||
"tabPosition" "0" | |||
"labelText" "o" | |||
"textAlignment" "north-west" | |||
"dulltext" "0" | |||
"brighttext" "0" | |||
"wrap" "0" | |||
"centerwrap" "0" | |||
"textinsetx" "2" | |||
"textinsety" "1" | |||
"auto_wide_tocontents" "0" | |||
"use_proportional_insets" "0" | |||
"command" "MinimizeToSysTray" | |||
"Default" "0" | |||
} | |||
"frame_close" | |||
{ | |||
"ControlName" "Button" | |||
"fieldName" "frame_close" | |||
"xpos" "487" | |||
"ypos" "8" | |||
"wide" "18" | |||
"tall" "18" | |||
"autoResize" "0" | |||
"pinCorner" "0" | |||
"RoundedCorners" "15" | |||
"pin_corner_to_sibling" "0" | |||
"pin_to_sibling_corner" "0" | |||
"visible" "1" | |||
"enabled" "1" | |||
"tabPosition" "0" | |||
"labelText" "r" | |||
"textAlignment" "north-west" | |||
"dulltext" "0" | |||
"brighttext" "0" | |||
"wrap" "0" | |||
"centerwrap" "0" | |||
"textinsetx" "2" | |||
"textinsety" "1" | |||
"auto_wide_tocontents" "0" | |||
"use_proportional_insets" "0" | |||
"Default" "0" | |||
"command" "turnoff" | |||
} | |||
"frame_menu" | |||
{ | |||
"ControlName" "FrameSystemButton" | |||
"fieldName" "frame_menu" | |||
"xpos" "7" | |||
"ypos" "8" | |||
"wide" "18" | |||
"tall" "18" | |||
"autoResize" "0" | |||
"pinCorner" "0" | |||
"RoundedCorners" "15" | |||
"pin_corner_to_sibling" "0" | |||
"pin_to_sibling_corner" "0" | |||
"visible" "1" | |||
"enabled" "1" | |||
"tabPosition" "0" | |||
"textAlignment" "west" | |||
"dulltext" "0" | |||
"brighttext" "0" | |||
"wrap" "0" | |||
"centerwrap" "0" | |||
"textinsetx" "0" | |||
"textinsety" "0" | |||
"auto_wide_tocontents" "0" | |||
"use_proportional_insets" "0" | |||
"Default" "0" | |||
} | |||
"BuildModeDialog" | |||
{ | |||
"ControlName" "BuildModeDialog" | |||
"fieldName" "BuildModeDialog" | |||
"xpos" "327" | |||
"ypos" "301" | |||
"wide" "300" | |||
"tall" "420" | |||
"autoResize" "0" | |||
"pinCorner" "0" | |||
"RoundedCorners" "15" | |||
"pin_corner_to_sibling" "0" | |||
"pin_to_sibling_corner" "0" | |||
"visible" "1" | |||
"enabled" "1" | |||
"tabPosition" "0" | |||
"settitlebarvisible" "1" | |||
"title" "#Frame_Untitled" | |||
} | |||
} | |||
</pre></div> | |||
==Final result== | |||
This is how it should look at the end both your Cpp and header files: | |||
'''MyPanel.cpp''' | |||
<div style="max-height:30em; overflow:auto;"> | |||
<source lang="cpp"> | |||
//The following include files are necessary to allow your MyPanel.cpp to compile. | |||
#include "cbase.h" | |||
#include "IMyPanel.h" | |||
using namespace vgui; | |||
#include <vgui/IVGui.h> | |||
#include <vgui_controls/Frame.h> | |||
#include <vgui_controls/Button.h> | |||
//CMyPanel class: Tutorial example class | |||
class CMyPanel : public vgui::Frame | |||
{ | |||
DECLARE_CLASS_SIMPLE(CMyPanel, vgui::Frame); | |||
//CMyPanel : This Class / vgui::Frame : BaseClass | |||
CMyPanel(vgui::VPANEL parent); // Constructor | |||
~CMyPanel(){}; // Destructor | |||
protected: | |||
//VGUI overrides: | |||
virtual void OnTick(); | |||
virtual void OnCommand(const char* pcCommand); | |||
private: | |||
//Other used VGUI control Elements: | |||
Button *m_pCloseButton; | |||
}; | |||
// Constuctor: Initializes the Panel | |||
CMyPanel::CMyPanel(vgui::VPANEL parent) | |||
: BaseClass(NULL, "MyPanel") | |||
{ | |||
SetParent(parent); | |||
SetKeyBoardInputEnabled(true); | |||
SetMouseInputEnabled(true); | |||
SetProportional(false); | |||
SetTitleBarVisible(true); | |||
SetMinimizeButtonVisible(false); | |||
SetMaximizeButtonVisible(false); | |||
SetCloseButtonVisible(false); | |||
SetSizeable(false); | |||
SetMoveable(false); | |||
SetVisible(true); | |||
SetScheme(vgui::scheme()->LoadSchemeFromFile("resource/SourceScheme.res", "SourceScheme")); | |||
LoadControlSettings("resource/UI/playermodelsel.res"); | |||
vgui::ivgui()->AddTickSignal(GetVPanel(), 100); | |||
DevMsg("MyPanel has been constructed\n"); | |||
//Button done | |||
m_pCloseButton = new Button(this, "Button", "Close", this, "turnoff"); | |||
m_pCloseButton->SetPos(433, 472); | |||
m_pCloseButton->SetDepressedSound("common/bugreporter_succeeded.wav"); | |||
m_pCloseButton->SetReleasedSound("ui/buttonclick.wav"); | |||
} | |||
//Class: CMyPanelInterface Class. Used for construction. | |||
class CMyPanelInterface : public MyPanel | |||
{ | |||
private: | |||
CMyPanel *MyPanel; | |||
public: | |||
CMyPanelInterface() | |||
{ | |||
MyPanel = NULL; | |||
} | |||
void Create(vgui::VPANEL parent) | |||
{ | |||
MyPanel = new CMyPanel(parent); | |||
} | |||
void Destroy() | |||
{ | |||
if (MyPanel) | |||
{ | |||
MyPanel->SetParent((vgui::Panel *)NULL); | |||
delete MyPanel; | |||
} | |||
} | |||
void Activate(void) | |||
{ | |||
if (MyPanel) | |||
{ | |||
MyPanel->Activate(); | |||
} | |||
} | |||
}; | |||
static CMyPanelInterface g_MyPanel; | |||
MyPanel* mypanel = (MyPanel*)&g_MyPanel; | |||
ConVar cl_showmypanel("cl_showmypanel", "0", FCVAR_CLIENTDLL, "Sets the state of myPanel <state>"); | |||
void CMyPanel::OnTick() | |||
{ | |||
BaseClass::OnTick(); | |||
SetVisible(cl_showmypanel.GetBool()); | |||
} | |||
CON_COMMAND(OpenTestPanelFenix, "Toggles testpanelfenix on or off") | |||
{ | |||
cl_showmypanel.SetValue(!cl_showmypanel.GetBool()); | |||
mypanel->Activate(); | |||
}; | |||
void CMyPanel::OnCommand(const char* pcCommand) | |||
{ | |||
BaseClass::OnCommand(pcCommand); | |||
if (!Q_stricmp(pcCommand, "turnoff")) | |||
cl_showmypanel.SetValue(0); | |||
} | |||
</source></div> | |||
'''IMyPanel.h''' | |||
<source lang="cpp"> | |||
class MyPanel | |||
{ | |||
public: | |||
virtual void Create(vgui::VPANEL parent) = 0; | |||
virtual void Destroy(void) = 0; | |||
virtual void Activate(void) = 0; | |||
}; | |||
extern MyPanel* mypanel; | |||
</source> | |||
Now compile your files, boot your mod, and press the new option that it should appear at the main menu. If everything goes fine, a panel will open, and pressing the button "Close" will close it. You can also close it by pressing again the main menu option, since we made it toggle. | |||
==Makefile changes on Linux== | |||
In source-sdk-2013/mp/src/game/client/client_linux32_hl2mp.mak (for multiplayer), you need to add a mention of your new file to the list of files to be built, and then a recipe to build it. | |||
First, add the .cpp file to the list to be built: | |||
<source lang="make"> | |||
CPPFILES= \ | |||
MyPanel.cpp \ | |||
../../common/compiledcaptionswap.cpp \ | |||
../../common/language.cpp \ | |||
... | |||
</source> | |||
Then, down near the "recipe" for vgui_int.cpp, add the following recipe to build MyPanel: | |||
<source lang="make"> | |||
ifneq (clean, $(findstring clean, $(MAKECMDGOALS))) | |||
-include $(OBJ_DIR)/MyPanel.P | |||
endif | |||
$(OBJ_DIR)/MyPanel.o : $(PWD)/MyPanel.cpp $(PWD)/client_linux32_hl2mp.mak $(SRCROOT)/devtools/makefile_base_posix.mak | |||
$(PRE_COMPILE_FILE) | |||
$(COMPILE_FILE) $(POST_COMPILE_FILE) | |||
</source> | |||
==See also== | ==See also== | ||
* [[Modifying Source GameUI]] is a lighter dose of creating your own UI. | * [[Modifying Source GameUI]] is a lighter dose of creating your own UI. | ||
* [[VGUI2: Non RES-File Controls|Non RES-File Controls]] are controls that can be compiled in code instead of RES files like in Half-Life 1 |
Latest revision as of 17:42, 18 July 2025
Requirements
Have read and understood (or understand):
Can code:
- C++
- Script
This tutorial is about creating a simple interactive interface
Understanding how VGUI2 works
Every VGUI2 dialog that you see while using source based games is basically called a Panel.
Every Panel consists of three components:
- Scheme
- Control Settings
- Code
The Scheme
The scheme is a general configuration file which stores information about the colors of certain elements such as buttons, combo boxes, labels, etc. A typical scheme file is SourceScheme.res, for example. If you plan to create a panel which looks like the other panels in the menu you should use the same scheme file as they do.
Control Settings
The control settings file stores information about the relative position of your panel and its elements.
Every panel has a very own resource file. To create a resource file, there are two ways: Either you create one on your own using an editor like notepad, or you use Valves InGame Resource Editor.
Code
The code is the most important part of a panel, since the code decides what to do if the user clicks a button. To create and destroy the panel, you use the code. Fortunately you can set a lot more things than in the resource file(s). Code is the most important thing in this tutorial.
Creating a panel
Ok, let us assume we want to create a door, for real this time. Since we are not able to create a door from scratch, the first thing we do is to step by at the building centre. What we ask for, is a basic door. It works, but we have still plans to customize it.
Starting Off
The panel class is the base class of all VGUI2 elements. To get a rough overview about all the VGUI2 elements, have a look into the vgui_elements folder. Of course, we don't just buy some wood in our local building centre.
The important class is the EditablePanel class that inherits from the Panel class. Our panel will be a new class which inherits from the EditablePanel class. This results in several advantages: We can code methods related to the content of the panel, we can overwrite the methods of the base classes and do much more useful stuff.
You can create a new file MyPanel.cpp underneath the Source Files inside the client project.
MyPanel.cpp
//The following include files are necessary to allow your MyPanel.cpp to compile.
#include "cbase.h"
#include "IMyPanel.h"
using namespace vgui;
#include <vgui/IVGui.h>
#include <vgui_controls/Frame.h>
//CMyPanel class: Tutorial example class
class CMyPanel : public vgui::Frame
{
DECLARE_CLASS_SIMPLE(CMyPanel, vgui::Frame);
//CMyPanel : This Class / vgui::Frame : BaseClass
CMyPanel(vgui::VPANEL parent); // Constructor
~CMyPanel(){}; // Destructor
protected:
//VGUI overrides:
virtual void OnTick();
virtual void OnCommand(const char* pcCommand);
private:
//Other used VGUI control Elements:
};
The constructor: The argument is vgui::VPANEL parent. After reading the VGUI Documentation, you should know that every panel has a parent and why it has a parent.
Underneath the above code, add:
// Constuctor: Initializes the Panel
CMyPanel::CMyPanel(vgui::VPANEL parent)
: BaseClass(NULL, "MyPanel")
{
SetParent( parent );
SetKeyBoardInputEnabled( true );
SetMouseInputEnabled( true );
SetProportional( false );
SetTitleBarVisible( true );
SetMinimizeButtonVisible( false );
SetMaximizeButtonVisible( false );
SetCloseButtonVisible( false );
SetSizeable( false );
SetMoveable( false );
SetVisible( true );
SetScheme(vgui::scheme()->LoadSchemeFromFile("resource/SourceScheme.res", "SourceScheme"));
LoadControlSettings("resource/UI/mypanel.res");
vgui::ivgui()->AddTickSignal( GetVPanel(), 100 );
DevMsg("MyPanel has been constructed\n");
}
The first lines are pretty easy to understand. SetScheme is used to set the source scheme, which is the standard scheme for Half-Life 2. We get a pointer to the scheme by calling LoadSchemeFromFile(...). The LoadControlSettings function is used to load the control settings resolution file. The last line is explained in the VGUI2 documentation.
The SetProportional( false ); function decides to make the panel big or small, having it set to true will cause your panel to have big fonts, controls etc.
Finally, if you're going to edit it with the VGUI Build Mode Editor, you will need precreate the directory it goes into, and posibly give your resource an all-lower-case name [See also the Discussion page]
Underneath the above code, add:
//Class: CMyPanelInterface Class. Used for construction.
class CMyPanelInterface : public IMyPanel
{
private:
CMyPanel *MyPanel;
public:
CMyPanelInterface()
{
MyPanel = NULL;
}
void Create(vgui::VPANEL parent)
{
MyPanel = new CMyPanel(parent);
}
void Destroy()
{
if (MyPanel)
{
MyPanel->SetParent( (vgui::Panel *)NULL);
delete MyPanel;
}
}
};
static CMyPanelInterface g_MyPanel;
IMyPanel* mypanel = (IMyPanel*)&g_MyPanel;
void CMyPanel::OnTick()
{
BaseClass::OnTick();
}
void CMyPanel::OnCommand(const char* pcCommand)
{
BaseClass::OnCommand(pcCommand);
}
Next you can create IMyPanel.h in the same folder.
IMyPanel.h
// IMyPanel.h
class IMyPanel
{
public:
virtual void Create( vgui::VPANEL parent ) = 0;
virtual void Destroy( void ) = 0;
};
extern IMyPanel* mypanel;
Calling the panel
To call the panel, we add a few lines to the vgui_int.cpp file. Vgui_int.cpp includes functions which call all the panels. The VGui_CreateGlobalPanels() function is where the addition takes place.
This is the point, where you have to decide when the panel should show up. Either you create a panel that can be accessed during the game, or you create a panel for the main menu.
I assume that you want to create a panel for the game.
So, after including the panel, you should add at VGui_CreateGlobalPanels() the following:
mypanel->Create(gameParent);
and check to see if gameParent has been declared at the top of the function, if it hasn't then add
VPANEL gameParent = enginevgui->GetPanel( PANEL_CLIENTDLL );
Note: To have your screen appear in-game like the Counter-Strike buy menus or team selection menus, change PANEL_CLIENTDLL to PANEL_INGAMESCREENS , otherwise it will be visible only when you press Escape to go to the game menu.
Note: Dont forget to add
#include "IMyPanel.h"
to vgui_int.cpp file.
Then add this line into the VGui_Shutdown() function.
mypanel->Destroy();
If you plan to create a panel for the main menu, you need to put this into the construction function (VGui_CreateGlobalPanels()):
VPANEL GameUiDll = enginevgui->GetPanel( PANEL_GAMEUIDLL);
mypanel->Create(GameUiDll);
This will cause the panel to appear when you start your mod.
Adding elements
There are two ways to add new elements. One is to use the VGUI2 Builder (Press CTRL+SHIFT+ALT+B to open it). Since the VGUI2 Builder doesn’t come with all the essential elements, you should add elements in your code. Therefore, you have the choice in-between 50 elements, individually stored in the vgui_controls folder. Indeed, even this is pretty easy. You add a pointer into the class declaration. Here is an example:
vgui::TextEntry* m_pTime; // Panel class declaration, private section
Add this to the panels constructor:
m_pTime = new vgui::TextEntry(this, "MyTextEntry");
m_pTime->SetPos(15, 310);
m_pTime->SetSize(50, 20);
You'll also have to add the following include to get the constructors for TextEntry:
#include <vgui_controls/TextEntry.h>
It is time to make it appear after pressing a new option that we will add to the main menu. On this example, we will use a variable which allows us to set the state of our panel, combined with a button. Here is the example:
MyPanel.cpp
On start, add at the top under <vgui_controls/Frame.h> the following include, so we can add buttons at the code for our panel:
#include <vgui_controls/Button.h>
Add this variable into the private declaration. It will allows you to create later a button for the panel so it can close it:
private:
//Other used VGUI control Elements:
Button *m_pCloseButton;
Underneath all of the other code, add:
ConVar cl_showmypanel("cl_showmypanel", "0", FCVAR_CLIENTDLL, "Sets the state of myPanel <state>");
This line defines a new CVAR that it will make show or hide our new panel. Now continue by adding the following line at OnTick():
void CMyPanel::OnTick()
{
BaseClass::OnTick();
SetVisible(cl_showmypanel.GetBool()); //CL_SHOWMYPANEL / 1 BY DEFAULT
}
On next, add a command to toggle the panel on or off:
CON_COMMAND(ToggleMyPanel, "Toggles myPanel on or off")
{
cl_showmypanel.SetValue(!cl_showmypanel.GetBool());
};
In the last part of this tutorial we will add a button that it will make close the panel on pressing it. Add the following at the OnCommand() part:
void CMyPanel::OnCommand(const char* pcCommand)
{
BaseClass::OnCommand(pcCommand);
if(!Q_stricmp(pcCommand, "turnoff"))
cl_showmypanel.SetValue(0);
}
This will recieve the command "turnoff" that gets sent when we press the button, and on next, it will set cl_showmypanel to 0, closing the panel.
Create the button itself at the panels constructor (you can do it too at the RES file, but coding it will allow you to have more customization options):
m_pCloseButton = new Button(this, "Button", "Close", this, "turnoff");
m_pCloseButton->SetPos(433, 472);
m_pCloseButton->SetDepressedSound("common/bugreporter_succeeded.wav");
m_pCloseButton->SetReleasedSound("ui/buttonclick.wav");
We are creating a button that is parented with the panel, is called "Button", it has written on it "Close", it sends the command "turnoff", it is set to appear at botton right of our panel, and it will make two sounds, one when pressed, and another when released.
Now add
mypanel->Activate();
underneath
cl_showmypanel.SetValue(!cl_showmypanel.GetBool());
. This will
focus (bring it at the front of the screen, over all the panels) on your panel. To be able use the Activate() command, you will need to add in MyPanel.cpp.:
void Activate( void )
{
if ( MyPanel )
{
MyPanel->Activate();
}
}
Underneath:
void Destroy( void )
{
if ( MyPanel )
{
MyPanel->SetParent( (vgui::Panel *)NULL );
delete MyPanel;
}
}
And add in IMyPanel.h.:
virtual void Activate( void ) = 0;
Underneath:
virtual void Destroy( void ) = 0;
Main Menu and the panel res file
To open to your new panel from the main menu, open up GameMenu.res in your /resource/ folder, and add:
"5"//Actually this number must be in order with the rest of elements { "label" "My Panel" "command" "engine ToggleMyPanel" "notmulti" "1" //Add this only if you don't want to show this option while on a multiplayer game ("notsingle" "1" for singleplayer) }
(There are more examples of the possible variables here).
This adds on the position 5 a new option called My Panel, that on press, it will send the command "ToggleMyPanel".
And for the last step, create your panel RES design (this tutorial creates one at the ui folder inside resource). You can use the following as example:
"resource/ui/mypanel.res" { "MyPanel" { "ControlName" "CMyPanel" "fieldName" "MyPanel" "xpos" "797" "ypos" "301" "wide" "512" "tall" "512" "autoResize" "0" "pinCorner" "0" "RoundedCorners" "15" "pin_corner_to_sibling" "0" "pin_to_sibling_corner" "0" "visible" "1" "enabled" "1" "tabPosition" "0" "settitlebarvisible" "1" "title" "MOD OPTIONS" } "frame_topGrip" { "ControlName" "Panel" "fieldName" "frame_topGrip" "xpos" "8" "ypos" "0" "wide" "496" "tall" "5" "autoResize" "0" "pinCorner" "0" "RoundedCorners" "15" "pin_corner_to_sibling" "0" "pin_to_sibling_corner" "0" "visible" "1" "enabled" "1" "tabPosition" "0" } "frame_bottomGrip" { "ControlName" "Panel" "fieldName" "frame_bottomGrip" "xpos" "8" "ypos" "507" "wide" "486" "tall" "5" "autoResize" "0" "pinCorner" "0" "RoundedCorners" "15" "pin_corner_to_sibling" "0" "pin_to_sibling_corner" "0" "visible" "1" "enabled" "1" "tabPosition" "0" } "frame_leftGrip" { "ControlName" "Panel" "fieldName" "frame_leftGrip" "xpos" "0" "ypos" "8" "wide" "5" "tall" "496" "autoResize" "0" "pinCorner" "0" "RoundedCorners" "15" "pin_corner_to_sibling" "0" "pin_to_sibling_corner" "0" "visible" "1" "enabled" "1" "tabPosition" "0" } "frame_rightGrip" { "ControlName" "Panel" "fieldName" "frame_rightGrip" "xpos" "507" "ypos" "8" "wide" "5" "tall" "486" "autoResize" "0" "pinCorner" "0" "RoundedCorners" "15" "pin_corner_to_sibling" "0" "pin_to_sibling_corner" "0" "visible" "1" "enabled" "1" "tabPosition" "0" } "frame_tlGrip" { "ControlName" "Panel" "fieldName" "frame_tlGrip" "xpos" "0" "ypos" "0" "wide" "8" "tall" "8" "autoResize" "0" "pinCorner" "0" "RoundedCorners" "15" "pin_corner_to_sibling" "0" "pin_to_sibling_corner" "0" "visible" "1" "enabled" "1" "tabPosition" "0" } "frame_trGrip" { "ControlName" "Panel" "fieldName" "frame_trGrip" "xpos" "504" "ypos" "0" "wide" "8" "tall" "8" "autoResize" "0" "pinCorner" "0" "RoundedCorners" "15" "pin_corner_to_sibling" "0" "pin_to_sibling_corner" "0" "visible" "1" "enabled" "1" "tabPosition" "0" } "frame_blGrip" { "ControlName" "Panel" "fieldName" "frame_blGrip" "xpos" "0" "ypos" "504" "wide" "8" "tall" "8" "autoResize" "0" "pinCorner" "0" "RoundedCorners" "15" "pin_corner_to_sibling" "0" "pin_to_sibling_corner" "0" "visible" "1" "enabled" "1" "tabPosition" "0" } "frame_brGrip" { "ControlName" "Panel" "fieldName" "frame_brGrip" "xpos" "494" "ypos" "494" "wide" "18" "tall" "18" "autoResize" "0" "pinCorner" "0" "RoundedCorners" "15" "pin_corner_to_sibling" "0" "pin_to_sibling_corner" "0" "visible" "1" "enabled" "1" "tabPosition" "0" } "frame_caption" { "ControlName" "Panel" "fieldName" "frame_caption" "xpos" "3" "ypos" "-16" "wide" "502" "tall" "23" "autoResize" "0" "pinCorner" "0" "RoundedCorners" "15" "pin_corner_to_sibling" "0" "pin_to_sibling_corner" "0" "visible" "1" "enabled" "1" "tabPosition" "0" } "frame_minimize" { "ControlName" "Button" "fieldName" "frame_minimize" "xpos" "0" "ypos" "0" "wide" "18" "tall" "18" "autoResize" "0" "pinCorner" "0" "RoundedCorners" "15" "pin_corner_to_sibling" "0" "pin_to_sibling_corner" "0" "visible" "0" "enabled" "1" "tabPosition" "0" "labelText" "0" "textAlignment" "north-west" "dulltext" "0" "brighttext" "0" "wrap" "0" "centerwrap" "0" "textinsetx" "2" "textinsety" "1" "auto_wide_tocontents" "0" "use_proportional_insets" "0" "Default" "0" } "frame_maximize" { "ControlName" "Button" "fieldName" "frame_maximize" "xpos" "0" "ypos" "0" "wide" "18" "tall" "18" "autoResize" "0" "pinCorner" "0" "RoundedCorners" "15" "pin_corner_to_sibling" "0" "pin_to_sibling_corner" "0" "visible" "0" "enabled" "1" "tabPosition" "0" "labelText" "1" "textAlignment" "north-west" "dulltext" "0" "brighttext" "0" "wrap" "0" "centerwrap" "0" "textinsetx" "2" "textinsety" "1" "auto_wide_tocontents" "0" "use_proportional_insets" "0" "Default" "0" } "frame_mintosystray" { "ControlName" "Button" "fieldName" "frame_mintosystray" "xpos" "0" "ypos" "0" "wide" "18" "tall" "18" "autoResize" "0" "pinCorner" "0" "RoundedCorners" "15" "pin_corner_to_sibling" "0" "pin_to_sibling_corner" "0" "visible" "0" "enabled" "1" "tabPosition" "0" "labelText" "o" "textAlignment" "north-west" "dulltext" "0" "brighttext" "0" "wrap" "0" "centerwrap" "0" "textinsetx" "2" "textinsety" "1" "auto_wide_tocontents" "0" "use_proportional_insets" "0" "command" "MinimizeToSysTray" "Default" "0" } "frame_close" { "ControlName" "Button" "fieldName" "frame_close" "xpos" "487" "ypos" "8" "wide" "18" "tall" "18" "autoResize" "0" "pinCorner" "0" "RoundedCorners" "15" "pin_corner_to_sibling" "0" "pin_to_sibling_corner" "0" "visible" "1" "enabled" "1" "tabPosition" "0" "labelText" "r" "textAlignment" "north-west" "dulltext" "0" "brighttext" "0" "wrap" "0" "centerwrap" "0" "textinsetx" "2" "textinsety" "1" "auto_wide_tocontents" "0" "use_proportional_insets" "0" "Default" "0" "command" "turnoff" } "frame_menu" { "ControlName" "FrameSystemButton" "fieldName" "frame_menu" "xpos" "7" "ypos" "8" "wide" "18" "tall" "18" "autoResize" "0" "pinCorner" "0" "RoundedCorners" "15" "pin_corner_to_sibling" "0" "pin_to_sibling_corner" "0" "visible" "1" "enabled" "1" "tabPosition" "0" "textAlignment" "west" "dulltext" "0" "brighttext" "0" "wrap" "0" "centerwrap" "0" "textinsetx" "0" "textinsety" "0" "auto_wide_tocontents" "0" "use_proportional_insets" "0" "Default" "0" } "BuildModeDialog" { "ControlName" "BuildModeDialog" "fieldName" "BuildModeDialog" "xpos" "327" "ypos" "301" "wide" "300" "tall" "420" "autoResize" "0" "pinCorner" "0" "RoundedCorners" "15" "pin_corner_to_sibling" "0" "pin_to_sibling_corner" "0" "visible" "1" "enabled" "1" "tabPosition" "0" "settitlebarvisible" "1" "title" "#Frame_Untitled" } }
Final result
This is how it should look at the end both your Cpp and header files:
MyPanel.cpp
//The following include files are necessary to allow your MyPanel.cpp to compile.
#include "cbase.h"
#include "IMyPanel.h"
using namespace vgui;
#include <vgui/IVGui.h>
#include <vgui_controls/Frame.h>
#include <vgui_controls/Button.h>
//CMyPanel class: Tutorial example class
class CMyPanel : public vgui::Frame
{
DECLARE_CLASS_SIMPLE(CMyPanel, vgui::Frame);
//CMyPanel : This Class / vgui::Frame : BaseClass
CMyPanel(vgui::VPANEL parent); // Constructor
~CMyPanel(){}; // Destructor
protected:
//VGUI overrides:
virtual void OnTick();
virtual void OnCommand(const char* pcCommand);
private:
//Other used VGUI control Elements:
Button *m_pCloseButton;
};
// Constuctor: Initializes the Panel
CMyPanel::CMyPanel(vgui::VPANEL parent)
: BaseClass(NULL, "MyPanel")
{
SetParent(parent);
SetKeyBoardInputEnabled(true);
SetMouseInputEnabled(true);
SetProportional(false);
SetTitleBarVisible(true);
SetMinimizeButtonVisible(false);
SetMaximizeButtonVisible(false);
SetCloseButtonVisible(false);
SetSizeable(false);
SetMoveable(false);
SetVisible(true);
SetScheme(vgui::scheme()->LoadSchemeFromFile("resource/SourceScheme.res", "SourceScheme"));
LoadControlSettings("resource/UI/playermodelsel.res");
vgui::ivgui()->AddTickSignal(GetVPanel(), 100);
DevMsg("MyPanel has been constructed\n");
//Button done
m_pCloseButton = new Button(this, "Button", "Close", this, "turnoff");
m_pCloseButton->SetPos(433, 472);
m_pCloseButton->SetDepressedSound("common/bugreporter_succeeded.wav");
m_pCloseButton->SetReleasedSound("ui/buttonclick.wav");
}
//Class: CMyPanelInterface Class. Used for construction.
class CMyPanelInterface : public MyPanel
{
private:
CMyPanel *MyPanel;
public:
CMyPanelInterface()
{
MyPanel = NULL;
}
void Create(vgui::VPANEL parent)
{
MyPanel = new CMyPanel(parent);
}
void Destroy()
{
if (MyPanel)
{
MyPanel->SetParent((vgui::Panel *)NULL);
delete MyPanel;
}
}
void Activate(void)
{
if (MyPanel)
{
MyPanel->Activate();
}
}
};
static CMyPanelInterface g_MyPanel;
MyPanel* mypanel = (MyPanel*)&g_MyPanel;
ConVar cl_showmypanel("cl_showmypanel", "0", FCVAR_CLIENTDLL, "Sets the state of myPanel <state>");
void CMyPanel::OnTick()
{
BaseClass::OnTick();
SetVisible(cl_showmypanel.GetBool());
}
CON_COMMAND(OpenTestPanelFenix, "Toggles testpanelfenix on or off")
{
cl_showmypanel.SetValue(!cl_showmypanel.GetBool());
mypanel->Activate();
};
void CMyPanel::OnCommand(const char* pcCommand)
{
BaseClass::OnCommand(pcCommand);
if (!Q_stricmp(pcCommand, "turnoff"))
cl_showmypanel.SetValue(0);
}
IMyPanel.h
class MyPanel
{
public:
virtual void Create(vgui::VPANEL parent) = 0;
virtual void Destroy(void) = 0;
virtual void Activate(void) = 0;
};
extern MyPanel* mypanel;
Now compile your files, boot your mod, and press the new option that it should appear at the main menu. If everything goes fine, a panel will open, and pressing the button "Close" will close it. You can also close it by pressing again the main menu option, since we made it toggle.
Makefile changes on Linux
In source-sdk-2013/mp/src/game/client/client_linux32_hl2mp.mak (for multiplayer), you need to add a mention of your new file to the list of files to be built, and then a recipe to build it.
First, add the .cpp file to the list to be built:
CPPFILES= \
MyPanel.cpp \
../../common/compiledcaptionswap.cpp \
../../common/language.cpp \
...
Then, down near the "recipe" for vgui_int.cpp, add the following recipe to build MyPanel:
ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
-include $(OBJ_DIR)/MyPanel.P
endif
$(OBJ_DIR)/MyPanel.o : $(PWD)/MyPanel.cpp $(PWD)/client_linux32_hl2mp.mak $(SRCROOT)/devtools/makefile_base_posix.mak
$(PRE_COMPILE_FILE)
$(COMPILE_FILE) $(POST_COMPILE_FILE)
See also
- Modifying Source GameUI is a lighter dose of creating your own UI.
- Non RES-File Controls are controls that can be compiled in code instead of RES files like in Half-Life 1