Revision Difference
Derma_Skin_Creation#546681
<cat>Dev.UI</cat>
# Introduction
As a developer you may be familiar with <page>PANEL:Paint</page> and <page>PANEL:PaintOver</page>. They are functions to draw your Derma objects. But one day you wanted to make your own Derma design. You may think that redrawing is too complicated: <page>surface</page>, <page>draw</page>, <page>render</page>. So many functions, aren't they? Is there a way to make it easier and fancy? Yes, this page will show you how to create your own Derma Skin. Exactly same method uses default Derma and it's defined in Lua.
# How Derma skins work?
First thing to know, they're located in `"garrysmod/lua/skins/"`. Default Derma skin is in "default.lua".
It contains [UV texture mapping](https://en.wikipedia.org/wiki/UV_mapping), <page>Global.Color</page> definitions and <page text="Derma Hooks">derma.SkinHook</page>.
UV mapping is possible through <page>GWEN</page>. Some GWEN functions require a <page>IMaterial</page> in last argument, but it can be empty: GWEN will use **SKIN.GwenTexture** variable instead. Default skin texture can be found in **Garry's Mod .vpk** file in `"materials/gwenskin/gmoddefault.png"`.
Inside file there're bunch of Derma Hooks. They're used in conjunction with <page>derma.SkinHook</page> (used in <page>Global.Derma_Hook</page>).
## Example
This is in **["default.lua"](https://github.com/Facepunch/garrysmod/blob/master/garrysmod/lua/skins/default.lua)**.
```
function SKIN:PaintFrame( panel, w, h )
if ( panel.m_bPaintShadow ) then -- This is DFrame shadow
DisableClipping( true )
SKIN.tex.Shadow( -4, -4, w+10, h+10 ) -- This is drawing function, that contains a piece of our texture
DisableClipping( false )
end
if ( panel:HasHierarchicalFocus() ) then -- Checks if DFrame is in focus and decide how to paint it
self.tex.Window.Normal( 0, 0, w, h ) -- This is drawing function, that contains a piece of our texture
else
self.tex.Window.Inactive( 0, 0, w, h ) -- This is drawing function, that contains a piece of our texture
end
end
```
And this is in `"lua/vgui/dframe.lua"` where <page>DFrame</page> is defined.
```
function PANEL:Paint( w, h )
-- ...
derma.SkinHook( "Paint", "Frame", self, w, h ) -- This creates hook to derma library, derma library is connected to our skin
-- ...
end
```
----
Last thing that skin file does is adding skin to Derma skins list. This action is done by <page>derma.DefineSkin</page>.
# Preparing to create Derma skin
1. Get [Adobe Photoshop](https://www.adobe.com/products/photoshop.html)
1. Extract `"materials/gwenskin/gmoddefault.psd"` (You can do this with [GCFScape](http://nemesis.thewavelength.net/index.php?p=26))
1. Copy code (or file) from `"lua/skins/default.lua"`
1. Create lua file in `"lua/skins/"`. For this tutorial we will name it **"myskin.lua"**.
1. Create lua file in `"lua/autorun/"`. You can name it how you want it, just make it unique, for example **"myskin_include.lua"**.
## Code for myskin_include.lua
```
if SERVER then AddCSLuaFile("skins/myskin.lua") else include("skins/myskin.lua") end
```
This file adds our skin to whitelist for server and includes for client on load.
# Reference
SKIN
Table SKIN is a global variable. It's made global to work with <page>GWEN</page>.
<br/>
<page>derma.DefineSkin</page>(skin name, skin description, SKIN table)
```
derma.DefineSkin( "Default", "Made to look like regular VGUI", SKIN )
```
This function will add your skin to list of all Derma skins.
<br/>
<page>derma.GetSkinTable</page>()
```
for k,v in pairs(derma.GetSkinTable()) do print(k) end -- Prints all defined skins
```
<br/>
<page>derma.RefreshSkins</page>()
This function will clear cache from Derma objects and set their skin again.
# Tester snippet
To test out your Derma skin you can use this snippet.
```
concommand.Add( "derma_setskin", function(_,_,args)
if GetConVar("sv_allowcslua"):GetInt() == 0 then return end
for k,v in pairs(vgui.GetWorldPanel():GetChildren()) do
v:SetSkin(args[1])
end
end,nil,"Sets skin for all Derma objects.")
local function deepskin(children,skin)
for k,v in pairs(children) do
v:SetSkin(skin)
if #v:GetChildren() != 0 then deepskin(v:GetChildren(),skin) end
end
end
concommand.Add( "derma_deepsetskin", function(_,_,args)
if GetConVar("sv_allowcslua"):GetInt() == 0 then return end
deepskin(vgui.GetWorldPanel():GetChildren(),args[1])
end,nil,"Forces skin set for all Derma objects and their children.")
concommand.Add( "derma_updateskin", function()
if GetConVar("sv_allowcslua"):GetInt() == 0 then return end
for k,v in pairs(derma.GetSkinTable()) do
if v.GwenTexture then
local tex = v.GwenTexture:GetTexture("$basetexture")
if tex then tex:Download() end
end
end
derma.RefreshSkins()
end,nil,"Updates skins for all Derma objects.")
```
Derma objects can't update themselves because skin that they used is **cached**.
Due to skins are being **cached**, Derma objects can't update themselves while you're editing. So we need to clear cache by typing command **derma_updateskin**. Also its material can't update itself, to fix that, it uses <page>ITexture:Download</page>.
Function **derma_setskin *skin name*** allows you to apply Derma skin on all Derma object through the console. If some panels didn't update use **derma_deepsetskin *skin name***.
For safety, you can't use these functions when **sv_allowcslua is 0**. You can remove this check if you don't forget to remove tester later.
To test your skin you can type **derma_controls** to preview.
# Part One: SKIN variables
SKIN table has a lot of variables. These are for info.
SKIN variables | Information variables |
-------------------------------------|:----:|----
| PrintName | My New Skin | Name for your skin to display |
| Author | Garry Newman | Author of the skin |
| DermaVersion | 1 | Version of the skin |
| GwenTexture | `Material( "gwenskin/GModDefault.png" )` | Texture path of the skin. This is used to draw Derma objects. This is internally used by <page>GWEN</page>. |
Next you will see lots of <page>Global.Color</page> variables. But not all of them are actually visible in game.
This list contains variables which were tested for changes.
SKIN variables | Color variables |
-------------------------------|:------:|-----
| colTextEntryText | `Color( 20, 20, 20 )` | Used for typed text color in <page>DTextEntry</page> and it's derivatives | <image src="DermaSkin_colTextEntryText.png"/> <note>Color can display incorrectly in game.</note> |
| colTextEntryTextHighlight | `Color( 20, 200, 250 )` | Used for highlight color in <page>DTextEntry</page> and it's derivatives | <image src="DermaSkin_colTextEntryTextHighlight.png"/> <note>Color can display incorrectly in game.</note> |
| colTextEntryTextCursor | `Color( 0, 0, 100 )` | Used for cursor color in <page>DTextEntry</page> and it's derivatives | <image src="DermaSkin_colTextEntryTextCursor.png"/> |
| colTextEntryTextPlaceholder | `Color( 128, 128, 128 )` | Used for <page text="SetPlaceholderText">DTextEntry:SetPlaceholderText</page> color in <page>DTextEntry</page> and it's derivatives | <image src="DermaSkin_colTextEntryTextPlaceholder.png"/> <note>Color can display incorrectly in game.</note> |
Column "In game" note: `Right filled square represents real color. Left screenshot represents in-game render.`
Other Color variables doesn't work because Derma objects use texture instead.
<note>ULX uses **SKIN.text_dark** for some labels</note>
# Part Two: UV Texture
## Named Textures
<image src="DermaSkin_UV_Mapping.png"/>
`UV mapping zones. Can be found in .psd file in folder "Overlay".`
<note>Even in UV texture not all of textures are used in GMod!</note>
|List of named layers (From left top to right bottom) |
|------|:------:|------|
| Window (In Focus) | Window - Active | Texture of <page>DFrame</page> when <page text="HasHierarchicalFocus">Panel:HasHierarchicalFocus</page> is **true** |
| Window (Out Focus) | Window - Active (copy) | Texture of <page>DFrame</page> when <page text="HasHierarchicalFocus">Panel:HasHierarchicalFocus</page> is **false** |
| Generic Panel (Only left top is used) | Panels - Generic Panel - Normal | Texture of <page>DPanel</page> by default. This texture can be colored by <page text="SetBackgroundColor">DPanel:SetBackgroundColor</page> |
| Btn Nrml | Buttons - Regular | Texture of <page>DButton</page> when <page text="IsHovered">Panel:IsHovered</page> is **false** and <page text="IsDown">DButton:IsDown</page> is **false** |
| Btn Hvr | Buttons - Hovered | Texture of <page>DButton</page> when <page text="IsHovered">Panel:IsHovered</page> is **true** and <page text="IsDown">DButton:IsDown</page> is **false** |
| Btn Dsbld | Buttons - Dead | Texture of <page>DButton</page> when <page text="IsEnabled">Panel:IsEnabled</page> is **false** |
| Btn Down | Buttons - Down | Texture of <page>DButton</page> when <page text="IsDown">DButton:IsDown</page> is **true** |
| Chk | Buttons - Checkbox - Enabled | Textures of <page>DCheckBox</page> when <page text="IsEnabled">Panel:IsEnabled</page> is **true** |
| Chk Dsbld | Buttons - Checkbox - Disabled | Textures of <page>DCheckBox</page> when <page text="IsEnabled">Panel:IsEnabled</page> is **false** |
| Expnd | Buttons - Tree | Textures of <page>DExpandButton</page> |
| Tree Background | Panels - Tree Panel | Texture of <page>DTree</page>. This texture can be colored by <page text="SetBackgroundColor">DPanel:SetBackgroundColor</page> |
| Mnu rght | Menu - Right Arrow | Texture of Arrow in <page text="AddSubMenu">DMenu:AddSubMenu</page> option <note><page>DMenu</page> doesn't derive panel's skin when called by <page>Global.DermaMenu</page>. So it will be always "Default" skin. Works only when you set skin for it when it's visible or in <page>DMenuBar</page>.</note> |
| Menu Strip | Menu - MenuStrip | Texture of <page>DMenuBar</page> <note><page>DMenu</page> doesn't derive panel's skin when called by <page>Global.DermaMenu</page>. So it will be always "Default" skin. Works only when you set skin for it when it's visible or in <page>DMenuBar</page>.</note> |
| Menu Background | Menu - With Border | Texture of <page>DMenuOption</page> when <page text="GetDrawColumn">DMenu:GetDrawColumn</page> is **true** <note><page>DMenu</page> doesn't derive panel's skin when called by <page>Global.DermaMenu</page>. So it will be always "Default" skin. Works only when you set skin for it when it's visible or in <page>DMenuBar</page>.</note> |
| Menu BG No Border | Menu - Without Border | Texture of <page>DMenuOption</page> when <page text="GetDrawColumn">DMenu:GetDrawColumn</page> is **false** <note><page>DMenu</page> doesn't derive panel's skin when called by <page>Global.DermaMenu</page>. So it will be always "Default" skin. Works only when you set skin for it when it's visible or in <page>DMenuBar</page>.</note> |
| Menu Selected | Menu - Hover | Texture of <page>DMenuOption</page> when <page text="IsHovered">Panel:IsHovered</page> is **true** <note><page>DMenu</page> doesn't derive panel's skin when called by <page>Global.DermaMenu</page>. So it will be always "Default" skin. Works only when you set skin for it when it's visible or in <page>DMenuBar</page>.</note> |
| Text Entry Normal | Text Entry - Normal | Texture of <page>DTextEntry</page> when <page text="IsEditing">DTextEntry:IsEditing</page> is **false** |
| Text Entry Focused | Text Entry - Focus | Texture of <page>DTextEntry</page> when <page text="IsEditing">DTextEntry:IsEditing</page> is **true** |
| Text Entry Disabled | Text Entry - Disabled | Texture of <page>DTextEntry</page> when <page text="IsEnabled">Panel:IsEnabled</page> is **false** |
| Scroller Track V | Scrollbar - Vertical | Texture of <page>DVScrollBar</page> track |
| VScrollbar - Normal | Scrollbar - Vertical | Texture of <page>DVScrollBar</page>**.btnGrip** when <page text="IsHovered">Panel:IsHovered</page> is **false** and <page text="IsDown">DButton:IsDown</page> is **false** |
| VScrollbar - Hovered | Scrollbar - Vertical | Texture of <page>DVScrollBar</page>**.btnGrip** when <page text="IsHovered">Panel:IsHovered</page> is **true** and <page text="IsDown">DButton:IsDown</page> is **false** |
| VScrollbar - Pressed | Scrollbar - Vertical | Texture of <page>DVScrollBar</page>**.btnGrip** when <page text="IsDown">DButton:IsDown</page> is **true** |
| VScrollbar - Disabled | Scrollbar - Vertical | Texture of <page>DVScrollBar</page>**.btnGrip** when <page text="IsEnabled">Panel:IsEnabled</page> is **false** |
| Tab Panel BG | Tabbed - Body | Texture of <page>DPropertySheet</page> where <page text="GetActiveTab">DPropertySheet:GetActiveTab</page> is docked |
| Tab Top | Tabbed - Active | Texture of <page>DTab</page> when <page text="IsActive">DFrame:IsActive</page> is **true** |
| Unfcs Top | Tabbed - Inactive | Texture of <page>DTab</page> when <page text="IsActive">DFrame:IsActive</page> is **false** |
| List Box | ListBox - Background | Texture of <page>DListBox</page>. This class is deprecated, but it's still in game |
<note>This list contains only named textures which are used in GMod! For example, RadioButton is not listed here.</note>
## Unnamed Textures
<image src="DermaSkin_Textures_Numbered.png" alt="500px"/>
| List of unnamed layers |
|----------------------|
| 1 | Texture of <page>DProgress</page> main body |
| 2 | Texture of <page>DProgress</page> progress body |
| 3 | Texture of <page>DFrame</page> outside shadow <bug>Will be used shadow texture from last loaded skin! To fix it change **SKIN** in functions **PaintShadow** and **PaintFrame** to **self**.</bug> |
| 4 | Texture of selection for <page>DTree</page> and <page>DIconBrowser</page> |
| 5 | Texture of <page>DTooltip</page> <bug><page>DTooltip</page> doesn't derive panel's skin. So it will be always "Default" skin. Works only when you set skin for it when it's visible.</bug> |
| 6 | Textures of **default**, **hovered** and **pressed** <page>DSlider</page> knob |
| 7 | Unused. Can be texture of vertical slider |
| 8 | Textures of **default**, **hovered** and **pressed** <page>DNumberWang</page> |
| 9 | Texture of <page>DMenu</page> check icon <note><page>DMenu</page> doesn't derive panel's skin when called by <page>Global.DermaMenu</page>. So it will be always "Default" skin. Works only when you set skin for it when it's visible or in <page>DMenuBar</page>.</note> |
| 10 | Textures of **default**, **hovered** and **pressed** <page>DScrollPanel:GetVBar</page> and <page>DHorizontalDivider</page>. Textures of **default**, **hovered** and **pressed** <page>DComboBox</page> button |
| 11 | Texture of selected <page>DListView_Line</page> |
| 12 | Texture of altered <page>DListView_Line</page>. To separate one line from another, DListView automatically color odd lines to slight dark color. Can be called by internal function <page>DListView_Line:SetAltLine</page> |
| 13 | Unused. Can be textures of alternative colors for <page>DListView_Line</page> |
| 14 | Texture of **hovered** <page>DListView_Line</page> |
| 15 | Unused |
| 16 | Texture of **default** <page>DComboBox</page> |
| 17 | Texture of **hovered** <page>DComboBox</page> |
| 18 | Texture of **pressed** <page>DComboBox</page> |
| 19 | Texture of **disabled** <page>DComboBox</page> |
| 20 | Texture of folded <page>DCategoryList</page> and <page>DCollapsibleCategory</page> |
| 21 | Texture of unfolded <page>DCategoryList</page> and <page>DCollapsibleCategory</page> |
| 22 | Texture of <page>DCategoryList</page> body |
| 23 | Unused |
| 24 | Textures of <page>DFrame</page>**.btnClose** button |
| 25 | Textures of <page>DFrame</page>**.btnMinim** button |
| 26 | Textures of <page>DFrame</page>**.btnMaxim** button |
| 27 | Unused. Can be textures of <page>DFrame</page> "Restore" button |
## Color Pallete Texture
<image src="DermaSkin_Color_Numbered_Table.png"/>
|Color Pallete Table |
|-------------------|
| 1 A | No Color / Not Used |
| 1 B | Color of <page>DFrame</page> title when <page text="HasHierarchicalFocus">Panel:HasHierarchicalFocus</page> is **true** |
| 2 A | No Color / Not Used |
| 2 B | Color of <page>DFrame</page> title when <page text="HasHierarchicalFocus">Panel:HasHierarchicalFocus</page> is **false** |
| 3 A | Color of <page>DButton</page> text when <page text="IsDown">DButton:IsDown</page> is **true** |
| 3 B | Color of <page>DButton</page> default text |
| 4 A | Color of <page>DButton</page> text when <page text="IsEnabled">Panel:IsEnabled</page> is **false** |
| 4 B | Color of <page>DButton</page> text when <page text="IsHovered">Panel:IsHovered</page> is **true** |
| 5 A | Color of <page>DTab</page> text when <page text="IsActive">DFrame:IsActive</page> is **true** and <page text="IsDown">DButton:IsDown</page> is **true** |
| 5 B | Color of <page>DTab</page> default text when <page text="IsActive">DFrame:IsActive</page> is **true** |
| 6 A | Color of <page>DTab</page> text when <page text="IsActive">DFrame:IsActive</page> is **true** and <page text="IsEnabled">Panel:IsEnabled</page> is **false** |
| 6 B | Color of <page>DTab</page> text when <page text="IsActive">DFrame:IsActive</page> is **true** and <page text="IsHovered">Panel:IsHovered</page> is **true** |
| 7 A | Color of <page>DTab</page> text when <page text="IsActive">DFrame:IsActive</page> is **false** and <page text="IsDown">DButton:IsDown</page> is **true** |
| 7 B | Color of <page>DTab</page> default text when <page text="IsActive">DFrame:IsActive</page> is **false** |
| 8 A | Color of <page>DTab</page> text when <page text="IsActive">DFrame:IsActive</page> is **false** and <page text="IsEnabled">Panel:IsEnabled</page> is **false** |
| 8 B | Color of <page>DTab</page> text when <page text="IsActive">DFrame:IsActive</page> is **false** and <page text="IsHovered">Panel:IsHovered</page> is **true** |
| 9 A | Color of <page>DLabel</page> and <page>DListView_Line</page> label when <page text="GetDark">DLabel:GetDark</page> is **true** |
| 9 B | Color of <page>DLabel</page> and <page>DListView_Line</page> label when default |
| 10 A | Color of <page>DLabel</page> and <page>DListView_Line</page> label when <page text="GetHighlight">DLabel:GetDark</page> is **true** |
| 10 B | Color of <page>DLabel</page> and <page>DListView_Line</page> label when <page text="GetBright">DLabel:GetDark</page> is **true** |
| 11 A | Color of <page>DTree_Node_Button</page> text when <page text="IsHovered">Panel:IsHovered</page> is **true** and <page>DForm:ControlHelp</page> text |
| 11 B | Not Used. `Colours.Tree.Lines` |
| 12 A | Color of <page>DTree_Node_Button</page> text when <page text="IsSelected">Panel:IsSelected</page> is **true** |
| 12 B | Color of <page>DTree_Node_Button</page> text when default |
| 13 A | Not Used. `Colours.Properties.Line_Hover` |
| 13 B | Not Used. `Colours.Properties.Line_Normal` |
| 14 A | Color of <page>DProperties</page> category label |
| 14 B | Not Used. `Colours.Properties.Line_Selected` |
| 15 A | Not Used. `Colours.Properties.Column_Hover` |
| 15 B | Not Used. `Colours.Properties.Column_Normal` |
| 16 A | Color of <page>DProperties</page> border between rows and columns |
| 16 B | Color of <page>DProperties</page> cell when row is being edited |
| 17 A | Not Used. `Colours.Properties.Label_Hover` |
| 17 B | Color of <page>DProperties</page> cell label when row isn't being edited |
| 18 A | No Color / Not Used |
| 18 B | Color of <page>DProperties</page> cell label when row is being edited |
| 19 A | Color of <page>DCategoryHeader</page> title when <page text="GetExpanded">DCollapsibleCategory:GetExpanded</page> is **false** |
| 19 B | Not Used. Named as "ModalBackground" |
| 20 A | Color of <page>DCategoryHeader</page> title when <page text="GetExpanded">DCollapsibleCategory:GetExpanded</page> is **true** |
| 20 B | Not Used. Named as "TooltipText" (Real tooltip text uses 27A) |
| 21 A | Color of <page>DCollapsibleCategory:Add</page> button text when **not Atled** and <page text="IsDown">DButton:IsDown</page> is **true** |
| 21 B | Color of <page>DCollapsibleCategory:Add</page> button text when **not Atled** |
| 22 A | Not Used. `Colours.Category.Line.Button` |
| 22 B | Color of <page>DCollapsibleCategory:Add</page> button text when **not Atled** and <page text="IsHovered">Panel:IsHovered</page> is **true** |
| 23 A | Color of <page>DCollapsibleCategory:Add</page> button text when **Atled** |
| 23 B | Not Used. `Colours.Category.Line.Button_Hover` |
| 24 A | Color of <page>DCollapsibleCategory:Add</page> button text when **Atled** and <page text="IsHovered">Panel:IsHovered</page> is **true** |
| 24 B | Not Used. `Colours.Category.Line.Button_Selected` |
| 25 A | Not Used. `Colours.Category.LineAlt.Button_Hover` |
| 25 B | Color of <page>DCollapsibleCategory:Add</page> button text when **not Atled** and <page text="IsDown">DButton:IsDown</page> is **true** |
| 26 A | Not Used. `Colours.Category.LineAlt.Button_Selected` |
| 26 B | Not Used. `Colours.Category.LineAlt.Button` |
| 27 A | Color of <page>DTooltip</page> text. <bug>Currently broken.</bug> |
| 27 B | No Color / Not Used |
# Demo
For tutorial was created a Demo Addon with Windows 10 UI skin for Derma.
You can download it [here](https://steamcommunity.com/sharedfiles/filedetails/?id=1633684835).
Addon uses this hook for painting all Derma objects:
```
hook.Add("ForceDermaSkin","Windows10SkinForce",function()
return "Windows" -- This will paint all Derma objects to new skin
end)
return "Windows" -- This will paint all Derma objects to new skin
end)
```