VGUI HTML Screen

From Valve Developer Community
Revision as of 08:32, 29 December 2006 by Daedalus (talk | contribs) (Created tutorial)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

HTMLView

This is a tutorial on adding a HTML screen that can be shown during gameplay, and have it's address changed by in-game commands. I use it for adding backstory to my maps, perhaps it will be useful for others too.

Note: This modification creates a HTML control that has JavaScript disabled for security reasons.

Requirements

  • Visual Studio .Net 2003/2005
  • Source SDK

Getting Started

For this example, we'll be creating our own mod (you can later make changes to your own mod if you desire.) Please note that this will not work for unedited HL2/HL2:MP, CS:Source, etc. It requires changes to some core files.

Create your mod

Before we begin, we need to ensure we have the latest Source SDK source files. On the Source SDK screen, double click Refresh SDK Content. This ensures we don't have out of date source code.

Now double click click Create A Mod.

For this example, we'll be using HL2 single player, so select Modify Half-Life 2 Single Player and click Next.

I always like to put my mods in the SourceMods folder, but choose yourself where you'd like to put your mod, then enter a name for it, eg HTMLTest and click Next.

Source will now copy all the files over and prepare your mod.

Double checking

Now we need to be sure that everything works before we start messing with the code. If you've done this all before, you may skip this step.

Now, go to your mod folder, then go into your src folder. There are a few different Visual Studio .Net solution files. We want the one named Game_HL2. However, there are two different versions. Try to open the 2005 version first, and if that isn't recognized, use the 2003 version.

Compiling

At the top of the screen inside Visual Studio.Net should be a 'Solution Configuration' option, which by default reads Debug HL2. We want to change that to Release HL2 before proceeding.

Next, click the Build menu and then click Build Solution and grab yourself a coffee.

Testing

If all goes well, you should be able to run the 'run_mod.bat' file in your mod directory and have it load up HL2. You can't do much at this point except look around the menus.

If something went wrong...Post in the discussion or email me and I'll see how I can help.

Adding the HTMLView

Now we get to the code changes.

In Visual Studio, there should be a file tree (it may just show client_hl2 and server_hl2 currently.)

Click the + next to client_hl2 to expand it.

Adding the header file

We need to add our header file now. Just create one in the header folder of the project and paste my code in. Right click Header Files and click Add->Add New Item.

IHTMLView.h

//========= Copyright © 2006, Julian 'Daedalus' Thatcher. =====================//
//
// Purpose: Header file for HTMLView window
//
//
// $NoKeywords: $
//=============================================================================//

// IHTMLView.h
class IHTMLView
{
public:
	virtual void		Create( vgui::VPANEL parent ) = 0;
	virtual void		Destroy( void ) = 0;
	virtual void		Activate( void ) = 0;
	virtual void		UpdateHTML ( void ) = 0;
};

extern IHTMLView* htmlview;

#define CenterThisPanelOnScreen()\
    int x,w,h;\
    GetBounds(x,x,w,h);\
    SetPos((ScreenWidth()-w)/2,(ScreenHeight()-h)/2)

#define CenterPanelOnScreen(panel)\
    int x,w,h;\
    panel->GetBounds(x,x,w,h);\
    panel->SetPos((panel->ScreenWidth()-w)/2,(panel->ScreenHeight()-h)/2)

That wasn't too hard now, was it?

Adding the source file

Now we add the source code for our HTML View.

HTMLView.cpp

//========= Copyright © 2006, Julian 'Daedalus' Thatcher. =====================//
//
// Purpose: HTMLView implementation
//
// Ingame Usage Commands:
//  cl_htmltarget path_to_file		Sets the URL to view
//  ToggleHTMLView					Shows or hides the HTML view
// 
// If the given url does not begin with 'http://', it is considered relative
// to the mod directory.
//
//
// $Created: Tuesday, 26 December 2006
// $Author:  Julian 'Daedalus' Thatcher (daedalus.raistlin@gmail.com)
// $NoKeywords: $
//=============================================================================//

