VGUI Documentation: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
m (Some text styles update)
 
(78 intermediate revisions by 37 users not shown)
Line 1: Line 1:
[[Category:Programming]]
{{LanguageBar}}
==Overview==
 


The VGUI2 library (vgui2.dll) is a proprietary graphic user interface (GUI) provided by the Source Engine. All Source & Steam applications use VGUI for displaying windows, dialogs or menus. The object architecture is hierarchical and all implemented elements drive from VGUI base classes. The keyboard & mouse input system is event driven and quite similar to other GUI libraries (like Java Swing or .Net forms). Implementations for the most common GUI elements like buttons, text fields or images are provided by the VGUI controls library (vgui_controls.lib). Besides drawing GUI elements and processing input, VGUI also handles localization for displaying text in the preferred user language. All VGUI base interface headers are located in \public\vgui\, control elements are defined in \public\vgui_controls\.
{{toc-right}}


As a mod author you will most likely use VGUI in the client.dll project to show menus, HUD elements or for in-game displays (on weapons or computer terminals etc). The base class all VGUI elements derive from is vgui::Panel, which defines an area on your screen that has a specific size and position, can draw itself and process input events. Dialog windows, text fields and buttons are all VGUI panels in a hierarchical parent-child relationship. The very first panel is the root panel and is provided by the Source engine. The client root panel covers the whole screen, but doesn't show anything. Even you could use the client root panel, most client panels use the shared BaseViewport panel as parent ( <code>g_pClientMode->GetViewport()</code> ).
'''[[:Category:VGUI|VGUI]]''' is Valve's proprietary [[Wikipedia:Graphical User Interface|Graphical User Interface]]. All or most {{src|2}} games, and formerly {{steamicon|2}} use VGUI to draw windows, dialogs and menus. It also handles {{↓|Localization|localization}}: the displaying of text in the user's preferred language.


This diagram shows a part of the client panel hierarchy. An arrow between two panels means "is parent of" :
* The object architecture is hierarchical and all implemented elements derive from VGUI base classes.
* The keyboard/mouse input system is event-driven and quite similar to other GUI systems.
* Implementations for the most common GUI elements like buttons, text fields or images are provided by the VGUI controls library {{Path|vgui_controls|lib|icon=file}}.
* Base interface headers are located in {{Path|\public\vgui\}}.
* Control elements are defined in {{Path|\public\vgui_controls\}}.


[[Image:Vgui2_1.gif|frame|left|VGUI client panel hierarchy]] <br style="clear:both">
As a mod author you will most likely use VGUI in the client.dll project to show menus, HUD elements or for in-game displays (on weapons or computer terminals etc).


To browse through the VGUI panel hierarchy during runtime, you can open the VGUI Hierarchy tool by executing in developer [[console]] <code>vgui_drawtree 1</code>. All panels are listed in an expandable tree view.
== Panels and hierarchy ==


[[Image:Vgui2_3.jpg|frame|left|VGUI Hierarchy tool]]
The base class all VGUI elements derive from is <code>vgui::Panel</code>, which defines an area on your screen that has a specific size and position, can draw itself and process input events. Dialog windows, text fields and buttons are all VGUI panels in a hierarchical parent-child relationship. The very first panel is the root panel and is provided by the Source engine. The client root panel covers the whole screen, but doesn't show anything. Even though you could use the client root panel, most client panels use the shared <code>BaseViewport</code> panel as parent ( <code>g_pClientMode->GetViewport()</code> ).


Panels name are colored:
This diagram shows the hierarchy of VGUI panels in Counter-Strike:


{|
[[File:Vgui hierarchy.png|center|link=|VGUI client panel hierarchy]]
| '''white''' || Panel is visible
|-
| '''grey''' || Panel is hidden
|-
| '''yellow''' || Popup panel (Frame)
|-
| '''green''' || Panel has focus
|}


