VGUI2: Creating a panel

From Valve Developer Community
(Redirected from VGUI: Making GameUI Panels)
Jump to navigation Jump to search
English (en)Español (es)中文 (zh)Translate (Translate)

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>

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:

#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