//The following include files are necessary to allow your MyPanel.cpp to compile.
#include "cbase.h"
#include "IHTMLView.h"

using namespace vgui;

#include "iclientmode.h"
#include <vgui/IVGui.h>
#include <vgui_controls/Button.h>
#include <vgui_controls/Frame.h>
#include <vgui_controls/HTML.h>
#include <vgui_controls/TextEntry.h>

static void onTargetUpdate (ConVar *var, char const *pOldString);

ConVar cl_showhtmlview("cl_showhtmlview", "0", FCVAR_CLIENTDLL, "Sets the state of HTMLView <state>");
ConVar cl_htmltarget("cl_htmltarget", "", FCVAR_CLIENTDLL, 
					 "Sets the HTML address to <state>. ", onTargetUpdate);

//CHTMLView_Panel class: Tutorial example class
class CHTMLView_Panel : public vgui::Frame
{
	DECLARE_CLASS_SIMPLE(CHTMLView_Panel, vgui::Frame); 
	//CHTMLView_Panel : This Class / vgui::Frame : BaseClass

	CHTMLView_Panel(vgui::VPANEL parent); 	// Constructor
	~CHTMLView_Panel(){};				// Destructor

protected:
	//VGUI overrides:
	virtual void OnTick();
	virtual void OnCommand(const char* pcCommand);

private:
	//Other used VGUI control Elements:
	vgui::Button *m_exit;

public:
	vgui::HTML   *m_HTML;
};

// Constuctor: Initializes the Panel
CHTMLView_Panel::CHTMLView_Panel(vgui::VPANEL parent)
: BaseClass(NULL, "HTMLView")
{
	SetParent( parent );

	SetKeyBoardInputEnabled( true );
	SetMouseInputEnabled( true );
	
	SetProportional( true );
	SetTitleBarVisible( true );
	SetMinimizeButtonVisible( false );
	SetMaximizeButtonVisible( false );
	SetCloseButtonVisible( true );
	SetSizeable( false );
	SetMoveable( true );
	SetVisible( true );

	m_HTML = new vgui::HTML(this, "MyHTMLPage");

	SetScheme(vgui::scheme()->LoadSchemeFromFile("resource/SourceScheme.res", "SourceScheme"));

	LoadControlSettings("resource/UI/HTMLView.res");

	vgui::ivgui()->AddTickSignal( GetVPanel(), 100 );
	
	DevMsg("HTMLView has been constructed\n");

	CenterThisPanelOnScreen();

}

//Class: CHTMLView_PanelInterface Class. Used for construction.
class CHTMLView_PanelInterface : public IHTMLView
{
private:
	CHTMLView_Panel *MyPanel;
public:
	CHTMLView_PanelInterface()
	{
		MyPanel = NULL;
	}
	void Create(vgui::VPANEL parent)
	{
		MyPanel = new CHTMLView_Panel(parent);
	}
	void Destroy()
	{
		if (MyPanel)
		{
			MyPanel->SetParent( (vgui::Panel *)NULL);
			delete MyPanel;
		}
	}
	void Activate( void )
	{
		if ( MyPanel )
		{
			MyPanel->Activate();
		}
	}

	// Update HTML address
	void UpdateHTML ( void )
	{
		char *target;
		char temp[1024];
		if ( MyPanel )
		{
			// Get value of 'cl_htmltarget'
			target = (char *) cl_htmltarget.GetString();
			if(strlen(target) > 0)
			{
				// Search for http:// prefix
				if( !(
					target[0] == 'h' &&
					target[1] == 't' &&
					target[2] == 't' &&
					target[3] == 'p' &&
					target[4] == ':' &&
					target[5] == '/' &&
					target[6] == '/' )
					)
				{
					// Start is not 'http://', append target to game directory
					strcpy(temp, engine->GetGameDirectory());
					strcat(temp, "/");
					strcat(temp, target);
					MyPanel->m_HTML->OpenURL(temp, true);
				} else {
					// Absolute (http://) path given
					MyPanel->m_HTML->OpenURL(cl_htmltarget.GetString(), true);
				}
			}
		}
	}
};

