Garry's Mod Wiki

Revision Difference

VGUI_Creating_Custom_Elements#561262

<cat>Dev.UI</cat> <title>VGUI Creating Custom Elements</title> ⤶ # Why Create Custom VGUI Elements?⤶ Creating custom VGUI elements helps with many things, the biggest being code readability and maintainability. These new elements don't always have to be some brand new element from the ground up, in fact many custom elements are just derivatives from the games base panels with customizations that fit that project better. ⤶ Think of something like a <page>DButton</page> the default styling may not suit the look and feel of your project, so instead of having to restyle every <page>DButton</page> you create, you can create a new custom VGUI element that is derived from the standard <page>DButton</page>. Then apply your styling to that the new element once. This means that each time you create this new custom element you wont need to reapply the same styling, you'll only need to add what makes that button unique. ⤶ Now you _could_ create panels in the same file you want to use them with <page>vgui.CreateFromTable</page> however using this method to create panels usually leads to a very long file that becomes hard to read and maintain once your no longer activity working on it. Therefor while acceptable under specific circumstances this shouldn't be the main way of creating your custom elements. ⤶ # Creating Custom Elements⤶ The first and most fundamental thing you should know about any <page>Panel</page> is that it consists of four fundamental components:⤶ - The Table⤶ - The <page>PANEL:Init</page> Function⤶ - The <page>PANEL:Paint</page> Function⤶ - the <page>vgui.Register</page> Function⤶ ⤶ There are other hooks available like the <page>PANEL:Think</page> hook, but this is not required the panel to operate. Also some hooks are only available to specific panels, and for the sake of simplicity we will skip over those here and just point you to <page>PANEL_Hooks</page> to see all the hooks available to panels. ## Setting up your workspace⤶ Hopefully you have a basic understanding of the filesystem in Garry's Mod but as a crash course, all you need to know is that everything included in your addon folder gets """merged""" with the existing folder structure in your copy of the game. Now these don't actually move any files around, but its important to remember that the engine sees your `*myAddonName*/lua/autorun/client/example.lua` in the same spot as: `*garrysmod*/lua/autorun/client/example.lua`⤶ ⤶ I wanted to emphasis that because we will be leveraging that functionality to help organize our custom VGUI elements. Using the <page>Lua Folder Structure</page> we can see that there is `lua/vgui` this folder is loaded whenever the game loads its VGUI elements. This means any custom VGUI elements we create for out addon will be loaded automatically when we enter the game instead of needing to manually include them in our autorun script. ⤶ ⤶ ## Coding the new Element⤶ Now that you understand the basics of how panels work, we can start writing out own.⤶ ⤶ to start working on a new panel create a new file under `lua/vgui/{nameOfElement}.lua` and make sure to write in the four fundamental elements from before like so:⤶ ⤶ ⤶ # 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 <page>DButton</page>. Instead of laboriously restyling and modifying each <page>DButton</page> separately, envision creating a new element that inherits all the functionalities of the <page>DButton</page>. 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 <page>vgui.CreateFromTable</page>, 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 <page text="Addon Structure">Lua Folder Structure</page> 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 <page text="Addon Structure">Lua Folder Structure</page> 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.⤶ ```lua⤶ 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 <page text="Panel Hook">PANEL_Hooks</page> 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 <page>Panel:Dock</page> state.⤶ ⤶ ```lua⤶ function PANEL:Init()⤶ -- Default / Initial Values Set Here⤶ end⤶ ``` -- Creating the Table our panel will use⤶ PANEL = PANEL or {}⤶ ⤶ --Creating our Init Function⤶ function PANEL:Init() ⤶ ⤶ ⤶ ⤶ ## 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 <page text="Panel Hook">PANEL_Hooks</page> is called every **Frame** the panel is visible. Now there are two libraries dedicated to painting panels, first is the <page>surface</page> library. This is the go-to library you want to use. Secondly there is the <page>draw</page> library. The <page>draw</page> library is just a wrapper for the <page>surface</page> library making some of the more difficult things to do in <page>surface</page> easier. Most Notably the <page>draw.RoundedBox</page>⤶ ⤶ The <page text="Paint Hook">PANEL:Paint</page> 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 <page>surface</page> and <page>draw</page> functions⤶ ⤶ ```lua⤶ 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 ⤶ --Creating the Paint Function⤶ function PANEL:Paint()⤶ ⤶ end⤶ ⤶ --Registering our new VGUI element, giving it a name, the table to use, and if it derives methods from another element⤶ vgui.Register("NewElementName",PANEL,"OptionalNameOfPanelItsDerivedFrom") ⤶ ```⤶ ⤶ Above include very primitive examples from both the <page>surface</page> and <page>draw</page> libraries. Now Order of these functions matter within in the <page text="Paint Hook">PANEL:Paint</page>. If you were to copy and paste this paint function ontop an existing panel it would look something like:⤶ <image src="aaf9e/8dc4174650c9356.png" size="565" name="sqre.png" />⤶ 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 <page>surface</page>⤶ ⤶ 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 <page>PANEL:NoClipping</page> or <page>Global.DisableClipping</page>. 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 <page>vgui.Register</page> 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.⤶ ⤶ ```lua⤶ vgui.Register( "WikiExample", PANEL, "Panel")⤶ ``` ⤶ Now that we the essential outline of our panel created lets start by seeing if one of the premade elements from <page>VGUI Element List</page> has similar functionality to what we already need. Its important to look at the methods these panels already have, for example we are going to create a custom button. So we should derive our new panel from the `DButton` panel since that will give us access to methods like `PANEL:DoClick`. ⤶ Lets do that now by updating our <page>vgui.Register</page> function in our code⤶ ```⤶ vgui.Register("WikiButton",PANEL,"DButton")⤶ ⤶ above is an example of a completed <page>vgui.Register</page> function. ⤶ ⤶ # Using Your New Element⤶ once you have registered your new custom element with <page>vgui.Register</page> in step 4 your now able to create that element within your main project files using the name you registered with it⤶ ⤶ ```lua⤶ local MyNewElement = vgui.Create( "WikiExample" )⤶ ``` ⤶ ### So what exactly did that do? ⤶ > All the premade panels in Garry's Mod are derived from one of the engine panels, these engine panels can be found on the <page>VGUI Element List</page> page. Most of the Panels that start with a "D" are derivatives of these engine panels with functionality and <page>PANEL:Paint</page> functions applied to them to be easier to work with. ⤶ > Most of the time you will never need to derive directly from an engine panel unless your creating a whole new type of element from scratch. Instead its commonly easier to derive your panel from one the "D" Panels which gives you access to more convivence functions instead of needing to recode basic functionalities into your panel every time. ⤶ ⤶ > When we set our new `WikiButton` element to derive from the `DButton` we get every method that `DButton` has access to and everything that it also derives from. Here is a tree that shows the chain of derivatives:⤶ ```⤶ Panel⤶ - Label⤶ - DLabel⤶ - DButton⤶ - WikiButton <-- this being our custom element⤶ ```⤶ ⤶ # Using your Custom VGUI Component ⤶ Using the components you've created is the same as using any other premade element (seeing as we just created an element in the same way)⤶ ⤶ all you need is the <page>vgui.Create</page> function with the name you gave the <page>vgui.Register</page> function.⤶ for our `WikiButton` our create function would look like:⤶ ```⤶ VarName = vgui.Create("WikiButton")⤶ ```⤶ ⤶ <note>This page did not cover anything about customizing this new panel we created. This page was created to be more informative about the process of creating custom VGUI elements instead of customizing them. The wiki button mentioned in this article will look identical to a `DButton` because nothing in this article covered changing anything about it (yet?) </note>⤶ ⤶ ⤶ ⤶ # 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 <page>VGUI Element List</page> 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 <page>DButton</page> in order to have access to `DoClick()`" when in actuality when you look at <page text="Where that function comes from">DLabel:DoClick</page> you really only need to derive your panel from a <page>DLabel</page> to have access to `DoClick()`