You can inspect your mod's panel hierarchy with the '''VGUI Hierarchy tool'''. Open it by submitting <code>vgui_drawtree 1</code> to the [[developer console]].


The following options are available:
[[File:Vgui drawtree.png|right|VGUI Hierarchy tool]]


{|
Panel names are color-coded:
| '''Show Visible''' || list all visible panels
|-
| '''Show Hidden''' || list all hidden panels
|-
| '''Popups only''' || list only Popup panels (Frames)
|-
| '''Highlight Mouse Over''' || Panels are highlighted with a colored border when you move the mouse cursor over. The panel tree will be expanded to show the current panel.
|-
| '''Freeze''' || Locks current tree view
|-
| '''Show Addresses''' || shows panel memory address
|-
| '''Show Alpha''' || show panel alpha value, 0 = translucent, 255 = opaque
|-
| '''In Render Order''' || sort panels in tree by rendering order
|}


<br style="clear:both">
* <span style="padding:0 .4em;background:#23221D;color:white;">Visible</span>
* <span style="padding:0 .4em;background:#23221D;color:grey;">Hidden</span>
* <span style="padding:0 .4em;background:#23221D;color:yellow;">Popup panel (Frame)</span>
* <span style="padding:0 .4em;background:#23221D;color:limegreen;">Has focus</span>


==The Panel Class==
And the following options are available as checkboxes:


The VGUI class Panel is the base class for over 50 different control elements, which are defined in \public\vgui_controls\. The header files usually provide a good documentation about the control features and behaviors. The following diagram shows the class hierarchy for some common used elements:
; Show Visible
: List all visible panels
; Show Hidden
: List all hidden panels
; Popups only
: List only Popup panels (Frames)
; Highlight Mouse Over
: Panels are highlighted with a colored border when you move the mouse cursor over. The panel tree will be expanded to show the current panel.
; Freeze
: Locks current tree view
; Show Addresses
: Shows panel memory address
; Show Alpha
: Show panel alpha value, 0 = translucent, 255 = opaque
; In Render Order
: Sort panels in tree by rendering order


[[Image:Vgui2_2.gif|center]]
== The Panel class==


The VGUI base class <code>Panel</code> is derived from by over 50 different control elements defined in {{Path|\public\vgui_controls\}}. The header files usually provide a good documentation about the control features and behaviors. The following diagram shows the class hierarchy for some common used elements:


<center>
{|class="standard-table"
|-
!colspan=5|Panel
|-
|EditablePanel
|Label
|TextEntry
|rowspan=3|RichText
|rowspan=3|ImagePanel
|-
|Frame
|Button
|rowspan=2|ComboBox
|-
|MessageBox
|ToggleButton
|}
</center>
Since all VGUI classes derive from class Panel, we should take a closer look at the essential member functions and properties.
Since all VGUI classes derive from class Panel, we should take a closer look at the essential member functions and properties.


The most commonly used constructor to create a panel is <code>Panel(Panel *parent, const char *panelName)</code>, since each panel needs a parent panel it belongs to and a unique name in its hierarchy level. Once a panel is created you can make it visible or hide it again with <code>SetVisible(bool state)</code>.
The most commonly used constructor to create a panel is <code>Panel(Panel* parent, const char* panelName)</code>, since each panel needs a parent panel it belongs to and a unique name in its hierarchy level. Once a panel is created you can make it visible or hide it again with <code>SetVisible(bool state)</code>.


VGUI keeps a handle for each new panel, which is used to globally address a panel without passing pointers. Lots of event messaging functions use these <code>VPANEL</code> handles to identify source and target panels. To get the unique <code>VPANEL</code> handle of a panel call <code>GetVPanel()</code>.
VGUI keeps a handle for each new panel, which is used to globally address a panel without passing pointers. Lots of event messaging functions use these <code>VPANEL</code> handles to identify source and target panels. To get the unique <code>VPANEL</code> handle of a panel call <code>GetVPanel()</code>.
Line 64: Line 83:
To set a panel's position and size use the <code>SetPos(int x,int y)</code> and <code>SetSize(int wide,int tall)</code> member functions. The size is always in pixels and doesn't scale automatically with increasing screen resolutions. The position is always relative to the given parent panel, where (0,0) is the upper left corner.
To set a panel's position and size use the <code>SetPos(int x,int y)</code> and <code>SetSize(int wide,int tall)</code> member functions. The size is always in pixels and doesn't scale automatically with increasing screen resolutions. The position is always relative to the given parent panel, where (0,0) is the upper left corner.


To iterate through the panel hierarchy using a certain panel as starting point, you get the parent panel by calling <code>GetParent()</code> or <code>GetVParent()</code> to get the corresponding <code>VPANEL</code> handle. Of course a panel can have only one parent panel, but multiple child panels (sub panels, buttons, list elements etc). <code>GetChildCount()</code> will return the total number of children, were each child can be queried with <code>GetChild(int index)</code>.
To iterate through the panel hierarchy using a certain panel as starting point, you get the parent panel by calling <code>GetParent()</code> or <code>GetVParent()</code> to get the corresponding <code>VPANEL</code> handle. Of course a panel can have only one parent panel, but multiple child panels (sub panels, buttons, list elements etc). <code>GetChildCount()</code> will return the total number of children, where each child can be queried with <code>GetChild(int index)</code>.


If a panel should react to input events like key pressed or mouse click, the virtual <code>OnEvent()</code> functions like <code>OnMousePressed(MouseCode code)</code> or <code>OnKeyCodePressed(KeyCode code)</code> must be overwritten. Mouse and keyboard input events can be enabled/disabled for a panel using <code>SetMouseInputEnabled(bool state)</code> or <code>SetKeyBoardInputEnabled(bool state)</code>.
If a panel should react to input events like key pressed or mouse click, the virtual <code>OnEvent()</code> functions like <code>OnMousePressed(MouseCode code)</code> or <code>OnKeyCodePressed(KeyCode code)</code> must be overridden. Mouse and keyboard input events can be enabled/disabled for a panel using <code>SetMouseInputEnabled(bool state)</code> or <code>SetKeyBoardInputEnabled(bool state)</code>.


Another useful virtual function to overwrite is <code>OnThink()</code> which is called every time the panel is redrawn. Sometimes this is too much and you want update or check the panel state less often. Then you can hook into <code>OnTick()</code> which is called frequently in an adjustable time interval. But VGUI must be told that it should call <code>OnTick()</code> for a given panel and how often, this is done with <code>ivgui()->AddTickSignal(VPANEL panel, int intervalMilliseconds)</code>.
Another useful virtual function to override is <code>OnThink()</code> which is called every time the panel is redrawn. Sometimes this is too much and you want update or check the panel state less often. Then you can hook into <code>OnTick()</code> which is called frequently in an adjustable time interval. But VGUI must be told that it should call <code>OnTick()</code> for a given panel and how often, this is done with <code>ivgui()->AddTickSignal(VPANEL panel, int intervalMilliseconds)</code>.


==Windows & Popups==
== Windows & popups ==


A Panel as described above is always a 100% sub area of its parent panel, its position is always a fixed relative offset and can only be changed by code. Also it draws itself only inside the parent region, if you move a VGUI element outside the parent panel, it will be clipped. This is fine for adding control elements, images or text fields to a panel, but wouldn't allow independent pop-up windows or dialog boxes the user can move around, resize and minimize. Therefore VGUI panels have the <code>MakePopup()</code> function which decouples a panel from is parent rendering and makes it a new, independent window. Still it belongs to a parent panel and becomes invisible if the parent does.
A <code>Panel</code> as described above is always a 100% sub area of its parent panel, its position is always a fixed relative offset and can only be changed by code. Also it draws itself only inside the parent region, if you move a VGUI element outside the parent panel, it will be clipped. This is fine for adding control elements, images or text fields to a panel, but wouldn't allow independent pop-up windows or dialog boxes the user can move around, resize and minimize. Therefore, VGUI panels have the <code>MakePopup()</code> function which decouples a panel from is parent rendering and makes it a new, independent window. Still it belongs to a parent panel and becomes invisible if the parent does.


In most cases you won't call <code>MakePopup()</code> by yourself but use the Frame class instead. The Frame class encapsulates all common GUI windows features like title bar, system menu (close, minimize, maximize), dragging, resizing, focus and layout management.
In most cases you won't call <code>MakePopup()</code> by yourself but use the <code>Frame</code> class instead. The <code>Frame</code> class encapsulates all common GUI windows features like title bar, system menu (close, minimize, maximize), dragging, resizing, focus and layout management.


Here a short example how to open a frame and activate it. Note that the frame will delete itself once the user closes the frame or the <code>Close()</code> function is called (just make sure that your stuff is cleaned up in the destructor).
Here a short example how to open a frame and activate it. Note that the frame will delete itself once the user closes the <code>Frame</code> or the <code>Close()</code> function is called (just make sure that your stuff is cleaned up in the destructor).


<pre>
<source lang=cpp>
Frame *pFrame = new Frame( g_pClientMode->GetViewport(), "MyFrame" );
Frame* pFrame = new Frame( g_pClientMode->GetViewport(), "MyFrame" );
pFrame->SetScheme("ClientScheme.res");
pFrame->SetScheme("ClientScheme.res");
pFrame->SetSize( 100, 100 );
pFrame->SetSize( 100, 100 );
pFrame->SetTitle("My First Frame", true );
pFrame->SetTitle("My First Frame", true );
pFrame->Activate(); // set visible, move to front, request focus
pFrame->Activate(); // set visible, move to front, request focus
</pre>
</source>


If a Frame is resized, the virtual function <code>PerformLayout()</code> is called so the frame can rearrange its elements to fit best for the new size. The current Frame position and size can also be locked with <code>SetMoveable(bool state)</code> and <code>SetSizeable(bool state)</code>.
If a Frame is resized, the virtual function <code>PerformLayout()</code> is called so the frame can rearrange its elements to fit best for the new size. The current Frame position and size can also be locked with <code>SetMoveable(bool state)</code> and <code>SetSizeable(bool state)</code>.


==Event Messaging==
== Event messaging ==


VGUI panels communicate via a message system to signal state changes or events to parents or children (or any other panel). Messages are not sent directly (e.g. by calling a panel listener function), rather they are handed over to VGUI which delivers them to the target panel. Thus beside the message content, a sender and a receiver panel must be specified as VPANEL handles. VGUI sends event messages to inform panels about changes or events (mouse moved, input focus changed etc).
VGUI panels communicate via a message system to signal state changes or events to parents or children (or any other panel). Messages are not sent directly (e.g. by calling a panel listener function), rather they are handed over to VGUI which delivers them to the target panel. Thus beside the message content, a sender and a receiver panel must be specified as VPANEL handles. VGUI sends event messages to inform panels about changes or events (mouse moved, input focus changed etc).


The message name and content is specified using a KeyValues object (\public\KeyValues.h). The KeyValues class is a very generic and flexible structure to store data records containing strings, integer or float numbers. A KeyValues object has a name and a set of data entries. Each entry has a unique key name and corresponding value. KeyValues also allow creating hierarchical structures using sub keys, though most VGUI messages are flat data records. KeyValues don't have data/type definitions or similar, so you can add or remove entries of any type as you like. Thus sender and receiver must both know the internal structure (e.g. key names, types and their meaning) of a KeyValues message object to communicate successfully. Here some sample code how to use KeyValues in general:
The message name and content is specified using a [[KeyValues]] object. The KeyValues class is a very generic and flexible structure to store data records containing strings, integer or float numbers. A KeyValues object has a name and a set of data entries. Each entry has a unique key name and corresponding value. KeyValues also allow creating hierarchical structures using sub keys, though most VGUI messages are flat data records. KeyValues don't have data/type definitions or similar, so you can add or remove entries of any type as you like. Thus sender and receiver must both know the internal structure (e.g. key names, types and their meaning) of a KeyValues message object to communicate successfully. Here some sample code how to use KeyValues in general:


<pre>
<source lang=cpp>
// create a new KeyValues Object named "MyName"
// create a new KeyValues Object named "MyName"
KeyValues *data = new KeyValues("MyName");  
KeyValues* data = new KeyValues("MyName");  


// add data entries
// add data entries
data->SetInt( "aInteger", 42 );
data->SetInt( "anInteger", 42 );
data->SetString( "aString", "Marvin" );
data->SetString( "aString", "Marvin" );


// read data entries
// read data entries
int x = data->GetInt("aInteger");
int x = data->GetInt("anInteger");
Con_Printf( data->GetString("aString") );
Con_Printf( data->GetString("aString") );


// destroy KeyValues object again, free data records
// destroy KeyValues object again, free data records
data->deleteThis();
data->deleteThis();
</pre>
</source>


To send a message you can call the Panel member function <code>PostMessage()</code> or directly <code>ivgui()-> PostMessage()</code>. The name of the KeyValues object is also the message name used for later dispatching. VGUI will call the target panel's <code>OnMessage()</code> function, which will dispatch the message to a previous defined message handler. A panel can register new message handlers with one of the <code>MESSAGE_FUNC_*</code> macros, which adds a handler function to the message map. Never overwrite <code>OnMessage()</code> to handle new messages, always use a macro.
To send a message you can call the Panel member function <code>PostMessage(...)</code> or directly <code>ivgui()-> PostMessage(...)</code>. The name of the KeyValues object is also the message name used for later dispatching. VGUI will call the target panel's <code>OnMessage(...)</code> function, which will dispatch the message to a previous defined message handler. A panel can register new message handlers with one of the <code>MESSAGE_FUNC_*</code> macros, which adds a handler function to the message map. Never override <code>OnMessage(...)</code> to handle new messages, always use a macro. Please note that when you use the macro MESSAGE_FUNC_* only those specific messages will be thrown to your function. If you want to catch the newLine (ENTER) message from a txtEntry element you will use <code>MESSAGE_FUNC_PARAMS( NewLineMessage, "TextNewLine",data)</code>. <code>NewLineMessage</code> is function to call, "TextNewLine" means all messages who's names are "TextNewLine" will be redirected and finally data is the input that will be passed to your function. In this case <code>NewLineMessage</code> will look like this void <code>MyParentPanel::NewLineMessage</code> (KeyValues* data).


First declare a message handler for the receiving panel:
<source lang=cpp>
 
<pre>
class MyParentPanel  : public vgui::Panel
class MyParentPanel  : public vgui::Panel
{
{
Line 122: Line 139:
}
}


void MyParentPanel::OnMyMessage (KeyValues *data)
void MyParentPanel::OnMyMessage (KeyValues* data)
{
{
const char *text = data->GetString("text");
const char* text = data->GetString("text");
}
}
</pre>
</source>


The sending panel creates a KeyValues object, adds message parameters and sends the message (to its parent in this case). The KeyValues object is destroyed by VGUI after it has been processed.
The sending panel creates a KeyValues object, adds message parameters and sends the message (to its parent in this case). The KeyValues object is destroyed by VGUI after it has been processed.


<pre>
<source lang=cpp>
void MyChildPanel::SomethingHappend()
void MyChildPanel::SomethingHappened()
{
{
if ( GetVParent() )
if ( GetVParent() )
{
{
KeyValues *msg = new KeyValues("MyMessage");
KeyValues* msg = new KeyValues("MyMessage");
msg->SetString("text", "Something happend");
msg->SetString("text", "Something happened");
PostMessage(GetVParent(), msg);
PostMessage(GetVParent(), msg);
}
}
}
}
</pre>
</source>


Using <code>PostMessage()</code> the sending panel must address a single, specific target, which means that all other panels interested in a state change must be known and addressed individually. To avoid hard coding these dependencies, panels have a public event system called action signals. A panel fires generic events with <code>PostActionSignal(KeyValues *message)</code> and interested panels can register as listeners for these signals with <code>AddActionSignalTarget(Panel *target)</code>. These action signals are widely used by VGUI controls, for example messages like <code>"TextChanged"</code> fired by class TextEntry or <code>"ItemSelected"</code> used by class ListPanel. All action signal messages contain a pointer entry "panel" that points to the sending panel.
Using <code>PostMessage()</code> the sending panel must address a single, specific target, which means that all other panels interested in a state change must be known and addressed individually. To avoid hard coding these dependencies, panels have a public event system called action signals. A panel fires generic events with <code>PostActionSignal(KeyValues* message)</code> and interested panels can register as listeners for these signals with <code>AddActionSignalTarget(Panel* target)</code>. These action signals are widely used by VGUI controls, for example messages like <code>"TextChanged"</code> fired by class TextEntry or <code>"ItemSelected"</code> used by class <code>ListPanel</code>. All action signal messages contain a pointer entry "panel" that points to the sending panel.


The example from above using action signals would need the parent panel to register as a listener, preferably in the constructor after the child panel has been created. The child panel just uses <code>PostActionSignal()</code> instead of <code>PostMessage()</code>:
The example from above using action signals would need the parent panel to register as a listener, preferably in the constructor after the child panel has been created. The child panel just uses <code>PostActionSignal()</code> instead of <code>PostMessage()</code>:


<pre>
<source lang=cpp>
MyParentPanel::MyParentPanel()
MyParentPanel::MyParentPanel()
{
{
Line 153: Line 170:
}
}


void MyParentPanel::OnMyMessage (KeyValues *data)
void MyParentPanel::OnMyMessage (KeyValues* data)
{
{
const char *text = data->GetString("text");
const char* text = data->GetString("text");
Panel *pSender = (Panel *) kv->GetPtr("panel", NULL);
Panel* pSender = (Panel* ) data->GetPtr("panel", NULL);
}
}


void MyChildPanel::SomethingHappend()
void MyChildPanel::SomethingHappened()
{
{
KeyValues *msg = new KeyValues("MyMessage");
KeyValues* msg = new KeyValues("MyMessage");
msg->SetString("text", "Something happend");
msg->SetString("text", "Something happened");
PostActionSignal ( msg );
PostActionSignal ( msg );
}
}
</pre>
</source>
 
To catch the return/enter key from a <code>txtEntry</code> element use this.
 
<source lang=cpp>
class MyParentPanel  : public vgui::Panel
{
...
private:
MESSAGE_FUNC_PARAMS( OnMyMessage, "NewLineMessage", data );
}
MyParentPanel::MyParentPanel()
{
...
m_pChildPanel = new vgui::TextEntry( this, "txtEntry" );
m_pChildPanel->AddActionSignalTarget( this );
m_pChildPanel->SendNewLine(true); // with the txtEntry Type you need to set it to pass the return key as a message
}


A commonly used action signal is the "Command" message, since no message handler needs to be installed. Panels just need to derive the virtual Panel function <code>OnCommand(const char *command)</code> and check for the correct command string. The "Command" message is used by all Button classes and is fired whenever the button is pressed. Here the example from above using the Command message:
void MyParentPanel::NewLineMessage (KeyValues* data)
{
// when the txtEntry box posts an action signal it only sets the name
// so it is wise to check whether that specific txtEntry is indeed in focus
// Is the Text Entry box in focus?
if (m_pChildPanel->HasFocus())
{
// We have caught the message, now we can handle it. I would simply repost it to the OnCommand function  
// Post the message to our command handler
this->OnCommand("Submit");
}
}
</source>


<pre>
A commonly used action signal is the "Command" message, since no message handler needs to be installed. Panels just need to derive the virtual Panel function <code>OnCommand(const char* command)</code> and check for the correct command string. The "Command" message is used by all Button classes and is fired whenever the button is pressed. Here is the example from above using the Command message:
 
<source lang=cpp>
class MyParentPanel  : public vgui::Panel
class MyParentPanel  : public vgui::Panel
{
{
...
...
protected:
protected:
virtual void OnCommand(const char *command);
virtual void OnCommand(const char* command);
}
}


Line 183: Line 231:
}
}


 
void MyParentPanel::OnCommand(const char* command)
void MyParentPanel::OnCommand(const char *command)
{
{
if (!stricmp(command, "SomethingHappend "))
if (!stricmp(command, "SomethingHappened"))
{
{
DoSomething ();
DoSomething ();
Line 196: Line 243:
}
}


void MyChildPanel::SomethingHappend()
void MyChildPanel::SomethingHappened()
{
{
KeyValues *msg = new KeyValues("Command");
KeyValues* msg = new KeyValues("Command");
msg->SetString("command", "SomethingHappend");
msg->SetString("command", "SomethingHappened");
PostActionSignal ( msg );
PostActionSignal ( msg );
}
}
</pre>
</source>


==Schemes==
== Schemes ==


A VGUI scheme defines the general "look & feel" of your panels by specifying used colors, fonts & icons of control elements. Schemes are defined in resource files, for example <code>hl2\resource\clientscheme.res</code>. A new panel inherits by default the scheme settings used by its parent. The VGUI scheme manager can load a new scheme with <code>LoadSchemeFromFile(char *fileName, char *tag)</code> which returns <code>HScheme</code> handle. To make a panel using a loaded VGUI scheme call the panel member function <code>SetScheme(HScheme scheme)</code>.
A VGUI scheme defines the general "look & feel" of your panels by specifying used colors, fonts & icons of control elements. Schemes are defined in resource files, for example {{Path|hl2\resource\clientscheme|res|icon=file}}. A new panel inherits by default the scheme settings used by its parent. It's also possible to preview the fonts, borders and colors of a scheme by using the <code>showschemevisualizer</code> command, with an argument matching the name of your scheme, in the console. The VGUI scheme manager can load a new scheme with <code>LoadSchemeFromFile(char* fileName, char* tag)</code> which returns <code>HScheme</code> handle. To make a panel using a loaded VGUI scheme call the panel member function <code>SetScheme(HScheme scheme)</code>.


Schemes set the general look of panel elements, but they don't arrange specific control elements of your panel. One way of adding elements to your panel is by doing that in source code. You can create them (usually in the constructor of the parent panel) and set properties like size and position manually using the panel member functions. This will become quite complex and time-consuming for larger dialogs with lots of control elements.
Schemes set the general look of panel elements, but they don't arrange specific control elements of your panel. One way of adding elements to your panel is by doing that in source code. You can create them (usually in the constructor of the parent panel) and set properties like size and position manually using the panel member functions. This will become quite complex and time-consuming for larger dialogs with lots of control elements.


The most common way to define the layout of your panel elements is to describe all elements in an external resource file like <code>hl2\resource\UI\classmenu.res</code> (a KeyValues text file). When the parent panel is created, this file is loaded and executed with <code>LoadControlSettings(char *dialogResourceName)</code>. Do note, however, that this function is only defined for EditablePanel (or an EditablePanel derivative such as Frame). In this resource file each control element is defined in a separate section. A typical control definition looks like this:
The most common way to define the layout of your panel elements is to describe all elements in an external resource file like {{Path|hl2\resource\UI\classmenu|res|icon=file}} (a KeyValues text file). When the parent panel is created, this file is loaded and executed with <code>LoadControlSettings(char* dialogResourceName)</code>. Do note, however, that this function is only defined for EditablePanel (or an <code>EditablePanel</code> derivative such as <code>Frame</code>). In this resource file each control element is defined in a separate section. A typical control definition looks like this:


<pre>
<source lang=cpp>
"MyControlName"
MyControlName
{
{
"ControlName" "Label" // control class  
ControlName Label // control class  
"fieldName" "MyControlName" // name of the control element
fieldName MyControlName // name of the control element
"xpos" "8" // x position
xpos 8 // x position
"ypos" "72" // y position
ypos 72 // y position
"wide" "160" // width in pixels
wide 160 // width in pixels
"tall" "24" // height in pixels
tall 24 // height in pixels
"visible" "1" // it's visible
visible 1 // it's visible...
"enabled" "1" // and enabled
enabled 1 // ...and enabled
"labelText" "Hello world" // show this text
labelText "Hello world" // display this unlocalized text
"textAlignment" "west" // right text alignment
textAlignment west // left text alignment
}
}
</pre>
</source>


Each control property has a key name and a value. Properties defined by the base class Panel are available for all controls (like xpos, ypos, wide, tall etc). A list of all available key names is returned by the panel member function <code>GetDescription()</code>. Derived panel classes may add new fields for their specific properties. Processing these new fields must be handled by overwriting the virtual function <code>ApplySettings(KeyValues *inResourceData)</code>. Here you can also lookup how values for an existing control property are interpreted.
Each control property has a key name and a value. Properties defined by the base class Panel are available for all controls (like <code>xpos, ypos, wide, tall</code> etc). A list of all available key names is returned by the panel member function <code>GetDescription()</code>. Derived panel classes may add new fields for their specific properties. Processing these new fields must be handled by overriding the virtual function <code>ApplySettings(KeyValues* inResourceData)</code>. Here you can also lookup how values for an existing control property are interpreted.


==Build Mode==
== Build mode ==


[[Image:Vgui2_4.jpg|frame|left|VGUI Build Mode panel]]
[[File:Vgui2_4.jpg|left|VGUI Build Mode panel]]


An even simpler way to edit panel and control layouts is the VGUI '''Build Mode'''. This allows you to modify and save the layout of a panel resource file while the application is running. To edit a panel, just start the game and open this panel, so it gets the input focus. Then press <code>SHIFT+CTRL+ALT+B</code> to open the VGUI Build Mode editor. In Build Mode you can easily rearrange existing elements and change their control properties (press 'Apply' to update changes). To add a new control element, just choose the desired class from the combo-box on the lower right side and an empty control object of that class will be place in your panel for further editing. To save the current layout in the associated resource file press the 'Save' button (make sure the resource file is not write-protected). <br style="clear:both">
An even simpler way to edit panel and control layouts is the VGUI Build Mode. This allows you to modify and save the layout of a panel resource file while the application is running. To edit a panel, just start the game and open this panel, so it gets the input focus. Then press {{key|Shift+Ctrl+Alt+B}} to open the VGUI Build Mode editor. In Build Mode you can easily rearrange existing elements and change their control properties (press 'Apply' to update changes). To add a new control element, just choose the desired class from the combo-box on the lower right side and an empty control object of that class will be place in your panel for further editing. To save the current layout in the associated resource file press the 'Save' button (make sure the resource file is not write-protected). {{clr}}


==Drawing & Surfaces==
== Drawing & surfaces ==


Using schemes and control properties you can change the general appearance and layout of existing controls, but it doesn't allow you to create completely new elements. To change the appearance of a panel you have to override the two virtual functions <code>Panel::Paint()</code> and <code>Panel::PaintBackground()</code>. In these functions you can use drawing functions provided by the ISurface interface to place lines, rectangles, text, images etc. When updating the screen, VGUI calls first <code>PaintBackground()</code> and then <code>Paint()</code> for each panel and its children panels recursively. Drawing coordinates are relative to the panel you draw in. Here same example code how to draw a red box in the upper left corner:
Using schemes and control properties you can change the general appearance and layout of existing controls, but it doesn't allow you to create completely new elements. To change the appearance of a panel you have to override the two virtual functions <code>Panel::Paint()</code> and <code>Panel::PaintBackground()</code>. In these functions you can use drawing functions provided by the ISurface interface to place lines, rectangles, text, images etc. When updating the screen, VGUI calls first <code>PaintBackground()</code> and then <code>Paint()</code> for each panel and its children panels recursively. Drawing coordinates are relative to the panel you draw in. Here same example code how to draw a red box in the upper left corner:


<pre>
<source lang=cpp>
void MyPanel::Paint(void)
void MyPanel::Paint(void)
{
{
Line 247: Line 294:
surface()->DrawFilledRect( 0, 0, 20, 20 ); //x0,y0,x1,y1
surface()->DrawFilledRect( 0, 0, 20, 20 ); //x0,y0,x1,y1
}
}
</pre>
</source>


To draw text on a panel surface you need to set the used font first. Fonts have names like "DefaultSmall" and their properties like True-Type-Font, size and weight are defined in scheme resource files. The font handle can be retrieved by calling <code>GetFont("Name")</code> of the current panel scheme, then the surface can be told to use it as the current font. After setting the color and position for the next text output, the text itself must be passed as wide char string (Unicode) to <code>DrawPrintText()</code>. This text will not be printed as it is and not localized anymore, so localization tokens must be translated beforehand.
To draw text on a panel surface you need to set the used font first. Fonts have names like "DefaultSmall" and their properties like True-Type-Font, size and weight are defined in scheme resource files. The font handle can be retrieved by calling <code>GetFont("Name")</code> of the current panel scheme, then the surface can be told to use it as the current font. After setting the color and position for the next text output, the text itself must be passed as wide char string (Unicode) to <code>DrawPrintText(...)</code>. This text will not be printed as it is and not localized anymore, so localization tokens must be translated beforehand.


<pre>
<source lang=cpp>
void MyPanel::Paint(void)
void MyPanel::Paint(void)
{
{
wchar_t *pText = L"Hello world!"; // wide char text
wchar_t* pText = L"Hello world!"; // wide char text


// get the right font handle for this scheme
// get the right font handle for this scheme
vgui::IScheme *pScheme = vgui::scheme()->GetIScheme(GetScheme());
vgui::IScheme* pScheme = vgui::scheme()->GetIScheme(GetScheme());
vgui::HFont hFont = pScheme->GetFont( "DefaultSmall" );
vgui::HFont hFont = pScheme->GetFont( "DefaultSmall" );


Line 265: Line 312:
surface()->DrawPrintText( pText, wcslen(pText) ); // print text
surface()->DrawPrintText( pText, wcslen(pText) ); // print text
}
}
</pre>
</source>


To draw a texture or image, VGUI has to load the texture from disk once (panel constructor) and generate a corresponding texture ID. This texture ID is then used as reference when drawing the texture. Here an example how to load and draw the texture <code>"\materials\your_folder\mylogo.vmt"</code>:
To draw a [[texture]] or image, VGUI has to load the texture from disk once (panel constructor) and generate a corresponding texture ID. This texture ID is then used as reference when drawing the texture. Here an example how to load and draw the texture {{Path|\materials\your_folder\mylogo|vmt|icon=file}}:


<pre>
<source lang=cpp>
MyPanel::MyPanel(void)
MyPanel::MyPanel(void)
{
{
//Do _not_ forget CreateNewTextureID(), it gave me a headache for a week
//Do _not_ forget CreateNewTextureID(), it gave me a headache for a week
m_nTextureID = vgui::surface()->CreateNewTextureID();
m_nTextureID = vgui::surface()->CreateNewTextureID();
vgui::surface()->DrawSetTextureFile( m_nTextureID, "materials/your_folder/mylogo" , true, false);
vgui::surface()->DrawSetTextureFile( m_nTextureID, "vgui/your_folder/mylogo" , true, false);
}
}


Line 280: Line 327:
{
{
vgui::surface()->DrawSetTexture( m_nTextureID );
vgui::surface()->DrawSetTexture( m_nTextureID );
        vgui::surface()->DrawSetColor(50,50,50,100);
vgui::surface()->DrawTexturedRect( 0, 0, 100, 100 );
vgui::surface()->DrawTexturedRect( 0, 0, 100, 100 );
}
}
</pre>
</source>
 
== Proportionality ==


==Localization==
Coordinate values that you specify in configuration files are relative to a 640x480 resolution if <code>SetProportional(true)</code> is called before a file is loaded (which, by default, it is). These values will be scaled to the correct size no matter what the user's current resolution may be. For example, a "tall" value of 120 will always mean the panel takes up 1/4th the height of the screen, and an "x" value of 320 means that the panel's left edge will always be centered.


VGUI text elements support automatic localization for the preferred user language. Lets assume a text label should show "Hello world!" then you could set this text directly with <code>SetText( "Hello World.")</code>. But if the user would choose a different language than English this label would still show the English text. Therefore you should always use localization tokens to tell VGUI to translate the token into user language, in this example <code>SetText( "#MyMod_HelloWorld")</code>. Tokens are strings starting with the pound sign '#' as control character to tell VGUI that this is not plain text.
{{tip|Use <code>'''c'''-<wide/tall></code> in your <code>xpos</code> and <code>ypos</code> values to '''c'''enter an element on-screen at different aspect ratios.}}


VGUI keeps a global translation table to map tokens to plain text representation. These translation tables are loaded from resource files, were each file has an extra copy for every supported language (e.g. <code>\resource\hl2_english.txt, \resource\hl2_german.txt</code>). A new token definition for your game would look like this:
However, sometimes you will need to calculate coordinates for custom painting and forgo the luxury of configuration files. The problem is that values that you specify in code are relative to the user's native resolution, which may or may ''not'' be the same as the current game resolution (thus rendering <code>surface()->GetScreenSize()</code> useless). The solution to this problem is to use <code>scheme()->GetProportionalScaledValue()</code>. This allows you to specify a coordinate on the 640x480 resolution and have it be remapped to the current resolution.
 
== Localization ==
 
VGUI text elements support automatic localization for the preferred user language. Lets assume a text label should show "Hello world!" then you could set this text directly with <code>SetText( "Hello World.")</code>. But if the user would choose a different language than English this label would still show the English text. Therefore you should always use localization tokens to tell VGUI to translate the token into user language, in this example <code>SetText( "#MyMod_HelloWorld")</code>. Tokens are strings starting with the hash sign '#' as control character to tell VGUI that this is not plain text.
 
VGUI keeps a global translation table to map tokens to plain text representation. These translation tables are loaded from resource files, where each file has an extra copy for every supported language (e.g. {{Path|\resource\hl2_english|txt|icon=file}}, {{Path|\resource\hl2_german|txt|icon=file}}).
 
{{tip|Localization files must be stored in the UCS2-Little Endian format. You can edit the localization using {{xblahmt|4}}, using {{vguiloct|4}}, or else you may need an advanced text editor like Notepad++.}}
 
A new token definition for your game would look like this:


In mymod_english.txt:
In mymod_english.txt:


<pre>
"MyMod_HelloWorld" "Hello world."
"MyMod_HelloWorld" "Hello world."
</pre>


In mymod_german.txt:
In mymod_german.txt:


<pre>
"MyMod_HelloWorld" "Hallo Welt."
"MyMod_HelloWorld" "Hallo Welt."
"[english]MyMod_HelloWorld" "Hello world."
"[english]MyMod_HelloWorld" "Hello world."
</pre>


If your game folder is named "mymod" the Source engine will automatically load the correct translation file (<code>/resource/gamedir_language.txt</code>). You can load additional translation files using the ILocalize interface function <code>AddFile()</code>.
If your game folder is named "mymod" the Source engine will automatically load the correct translation file {{Path|/resource/gamedir_language|txt|icon=file}}. You can load additional translation files using the ILocalize interface function <code>AddFile(...)</code>.


You can use the <code>ILocalize</code> interface also to manually translate a token into current user language, e.g. <code>vgui::localize()->Find("#MyMod_HelloWorld")</code>. This function returns the translation as a 16-bit wide char string (Unicode).
You can use the <code>ILocalize</code> interface also to manually translate a token into current user language, e.g. <code>g_pVGuiLocalize->Find("#MyMod_HelloWorld")</code> (or <code>vgui::localize()->Find("#MyMod_HelloWorld")</code> in previous versions). This function returns the translation as a 16-bit wide char string (Unicode).


VGUI uses Unicode for all text representation to support languages that use more then the 255 ASCII characters like Chinese or Russian. Language resource files are encoded as Unicode. To convert strings between ANSI ASCII and Unicode during runtime you can used the ILocalize interface functions <code>ConvertANSIToUnicode()</code> and <code>ConvertUnicodeToANSI()</code>. Also a very useful function is <code>ConstructString()</code> which is basically like sprintf() for Unicode strings.
VGUI uses Unicode for all text representation to support languages that use more then the 255 ASCII characters, like Chinese or Russian. Language resource files are encoded as Unicode. Note that the files will not be loaded properly unless they are specifically saved with proper Unicode encoding.  To convert strings between ANSI ASCII and Unicode during runtime you can used the <code>ILocalize</code> interface functions <code>ConvertANSIToUnicode(...)</code> and <code>ConvertUnicodeToANSI(...)</code>. Also a very useful function is <code>ConstructString(...)</code> which is basically like sprintf(...) for Unicode strings.


==See Also==
== See also ==
* [[HUD Elements]]


[[HUD Elements]]
== Tutorials ==
* [[VGUI2: Creating a panel|Creating a Panel]]
* [[Adding Your Logo to the Menu]]
* [[VGUI Screen Creation|Creating a VGUI Screen]]
 
[[Category:Programming]]
[[Category:VGUI]]

Latest revision as of 23:08, 21 June 2025

English (en)Español (es)Polski (pl)Русский (ru)中文 (zh)Translate (Translate)


VGUI is Valve's proprietary Graphical User Interface. All or most Source Source games, and formerly Steam Steam use VGUI to draw windows, dialogs and menus. It also handles localization ↓: the displaying of text in the user's preferred language.

  • The object architecture is hierarchical and all implemented elements derive from VGUI base classes.
  • The keyboard/mouse input system is event-driven and quite similar to other GUI systems.
  • Implementations for the most common GUI elements like buttons, text fields or images are provided by the VGUI controls library Filevgui_controls.lib.
  • Base interface headers are located in 🖿\public\vgui\.
  • Control elements are defined in 🖿\public\vgui_controls\.

As a mod author you will most likely use VGUI in the client.dll project to show menus, HUD elements or for in-game displays (on weapons or computer terminals etc).

Panels and hierarchy

The base class all VGUI elements derive from is vgui::Panel, which defines an area on your screen that has a specific size and position, can draw itself and process input events. Dialog windows, text fields and buttons are all VGUI panels in a hierarchical parent-child relationship. The very first panel is the root panel and is provided by the Source engine. The client root panel covers the whole screen, but doesn't show anything. Even though you could use the client root panel, most client panels use the shared BaseViewport panel as parent ( g_pClientMode->GetViewport() ).

This diagram shows the hierarchy of VGUI panels in Counter-Strike:

VGUI client panel hierarchy

You can inspect your mod's panel hierarchy with the VGUI Hierarchy tool. Open it by submitting vgui_drawtree 1 to the developer console.

VGUI Hierarchy tool

Panel names are color-coded:

  • Visible
  • Hidden
  • Popup panel (Frame)
  • Has focus

And the following options are available as checkboxes:

Show Visible
List all visible panels
Show Hidden
List all hidden panels
Popups only
List only Popup panels (Frames)
Highlight Mouse Over
Panels are highlighted with a colored border when you move the mouse cursor over. The panel tree will be expanded to show the current panel.
Freeze
Locks current tree view
Show Addresses
Shows panel memory address
Show Alpha
Show panel alpha value, 0 = translucent, 255 = opaque
In Render Order
Sort panels in tree by rendering order

The Panel class

The VGUI base class Panel is derived from by over 50 different control elements defined in 🖿\public\vgui_controls\. The header files usually provide a good documentation about the control features and behaviors. The following diagram shows the class hierarchy for some common used elements:

Panel
EditablePanel Label TextEntry RichText ImagePanel
Frame Button ComboBox
MessageBox ToggleButton

Since all VGUI classes derive from class Panel, we should take a closer look at the essential member functions and properties.

The most commonly used constructor to create a panel is Panel(Panel* parent, const char* panelName), since each panel needs a parent panel it belongs to and a unique name in its hierarchy level. Once a panel is created you can make it visible or hide it again with SetVisible(bool state).

VGUI keeps a handle for each new panel, which is used to globally address a panel without passing pointers. Lots of event messaging functions use these VPANEL handles to identify source and target panels. To get the unique VPANEL handle of a panel call GetVPanel().

To set a panel's position and size use the SetPos(int x,int y) and SetSize(int wide,int tall) member functions. The size is always in pixels and doesn't scale automatically with increasing screen resolutions. The position is always relative to the given parent panel, where (0,0) is the upper left corner.

To iterate through the panel hierarchy using a certain panel as starting point, you get the parent panel by calling GetParent() or GetVParent() to get the corresponding VPANEL handle. Of course a panel can have only one parent panel, but multiple child panels (sub panels, buttons, list elements etc). GetChildCount() will return the total number of children, where each child can be queried with GetChild(int index).

If a panel should react to input events like key pressed or mouse click, the virtual OnEvent() functions like OnMousePressed(MouseCode code) or OnKeyCodePressed(KeyCode code) must be overridden. Mouse and keyboard input events can be enabled/disabled for a panel using SetMouseInputEnabled(bool state) or SetKeyBoardInputEnabled(bool state).

Another useful virtual function to override is OnThink() which is called every time the panel is redrawn. Sometimes this is too much and you want update or check the panel state less often. Then you can hook into OnTick() which is called frequently in an adjustable time interval. But VGUI must be told that it should call OnTick() for a given panel and how often, this is done with ivgui()->AddTickSignal(VPANEL panel, int intervalMilliseconds).

Windows & popups

A Panel as described above is always a 100% sub area of its parent panel, its position is always a fixed relative offset and can only be changed by code. Also it draws itself only inside the parent region, if you move a VGUI element outside the parent panel, it will be clipped. This is fine for adding control elements, images or text fields to a panel, but wouldn't allow independent pop-up windows or dialog boxes the user can move around, resize and minimize. Therefore, VGUI panels have the MakePopup() function which decouples a panel from is parent rendering and makes it a new, independent window. Still it belongs to a parent panel and becomes invisible if the parent does.

In most cases you won't call MakePopup() by yourself but use the Frame class instead. The Frame class encapsulates all common GUI windows features like title bar, system menu (close, minimize, maximize), dragging, resizing, focus and layout management.

Here a short example how to open a frame and activate it. Note that the frame will delete itself once the user closes the Frame or the Close() function is called (just make sure that your stuff is cleaned up in the destructor).

Frame* pFrame = new Frame( g_pClientMode->GetViewport(), "MyFrame" );
pFrame->SetScheme("ClientScheme.res");
pFrame->SetSize( 100, 100 );
pFrame->SetTitle("My First Frame", true );
pFrame->Activate();	// set visible, move to front, request focus

If a Frame is resized, the virtual function PerformLayout() is called so the frame can rearrange its elements to fit best for the new size. The current Frame position and size can also be locked with SetMoveable(bool state) and SetSizeable(bool state).

Event messaging

VGUI panels communicate via a message system to signal state changes or events to parents or children (or any other panel). Messages are not sent directly (e.g. by calling a panel listener function), rather they are handed over to VGUI which delivers them to the target panel. Thus beside the message content, a sender and a receiver panel must be specified as VPANEL handles. VGUI sends event messages to inform panels about changes or events (mouse moved, input focus changed etc).

The message name and content is specified using a KeyValues object. The KeyValues class is a very generic and flexible structure to store data records containing strings, integer or float numbers. A KeyValues object has a name and a set of data entries. Each entry has a unique key name and corresponding value. KeyValues also allow creating hierarchical structures using sub keys, though most VGUI messages are flat data records. KeyValues don't have data/type definitions or similar, so you can add or remove entries of any type as you like. Thus sender and receiver must both know the internal structure (e.g. key names, types and their meaning) of a KeyValues message object to communicate successfully. Here some sample code how to use KeyValues in general:

// create a new KeyValues Object named "MyName"
KeyValues* data = new KeyValues("MyName"); 

// add data entries
data->SetInt( "anInteger", 42 );
data->SetString( "aString", "Marvin" );

// read data entries
int x = data->GetInt("anInteger");
Con_Printf( data->GetString("aString") );

// destroy KeyValues object again, free data records
data->deleteThis();

To send a message you can call the Panel member function PostMessage(...) or directly ivgui()-> PostMessage(...). The name of the KeyValues object is also the message name used for later dispatching. VGUI will call the target panel's OnMessage(...) function, which will dispatch the message to a previous defined message handler. A panel can register new message handlers with one of the MESSAGE_FUNC_* macros, which adds a handler function to the message map. Never override OnMessage(...) to handle new messages, always use a macro. Please note that when you use the macro MESSAGE_FUNC_* only those specific messages will be thrown to your function. If you want to catch the newLine (ENTER) message from a txtEntry element you will use MESSAGE_FUNC_PARAMS( NewLineMessage, "TextNewLine",data). NewLineMessage is function to call, "TextNewLine" means all messages who's names are "TextNewLine" will be redirected and finally data is the input that will be passed to your function. In this case NewLineMessage will look like this void MyParentPanel::NewLineMessage (KeyValues* data).

class MyParentPanel  : public vgui::Panel
{
	...
private:
	MESSAGE_FUNC_PARAMS( OnMyMessage, "MyMessage", data );
}

void MyParentPanel::OnMyMessage (KeyValues* data)
{
	const char* text = data->GetString("text");
}

The sending panel creates a KeyValues object, adds message parameters and sends the message (to its parent in this case). The KeyValues object is destroyed by VGUI after it has been processed.

void MyChildPanel::SomethingHappened()
{
	if ( GetVParent() )
	{
		KeyValues* msg = new KeyValues("MyMessage");
		msg->SetString("text", "Something happened");
		PostMessage(GetVParent(), msg);
	}
}

Using PostMessage() the sending panel must address a single, specific target, which means that all other panels interested in a state change must be known and addressed individually. To avoid hard coding these dependencies, panels have a public event system called action signals. A panel fires generic events with PostActionSignal(KeyValues* message) and interested panels can register as listeners for these signals with AddActionSignalTarget(Panel* target). These action signals are widely used by VGUI controls, for example messages like "TextChanged" fired by class TextEntry or "ItemSelected" used by class ListPanel. All action signal messages contain a pointer entry "panel" that points to the sending panel.

The example from above using action signals would need the parent panel to register as a listener, preferably in the constructor after the child panel has been created. The child panel just uses PostActionSignal() instead of PostMessage():

MyParentPanel::MyParentPanel()
{
	...
	m_pChildPanel->AddActionSignalTarget( this );
}

void MyParentPanel::OnMyMessage (KeyValues* data)
{
	const char* text = data->GetString("text");
	Panel* pSender = (Panel* ) data->GetPtr("panel", NULL);
}

void MyChildPanel::SomethingHappened()
{
	KeyValues* msg = new KeyValues("MyMessage");
	msg->SetString("text", "Something happened");
	PostActionSignal ( msg );
}

To catch the return/enter key from a txtEntry element use this.

class MyParentPanel  : public vgui::Panel
{
	...
private:
	MESSAGE_FUNC_PARAMS( OnMyMessage, "NewLineMessage", data );
}
MyParentPanel::MyParentPanel()
{
	...
	m_pChildPanel = new vgui::TextEntry( this, "txtEntry" );
	m_pChildPanel->AddActionSignalTarget( this );
	m_pChildPanel->SendNewLine(true); // with the txtEntry Type you need to set it to pass the return key as a message
}

void MyParentPanel::NewLineMessage (KeyValues* data)
{
	// when the txtEntry box posts an action signal it only sets the name
	// so it is wise to check whether that specific txtEntry is indeed in focus
	// Is the Text Entry box in focus?
	if (m_pChildPanel->HasFocus())
	{
	// We have caught the message, now we can handle it. I would simply repost it to the OnCommand function 
	// Post the message to our command handler
		this->OnCommand("Submit");
	}
}

A commonly used action signal is the "Command" message, since no message handler needs to be installed. Panels just need to derive the virtual Panel function OnCommand(const char* command) and check for the correct command string. The "Command" message is used by all Button classes and is fired whenever the button is pressed. Here is the example from above using the Command message:

class MyParentPanel  : public vgui::Panel
{
	...
protected:
	virtual void OnCommand(const char* command);
}

MyParentPanel::MyParentPanel()
{
	...
	m_pChildPanel->AddActionSignalTarget( this );
}

void MyParentPanel::OnCommand(const char* command)
{
	if (!stricmp(command, "SomethingHappened"))
	{
		DoSomething ();
	}
	else
	{
		BaseClass::OnCommand(command);
	}
}

void MyChildPanel::SomethingHappened()
{
	KeyValues* msg = new KeyValues("Command");
	msg->SetString("command", "SomethingHappened");
	PostActionSignal ( msg );
}

Schemes

A VGUI scheme defines the general "look & feel" of your panels by specifying used colors, fonts & icons of control elements. Schemes are defined in resource files, for example Filehl2\resource\clientscheme.res. A new panel inherits by default the scheme settings used by its parent. It's also possible to preview the fonts, borders and colors of a scheme by using the showschemevisualizer command, with an argument matching the name of your scheme, in the console. The VGUI scheme manager can load a new scheme with LoadSchemeFromFile(char* fileName, char* tag) which returns HScheme handle. To make a panel using a loaded VGUI scheme call the panel member function SetScheme(HScheme scheme).

Schemes set the general look of panel elements, but they don't arrange specific control elements of your panel. One way of adding elements to your panel is by doing that in source code. You can create them (usually in the constructor of the parent panel) and set properties like size and position manually using the panel member functions. This will become quite complex and time-consuming for larger dialogs with lots of control elements.

The most common way to define the layout of your panel elements is to describe all elements in an external resource file like Filehl2\resource\UI\classmenu.res (a KeyValues text file). When the parent panel is created, this file is loaded and executed with LoadControlSettings(char* dialogResourceName). Do note, however, that this function is only defined for EditablePanel (or an EditablePanel derivative such as Frame). In this resource file each control element is defined in a separate section. A typical control definition looks like this:

MyControlName
{
	ControlName	Label		// control class 
	fieldName	MyControlName	// name of the control element
	xpos		8		// x position
	ypos		72		// y position
	wide		160		// width in pixels
	tall		24		// height in pixels
	visible		1		// it's visible...
	enabled		1		// ...and enabled
	labelText	"Hello world"	// display this unlocalized text
	textAlignment	west		// left text alignment
}

Each control property has a key name and a value. Properties defined by the base class Panel are available for all controls (like xpos, ypos, wide, tall etc). A list of all available key names is returned by the panel member function GetDescription(). Derived panel classes may add new fields for their specific properties. Processing these new fields must be handled by overriding the virtual function ApplySettings(KeyValues* inResourceData). Here you can also lookup how values for an existing control property are interpreted.

Build mode

VGUI Build Mode panel

An even simpler way to edit panel and control layouts is the VGUI Build Mode. This allows you to modify and save the layout of a panel resource file while the application is running. To edit a panel, just start the game and open this panel, so it gets the input focus. Then press Shift+Ctrl+Alt+B to open the VGUI Build Mode editor. In Build Mode you can easily rearrange existing elements and change their control properties (press 'Apply' to update changes). To add a new control element, just choose the desired class from the combo-box on the lower right side and an empty control object of that class will be place in your panel for further editing. To save the current layout in the associated resource file press the 'Save' button (make sure the resource file is not write-protected).

Drawing & surfaces

Using schemes and control properties you can change the general appearance and layout of existing controls, but it doesn't allow you to create completely new elements. To change the appearance of a panel you have to override the two virtual functions Panel::Paint() and Panel::PaintBackground(). In these functions you can use drawing functions provided by the ISurface interface to place lines, rectangles, text, images etc. When updating the screen, VGUI calls first PaintBackground() and then Paint() for each panel and its children panels recursively. Drawing coordinates are relative to the panel you draw in. Here same example code how to draw a red box in the upper left corner:

void MyPanel::Paint(void)
{
	BaseClass::Paint();  
	surface()->DrawSetColor(  255, 0, 0, 255 ); //RGBA
	surface()->DrawFilledRect( 0, 0, 20, 20 ); //x0,y0,x1,y1
}

To draw text on a panel surface you need to set the used font first. Fonts have names like "DefaultSmall" and their properties like True-Type-Font, size and weight are defined in scheme resource files. The font handle can be retrieved by calling GetFont("Name") of the current panel scheme, then the surface can be told to use it as the current font. After setting the color and position for the next text output, the text itself must be passed as wide char string (Unicode) to DrawPrintText(...). This text will not be printed as it is and not localized anymore, so localization tokens must be translated beforehand.

void MyPanel::Paint(void)
{
	wchar_t* pText = L"Hello world!";	// wide char text

	// get the right font handle for this scheme
	vgui::IScheme* pScheme = vgui::scheme()->GetIScheme(GetScheme());
	vgui::HFont hFont = pScheme->GetFont( "DefaultSmall" );

	surface()->DrawSetTextFont( hFont ); // set the font	
	surface()->DrawSetTextColor( 255, 0, 0, 255 ); // full red
	surface()->DrawSetTextPos( 10, 10 ); // x,y position
	surface()->DrawPrintText( pText, wcslen(pText) ); // print text
}

To draw a texture or image, VGUI has to load the texture from disk once (panel constructor) and generate a corresponding texture ID. This texture ID is then used as reference when drawing the texture. Here an example how to load and draw the texture File\materials\your_folder\mylogo.vmt:

MyPanel::MyPanel(void)
{
	//Do _not_ forget CreateNewTextureID(), it gave me a headache for a week
	m_nTextureID = vgui::surface()->CreateNewTextureID();
	vgui::surface()->DrawSetTextureFile( m_nTextureID, "vgui/your_folder/mylogo" , true, false);
}

void MyPanel::Paint(void)
{
	vgui::surface()->DrawSetTexture( m_nTextureID );
        vgui::surface()->DrawSetColor(50,50,50,100);
	vgui::surface()->DrawTexturedRect( 0, 0, 100, 100 );
}

Proportionality

Coordinate values that you specify in configuration files are relative to a 640x480 resolution if SetProportional(true) is called before a file is loaded (which, by default, it is). These values will be scaled to the correct size no matter what the user's current resolution may be. For example, a "tall" value of 120 will always mean the panel takes up 1/4th the height of the screen, and an "x" value of 320 means that the panel's left edge will always be centered.

Tip.pngTip:Use c-<wide/tall> in your xpos and ypos values to center an element on-screen at different aspect ratios.

However, sometimes you will need to calculate coordinates for custom painting and forgo the luxury of configuration files. The problem is that values that you specify in code are relative to the user's native resolution, which may or may not be the same as the current game resolution (thus rendering surface()->GetScreenSize() useless). The solution to this problem is to use scheme()->GetProportionalScaledValue(). This allows you to specify a coordinate on the 640x480 resolution and have it be remapped to the current resolution.

Localization

VGUI text elements support automatic localization for the preferred user language. Lets assume a text label should show "Hello world!" then you could set this text directly with SetText( "Hello World."). But if the user would choose a different language than English this label would still show the English text. Therefore you should always use localization tokens to tell VGUI to translate the token into user language, in this example SetText( "#MyMod_HelloWorld"). Tokens are strings starting with the hash sign '#' as control character to tell VGUI that this is not plain text.

VGUI keeps a global translation table to map tokens to plain text representation. These translation tables are loaded from resource files, where each file has an extra copy for every supported language (e.g. File\resource\hl2_english.txt, File\resource\hl2_german.txt).

Tip.pngTip:Localization files must be stored in the UCS2-Little Endian format. You can edit the localization using XBLAH's Modding Tool XBLAH's Modding Tool, using VGUI Localization Tool VGUI Localization Tool, or else you may need an advanced text editor like Notepad++.

A new token definition for your game would look like this:

In mymod_english.txt:

"MyMod_HelloWorld"		"Hello world."

In mymod_german.txt:

"MyMod_HelloWorld"		"Hallo Welt."
"[english]MyMod_HelloWorld"	"Hello world."

If your game folder is named "mymod" the Source engine will automatically load the correct translation file File/resource/gamedir_language.txt. You can load additional translation files using the ILocalize interface function AddFile(...).

You can use the ILocalize interface also to manually translate a token into current user language, e.g. g_pVGuiLocalize->Find("#MyMod_HelloWorld") (or vgui::localize()->Find("#MyMod_HelloWorld") in previous versions). This function returns the translation as a 16-bit wide char string (Unicode).

VGUI uses Unicode for all text representation to support languages that use more then the 255 ASCII characters, like Chinese or Russian. Language resource files are encoded as Unicode. Note that the files will not be loaded properly unless they are specifically saved with proper Unicode encoding. To convert strings between ANSI ASCII and Unicode during runtime you can used the ILocalize interface functions ConvertANSIToUnicode(...) and ConvertUnicodeToANSI(...). Also a very useful function is ConstructString(...) which is basically like sprintf(...) for Unicode strings.

See also

Tutorials