void CHTMLView_Panel::OnTick()
{
	BaseClass::OnTick();
	SetVisible(cl_showhtmlview.GetBool()); //CL_SHOWMYPANEL / 1 BY DEFAULT
}

//
// Handle commands from VGUI events.
// By default, only the 'exit' button has a command, and that
// command is 'turnoff'
void CHTMLView_Panel::OnCommand(const char* pcCommand)
{
	if(!Q_stricmp(pcCommand, "turnoff"))
		cl_showhtmlview.SetValue(0);
}

static void onTargetUpdate (ConVar *var, char const *pOldString)
{
	htmlview->UpdateHTML();
}

static CHTMLView_PanelInterface g_MyPanel;

// The following are exported to the game system
IHTMLView* htmlview = (IHTMLView*)&g_MyPanel;

CON_COMMAND(ToggleHTMLView, "Toggles HTMLView on or off")
{
	cl_showhtmlview.SetValue(!cl_showhtmlview.GetBool());
	htmlview->Activate();	
};

CON_COMMAND(UpdateHTMLView, "Updates HTMLView address")
{
	DevMsg("Updating myPanel location");
	htmlview->UpdateHTML();
}

// This was used for debugging
// Displays current mod directory
CON_COMMAND(WhereAmI, "Displays current path")
{
	const char *pGameDir = engine->GetGameDirectory();
	DevMsg(pGameDir);
}

Other required code modifications

There are changes that we need to make to one of the core game files. Don't worry, we don't change much.

vgui_int.cpp

Find this file in the Source Files tree somewhere near the bottom and open it up.

At the top of the file are a bunch of #include statements. Under the last one (but before the line using namespace vgui), add this:

#include "IHTMLView.h"

This lets the game access our HTMLView.

Next, find the line void VGui_CreateGlobalPanels( void ), somewhere around line 154. There should be two statements beginning with VPANEL. Directly underneath these, add:

VPANEL gameParent	  = enginevgui->GetPanel( PANEL_INGAMESCREENS );
htmlview->Create(gameParent);

Next, find the line void VGui_Shutdown() which should be the function directory below the one you just edited. Somewhere in this function, after the { and before the } you need to add:

htmlview->Destroy();

Good news! We're almost done!

The User Interface

Now we need to add the UI file that the game loads for our HTML view.

HTMLView.res

Save this file to yourmod\resource\ui\HTMLView.res.

