Garry's Mod Wiki

VGUI Creating Custom Elements

Why Create Custom VGUI Elements?

Developing custom VGUI (Valve Graphical User Interface) elements offers numerous advantages, with enhanced code readability and maintainability standing out as primary benefits. These new elements do not always need to be entirely original components; in many cases, they are simple modifications or extensions of the game's base panels. By adopting this approach, you can streamline the development process, making UI elements much more manageable.

Imagine a project in which you typically employ a DButton. Instead of laboriously restyling and modifying each DButton separately, envision creating a new element that inherits all the functionalities of the DButton. This approach enables you to effortlessly restyle and enhance its capabilities in a single location.

Now you can create multiple elements in the same file using vgui.CreateFromTable, however, you normally only want to do this for specifically Internal elements. Components that only exist to serve a larger Element within the same file.

The Main Steps to Creating a Custom Element

The Pre-Knowledge

Before we start going into depth about what a panel is and how to create one let us first establish some base knowledge on how VGUI elements are loaded and located within your Addon Structure and within the larger garrysmod/ folders.

Assuming you have a basic grasp of the Garry's Mod filesystem, here's a crash course: all contents within your addon folder essentially get "merged" with the pre-existing folder structure in your game copy. Although no actual file relocation occurs, it's crucial to remeber that the engine interprets your *myAddonName*/lua/autorun/client/example.lua as residing in the same location as garrysmod/lua/autorun/client/example.lua.

This is emphasized because it is good practice to use this to our advantage when creating new elements. Looking at the Addon Structure page we can see our addons can have a dedicated lua/vgui folder. This is where you should create and store your new vgui elements. The benefit to this is that your new elements will be loaded and registered just as if they were shipped with the default game. meaning we don't need to worry about load order.

Lastly there are a few things to note. Your new elements should be given distinct names as to not collide with potentially any other addons an end user might also have loaded. A good example of this is the games default panels all being prefixed with D. Your filenames should be just the name of your element. This does not affect the code directly by doing this you will thank yourself later. Lastly be wise with how you create elements, a Red and Blue button do not need to be their own components, make one button component and style both variants individually.

Step 1: All Elements Are Tables

At their core all vgui elements are just fancy tables. This means that the first step to creating any new element is to create the table that will define this element.

local PANEL = {}

Above we create a local empty table and called it PANEL. This name can be anything you'd like but PANEL is the standard and its encouraged you also use it.

Step 2: The Init Function

The Init function is always the first Panel Hook called when a panel is created. This hook is only called once on creation so it is normally used to set default values and static values of the panel. This could range from inital values you want the panel to have to more dynamic functions like setting the Panel:Dock state.

function PANEL:Init() -- Default / Initial Values Set Here end

Step 3: The Paint Function

This function will determine the look of panel. This can be a bit unintuitive at first so please take the time to understand how the paint hook really works. Painting a panel is NOT persistent, every panel must be painted every single frame it is visible or it will not appear. This means unlike the Init function, this Panel Hook is called every Frame the panel is visible. Now there are two libraries dedicated to painting panels, first is the surface library. This is the go-to library you want to use. Secondly there is the draw library. The draw library is just a wrapper for the surface library making some of the more difficult things to do in surface easier. Most Notably the draw.RoundedBox

The Paint Hook is always called with two arguments; the width and the height of the panel (sometimes denoted as w and h) this allows you to easily access the panels width and height in this hook to use within your surface and draw functions

function PANEL:Paint( width, height ) --Surface and Draw functions go here surface.SetDrawColor(255,0,0,255) surface.DrawRect( 0, 0, width, height ) -- Using surface to draw a standard rectangle draw.RoundedBox( 15, 0, 0, width, height, Color(0,255,0,255)) -- Using Draw to draw a rounded green box end

Above include very primitive examples from both the surface and draw libraries. Now Order of these functions matter within in the Paint Hook. If you were to copy and paste this paint function ontop an existing panel it would look something like: Why? Because look at the order we called our painting functions, First we drew the red rectangle AND THEN drew the Green rounded rectangle. This means that our green rounded rectangle was painted OVER the red rectangle we initially drew with surface

We cant cover everything relating to painting panels here but the biggest things to keep in mind is that: You can only paint within the actual bounds of the panel unless you use PANEL:NoClipping or DisableClipping. and that the order in which you paint matters, see the red rectangle and green rounded box above.

Step 4: Registering Your New Element

Registering your element should always be the last thing you do in the elements file (unless creating internal elements, then register after each internal element is done). Once your ready to register your element you will need to call the vgui.Register function. This function is how you will define your elements name, the name of created table we used (PANEL), and the optional name of a base panel we want derive this element from.

vgui.Register( "WikiExample", PANEL, "Panel")

above is an example of a completed vgui.Register function.

Using Your New Element

once you have registered your new custom element with vgui.Register in step 4 your now able to create that element within your main project files using the name you registered with it

local MyNewElement = vgui.Create( "WikiExample" )

Additional Information

Inheritance

Inheritance is important when working with Garry mod UI. Garry's Mod default panels already come with a lot of built in functionalities that you can leverage when creating your own panel. Its worth looking through the VGUI Element List to see if your able to derive your new panel from one of them and save yourself some time.

Another part of that is knowing where functions come from. a popular misconception is "I need to derive my panel from a DButton in order to have access to DoClick()" when in actuality when you look at Where that function comes from you really only need to derive your panel from a DLabel to have access to DoClick()