"resource/UI/HTMLView.res"
{
	"HTMLView"
	{
		"ControlName"		"CHTMLView_Panel"
		"fieldName"		"HTMLView"
		"xpos"		"64"
		"ypos"		"48"
		"wide"		"512"
		"tall"		"384"
		"autoResize"		"0"
		"pinCorner"		"0"
		"visible"		"1"
		"enabled"		"1"
		"tabPosition"		"0"
		"settitlebarvisible"		"1"
		"title"		"Document"
	}
	"frame_topGrip"
	{
		"ControlName"		"Panel"
		"fieldName"		"frame_topGrip"
		"xpos"		"6"
		"ypos"		"0"
		"wide"		"499"
		"tall"		"4"
		"autoResize"		"0"
		"pinCorner"		"0"
		"visible"		"1"
		"enabled"		"1"
		"tabPosition"		"0"
	}
	"frame_bottomGrip"
	{
		"ControlName"		"Panel"
		"fieldName"		"frame_bottomGrip"
		"xpos"		"6"
		"ypos"		"380"
		"wide"		"491"
		"tall"		"4"
		"autoResize"		"0"
		"pinCorner"		"0"
		"visible"		"1"
		"enabled"		"1"
		"tabPosition"		"0"
	}
	"frame_leftGrip"
	{
		"ControlName"		"Panel"
		"fieldName"		"frame_leftGrip"
		"xpos"		"0"
		"ypos"		"6"
		"wide"		"4"
		"tall"		"371"
		"autoResize"		"0"
		"pinCorner"		"0"
		"visible"		"1"
		"enabled"		"1"
		"tabPosition"		"0"
	}
	"frame_rightGrip"
	{
		"ControlName"		"Panel"
		"fieldName"		"frame_rightGrip"
		"xpos"		"508"
		"ypos"		"6"
		"wide"		"4"
		"tall"		"363"
		"autoResize"		"0"
		"pinCorner"		"0"
		"visible"		"1"
		"enabled"		"1"
		"tabPosition"		"0"
	}
	"frame_tlGrip"
	{
		"ControlName"		"Panel"
		"fieldName"		"frame_tlGrip"
		"xpos"		"0"
		"ypos"		"0"
		"wide"		"6"
		"tall"		"6"
		"autoResize"		"0"
		"pinCorner"		"0"
		"visible"		"1"
		"enabled"		"1"
		"tabPosition"		"0"
	}
	"frame_trGrip"
	{
		"ControlName"		"Panel"
		"fieldName"		"frame_trGrip"
		"xpos"		"505"
		"ypos"		"0"
		"wide"		"6"
		"tall"		"6"
		"autoResize"		"0"
		"pinCorner"		"0"
		"visible"		"1"
		"enabled"		"1"
		"tabPosition"		"0"
	}
	"frame_blGrip"
	{
		"ControlName"		"Panel"
		"fieldName"		"frame_blGrip"
		"xpos"		"0"
		"ypos"		"377"
		"wide"		"6"
		"tall"		"6"
		"autoResize"		"0"
		"pinCorner"		"0"
		"visible"		"1"
		"enabled"		"1"
		"tabPosition"		"0"
	}
	"frame_brGrip"
	{
		"ControlName"		"Panel"
		"fieldName"		"frame_brGrip"
		"xpos"		"497"
		"ypos"		"369"
		"wide"		"14"
		"tall"		"14"
		"autoResize"		"0"
		"pinCorner"		"0"
		"visible"		"1"
		"enabled"		"1"
		"tabPosition"		"0"
	}
	"frame_caption"
	{
		"ControlName"		"Panel"
		"fieldName"		"frame_caption"
		"xpos"		"60"
		"ypos"		"32"
		"wide"		"504"
		"tall"		"18"
		"autoResize"		"0"
		"pinCorner"		"0"
		"visible"		"1"
		"enabled"		"1"
		"tabPosition"		"0"
	}
	"frame_minimize"
	{
		"ControlName"		"Button"
		"fieldName"		"frame_minimize"
		"xpos"		"0"
		"ypos"		"0"
		"wide"		"8"
		"tall"		"8"
		"autoResize"		"0"
		"pinCorner"		"0"
		"visible"		"0"
		"enabled"		"1"
		"tabPosition"		"0"
		"labelText"		"0"
		"textAlignment"		"north-west"
		"dulltext"		"0"
		"brighttext"		"0"
		"wrap"		"0"
		"Default"		"0"
	}
	"frame_maximize"
	{
		"ControlName"		"Button"
		"fieldName"		"frame_maximize"
		"xpos"		"0"
		"ypos"		"0"
		"wide"		"8"
		"tall"		"8"
		"autoResize"		"0"
		"pinCorner"		"0"
		"visible"		"0"
		"enabled"		"1"
		"tabPosition"		"0"
		"labelText"		"1"
		"textAlignment"		"north-west"
		"dulltext"		"0"
		"brighttext"		"0"
		"wrap"		"0"
		"Default"		"0"
	}
	"frame_mintosystray"
	{
		"ControlName"		"Button"
		"fieldName"		"frame_mintosystray"
		"xpos"		"0"
		"ypos"		"0"
		"wide"		"8"
		"tall"		"8"
		"autoResize"		"0"
		"pinCorner"		"0"
		"visible"		"0"
		"enabled"		"1"
		"tabPosition"		"0"
		"labelText"		"o"
		"textAlignment"		"north-west"
		"dulltext"		"0"
		"brighttext"		"0"
		"wrap"		"0"
		"Command"		"MinimizeToSysTray"
		"Default"		"0"
	}
	"frame_close"
	{
		"ControlName"		"Button"
		"fieldName"		"frame_close"
		"xpos"		"0"
		"ypos"		"0"
		"wide"		"8"
		"tall"		"8"
		"autoResize"		"0"
		"pinCorner"		"0"
		"visible"		"0"
		"enabled"		"1"
		"tabPosition"		"0"
		"labelText"		"r"
		"textAlignment"		"north-west"
		"dulltext"		"0"
		"brighttext"		"0"
		"wrap"		"0"
		"Default"		"0"
	}
	"frame_menu"
	{
		"ControlName"		"FrameSystemButton"
		"fieldName"		"frame_menu"
		"xpos"		"5"
		"ypos"		"6"
		"wide"		"14"
		"tall"		"14"
		"autoResize"		"0"
		"pinCorner"		"0"
		"visible"		"1"
		"enabled"		"1"
		"tabPosition"		"0"
		"textAlignment"		"west"
		"dulltext"		"0"
		"brighttext"		"0"
		"wrap"		"0"
		"Default"		"0"
	}
	"MyHTMLPage"
	{
		"ControlName"		"HTML"
		"fieldName"		"MyHTMLPage"
		"xpos"		"8"
		"ypos"		"25"
		"wide"		"494"
		"tall"		"320"
		"autoResize"		"0"
		"pinCorner"		"0"
		"visible"		"1"
		"enabled"		"1"
		"tabPosition"		"0"
	}
	"ExitButton"
	{
		"ControlName"		"Button"
		"fieldName"		"ExitButton"
		"xpos"		"460"
		"ypos"		"356"
		"wide"		"40"
		"tall"		"16"
		"autoResize"		"0"
		"pinCorner"		"0"
		"visible"		"1"
		"enabled"		"1"
		"tabPosition"		"0"
		"labelText"		"Exit"
		"textAlignment"		"west"
		"dulltext"		"0"
		"brighttext"		"0"
		"wrap"		"0"
		"Command"		"turnoff"
		"Default"		"1"
	}
	"BuildModeDialog"
	{
		"ControlName"		"BuildModeDialog"
		"fieldName"		"BuildModeDialog"
		"xpos"		"1"
		"ypos"		"124"
		"wide"		"240"
		"tall"		"336"
		"autoResize"		"0"
		"pinCorner"		"0"
		"visible"		"1"
		"enabled"		"1"
		"tabPosition"		"0"
		"settitlebarvisible"		"1"
		"title"		"#Frame_Untitled"
	}
}

Optional step: Add HTMLView to main menu

This step isn't required, but may be helpful in quickly checking if things worked. Open up your yourmod\resource\GameMenu.res file, and add the following before the final }.

        "5"
	{
		"label" "HTMLView"
		"command" "engine ToggleHTMLView"
		"notmulti" "1"
	}

Finishing up

Ok, so all the code has been added, the resource file created, and the other source edited. Hit Build Solution again and wait for it to compile. If there are any problems, please post in the Discussion section of this page and I'll try to help out.

In-Game operation

So now, load up your mod. If you edited your GameMenu.res file, there should be a new menu item at the end simply titled 'HTMLView'. A big screen showing a black box and an 'Exit' button should appear when you click this.

Alternatively, use the console command ToggleHTMLView.

So far, all you've seen is a black box and a button. In the console, type:

cl_htmltarget "http://www.google.com"

And then either press the menu item or type ToggleHTMLView again and you should see google :)

Sample Level

Want an example of how to implement this into your levels? Coming soon!