S&box Wiki
Home
/
Edit Guide to Widgets
View
Edit
History
No Category
Developer Overview
The Project System
Publishing To Asset Party
Getting Started With Hammer
Mapping Basics
Mapping Entities
Advanced Mapping Techniques
Getting Started with Modeldoc
Animgraph & Animation
Physics
Modeldoc Nodes
Advanced Modelling
UI Basics
Styles & Stylesheets
Razor Templates
Game Menus
Materials
Built In Shaders
Shaders
Shader Reference
Sounds & Audio
Particles
Getting Started
Making Games
Input
Networking
Physics
Rendering
Editor & Tools
VR
Misc
Playing Guides
Console Commands & Variables
Dedicated Server
Log in to edit
Guide to Widgets
<cat>Code.Editor</cat> <title>Guide to Widgets</title> # A beginners guide to Widgets Editor UIs are built entirely out of widgets. Widgets are different from [ASP.NET Razor](ui-razor), which is used for the in-game UI. Widgets are available in the Editor namespace, thus you should only make them from a [tool or editor project](Creating_Editor_Tools). Widgets can be various elements or components, such as buttons, text boxes, trees, or images. Children widgets are the smaller widgets that are contained within a larger widget, known as the parent widget. Imagine a parent widget as a container, like a box, and the children widgets are like smaller objects placed inside that box. If a widget does not have a parent, it is a root widget. This widget will act as a window on the user's operating system. # Creating and opening a window There are four primary ways to do so: 1. Using the [Dock attribute](https://asset.party/api/Editor.DockAttribute) on a class that inherits [Widget](https://asset.party/api/Editor.Widget) at some level. 2. Implementing the [IAssetEditor interface](https://asset.party/api/Editor.IAssetEditor) and opening an asset with the extension specified on the class' [EditorForAssetType attribute](https://asset.party/api/Editor.EditorForAssetTypeAttribute). 3. Manually creating the widget with the Parent field set to `null`, typically in a static method with the [Menu attribute](https://asset.party/api/Editor.MenuAttribute). 4. Using the [Tool attribute](h) on a class that inherits [Widget](https://asset.party/api/Editor.Widget) at some level. ## Option 1 - Widget with Dock attribute This will create widgets that behave like the Asset Browser, Projects list, Console feed, etc. ```cs using Editor; namespace MyToolProject; //never forget your namespace [Dock("Editor", "Example Editor Dock", "local_fire_department")] public class DockAttrExample : Widget { public DockAttrExample(Widget parent) : base(parent, false) { //the constructor's parameters must have a widget and nothing else //otherwise the attribute will fail to find a constructor to use when creating the widget //create your child widgets } } ``` The Dock attribute has three parameters 1. `target` This is the [DockWindow](https://asset.party/api/Editor.DockWindow) registered with the attribute that the widget should show up in. `Editor` and `Hammer` are the only known options. 2. `name` What should be displayed when listing available docks. 3. `icon` (optional) Identifier of the icon to use. The dock can then be opened from the associated window's list. For `Editor`: <upload src="8f39a/8dbc7a4c911f556.png" size="15179" name="sbox-dev_gYojMiKVH2.png" /> For `Hammer`: <upload src="8f39a/8dbc7a4f928920d.png" size="34001" name="sbox-dev_glPOyG8aAl.png" /> ## Option 2 - Asset Editor ```cs using Editor; namespace MyToolProject; //never forget your namespace [EditorForAssetType("8letters")] //cannot be more than 8 characters, will need to be a registered asset public class AssetEditorExample : Widget, IAssetEditor { //return false if you want to have a widget created for each asset //return true if you want only one widget to be made public bool CanOpenMultipleAssets => true; public AssetEditorExample(Widget parent, bool isDarkWindow = false) : base(parent, isDarkWindow) { //create your child widgets } public void AssetOpen(Asset asset) { //called every time an asset is opened in the asset browser (only for the type specified in EditorForAssetType) //do something with the asset, like call LoadResource //save your asset by calling SaveToDisk on the asset with the resource //if CanOpenMultipleAssets returns true, //you should refocus this widget Focus(); } } ``` The [EditorForAssetType attribute](Editor.EditorForAssetTypeAttribute) should have the extension of your [custom asset type](Custom_Asset_Types). When the an asset is double clicked in the Asset Browser (or the edit button on the inspector) the widget will be created and `AssetOpen` will be called with the asset. You may need to load ## Option 3 - Manually Not recommended for most use cases, but still an option. ```cs using Editor; namespace MyToolProject; //never forget your namespace public class WidgetExample : Widget { public WidgetExample(Widget parent, bool isDarkWindow = false) : base(parent, isDarkWindow) { //create children widgets //if you don't call this, you essentiall create a memory leak Show(); } //methods [Menu("Editor", "Example Code/Widget Example")] public static void OpenExample() { WidgetExample widget = new(null); } } ``` If the constructor doesn't call `Show`, then you will need to call it somewhere after constructing. This only needs to be done for widgets that don't have a parent. The static method with the [Menu attribute](https://asset.party/api/Editor.MenuAttribute) creates an entry in the Editor's tool bar. <upload src="8f39a/8dbc7aa50929051.png" size="13224" name="sbox-dev_drdnpJjEiz.png" /> ## Option 4 - Widget with Tool attribute Does not have to be a DockWindow, just has to inherit Widget. This is typically used for larger tools with many features and widgets. ```cs using Editor; namespace MyToolProject; //never forget your namespace [Tool("Full Scale Tool", "local_fire_department", "Example of a large tool with lots of widgets")] public class ToolAttrExample : DockWindow { public ToolAttrExample() : base() { //you must have an anonymous constructor //otherwise the attribute will fail to find a constructor to use when creating the widget //create your child widgets } } ``` # Example Window Let's create a widget that tests our user's *Half-Life: 2* knowledge. ```cs using Editor; using System; using System.Collections.Generic; namespace MyToolProject; //never forget your namespace, choose a more appropriate one than this though public class HalfLife2Exam : Widget { public HalfLife2Exam(Widget parent, bool isDarkWindow = false) : base(parent, isDarkWindow) { //set to false if you want the widget to be hidden instead //only do this if you will re-open the widget instead of creating a new one DeleteOnClose = true; //these sizes will get scaled by the operating system's window scaling MinimumSize = new(256, 256); Size = new(384, 512); //Name is used for the widget debugger, and DockWindow widgets //it is optional Name = "HL2Exam"; //also optional, but highly recommended if you are making your widget as a window WindowTitle = "Exam - Half-Life: 2"; //also optional, but looks much better than the operating system's default SetWindowIcon("edit_note"); //a Layout controls the positioning and sizing of widgets added to it //it's not necessary, but makes things a LOT easier Layout = Layout.Column(); //we will write more here //leave this at the bottom Show(); } [Menu("Editor", "Exams/Half-Life: 2")] public static void OpenExam() { HalfLife2Exam _ = new(null); } } ``` Using the Editor's tool bar we can open the window <upload src="8f39a/8dbc7ac1f241e31.png" size="3288" name="sbox-dev_xWOFyc4Q9y.png" /> Let's start adding a few widgets to ask questions and take answers. First question we will ask, is for the name of the character you play as. ```cs Layout.Add(new Label("1. What is the name of the character you play as?", this)); //single line text input GordonsName = Layout.Add(new LineEdit(this) { PlaceholderText = "Firstname Lastname" }); ``` If we re-open our exam, we can now see the label and text input. <upload src="8f39a/8dbc7acf0f7988f.png" size="8425" name="sbox-dev_YHrZshWzTI.png" /> It's nice to see things working, but the spacing is ugly. We can fix this by adding a stretch cell to occupy the extra space in one spot. ```cs Layout.AddStretchCell(); ``` This will bunch the label and text input together <upload src="8f39a/8dbc7ad512e8ad5.png" size="8459" name="sbox-dev_VIOWf8ouPo.png" /> Changing when the stretch cell is added to the layout, you can spread the widgets out differently. This is what happens when you add the stretch cell after the label, but before the text input <upload src="8f39a/8dbc7ad893bad5d.png" size="8458" name="sbox-dev_jzu3bGhmc6.png" /> For now, let's make the stretch cell as the last member of the layout. Next, let's ask what weapons the player can use in Half-Life 2 using a list of check boxes ```cs //code ... public class HalfLife2Exam : Widget { private LineEdit GordonsName; private List<CheckBox> CorrectCheckBoxes = new(); private List<CheckBox> WrongCheckBoxes = new(); public HalfLife2Exam(Widget parent, bool isDarkWindow = false) : base(parent, isDarkWindow) { //code ... Layout.Add(new Label("2. Which of these weapons can players use in Half-Life: 2?", this)); //create the check boxes and put them into a list of correct and incorrect //we will evaluate these later CorrectCheckBoxes.Add(Layout.Add(new CheckBox("SMG", this))); CorrectCheckBoxes.Add(Layout.Add(new CheckBox("Crowbar", this))); WrongCheckBoxes.Add(Layout.Add(new CheckBox("AK-47", this))); WrongCheckBoxes.Add(Layout.Add(new CheckBox("M4A1", this))); CorrectCheckBoxes.Add(Layout.Add(new CheckBox("AR2", this))); //fill up space after all our widgets Layout.AddStretchCell(); //leave this at the bottom Show(); } //code ... } ``` Notice we store the check boxes into a list. We will need to access to the check boxes later when grading the test. The check boxes work, but there should be spacing between question 1 and 2. We can solve this using the layout ```cs Layout.AddSpacingCell(16); ``` Add this between the text input and the question 2 label, and a 16 pixel (scaled by operating system) gap will now appear between the two questions <upload src="8f39a/8dbc7b078b8e6fb.png" size="5886" name="sbox-dev_NazSyZz9hL.png" /> This test is already quite big with 2 whole questions, so let's wrap it up by adding a submit button ```cs Layout.AddSpacingCell(16); Layout.Add(new Button("Submit", this) { Clicked = ExamSubmitted }); ``` and a method to grade the exam when it's clicked ```cs public void ExamSubmitted() { /* grade exam however you wamt */ float score = 75f; PopupWindow popup = new("Exam results", $"You scored {score}%"); popup.Show(); } ``` The final code should look something like ```cs using Editor; using System; using System.Collections.Generic; namespace MyToolProject; //never forget your namespace, choose a more appropriate one than this though public class HalfLife2Exam : Widget { private LineEdit GordonsName; private List<CheckBox> CorrectCheckBoxes = new(); private List<CheckBox> WrongCheckBoxes = new(); public HalfLife2Exam(Widget parent, bool isDarkWindow = false) : base(parent, isDarkWindow) { //set to false if you want the widget to be hidden instead //only do this if you will re-open the widget instead of creating a new one DeleteOnClose = true; //these sizes will get scaled by the operating system's window scaling MinimumSize = new(256, 256); Size = new(384, 512); //Name is used for the widget debugger, and DockWindow widgets //it is optional Name = "HL2Exam"; //also optional, but highly recommended if you are making your widget as a window WindowTitle = "Exam - Half-Life: 2"; //also optional, but looks much better than the operating system's default SetWindowIcon("edit_note"); //a Layout controls the positioning and sizing of widgets added to it //it's not necessary, but makes things a LOT easier Layout = Layout.Column(); Layout.Add(new Label("1. What is the name of the character you play as?", this)); //single line text input GordonsName = Layout.Add(new LineEdit(this) { PlaceholderText = "Firstname Lastname" }); Layout.AddSpacingCell(16); Layout.Add(new Label("2. Which of these weapons can players use in Half-Life: 2?", this)); //create the check boxes and put them into a list of correct and incorrect //we will evaluate these later CorrectCheckBoxes.Add(Layout.Add(new CheckBox("SMG", this))); CorrectCheckBoxes.Add(Layout.Add(new CheckBox("Crowbar", this))); WrongCheckBoxes.Add(Layout.Add(new CheckBox("AK-47", this))); WrongCheckBoxes.Add(Layout.Add(new CheckBox("M4A1", this))); CorrectCheckBoxes.Add(Layout.Add(new CheckBox("AR2", this))); Layout.AddSpacingCell(16); Layout.Add(new Button("Submit", this) { Clicked = ExamSubmitted }); //fill up space after all our widgets Layout.AddStretchCell(); //leave this at the bottom Show(); } public void ExamSubmitted() { int points = 0; int maximumPoints = 1; //remove capitalization, and trim whitespace if (GordonsName.Value.ToLower().Trim() == "gordon freeman") points++; foreach (CheckBox checkbox in CorrectCheckBoxes) { maximumPoints++; if (checkbox.Value) points++; } foreach (CheckBox checkbox in WrongCheckBoxes) { maximumPoints++; if (!checkbox.Value) points++; } float score = MathF.Round(points / (float) maximumPoints * 100f); PopupWindow popup = new("Exam results", $"You scored {score}%"); popup.Show(); } [Menu("Editor", "Exams/Half-Life: 2")] public static void OpenExam() { HalfLife2Exam _ = new(null); } } ``` Fill out the exam <upload src="8f39a/8dbc7b4cdbb6a51.png" size="14180" name="sbox-dev_DHmeBe6p4W.png" /> and click submit <upload src="8f39a/8dbc7b4e04bff89.png" size="4927" name="sbox-dev_pbrvvCMbFM.png" /> # Dock Windows Read <page>Widget Docking</page> for guidance
S&box Wiki
Development
Developer Overview
6
Editor Overview
General FAQ
System Requirements
The s&box wiki
Troubleshooting
Useful Links
The Project System
4
Adding Assets
Creating a Game Project
Project Settings Window - Games
Project Types
Publishing To Asset Party
2
Uploading assets
Uploading projects
Hammer
Getting Started With Hammer
3
Getting Started With Hammer
Making Your First Map
Mapping Resources
Mapping Basics
7
Cordons
Hotspot Materials
Selection Sets
Standard Mapping Dimensions
Tool Materials
Tools Visualisation Modes
Using Entities That Require a Mesh
Mapping Entities
2
Creating a Door
Light Entities
Advanced Mapping Techniques
8
Collaborating With Prefabs and Git
Instances
Prefabs
Quixel Bridge Plugin
Tilesets
Tilesets-Advanced
Tilesets-Proxies
VIS Optimizations
Models & Animation
Getting Started with Modeldoc
7
Automatic Model Setup
Breakpieces
Creating a Model
Guide to Models
Importing Rust Weapons
LODs
ModelDoc FAQ & best practices
Animgraph & Animation
4
Animations without Animgraph
AnimEvents, AnimGraph Tags, Attachments
Animgraph
Delta Animations
Physics
3
Cloth Physics
Collisions, Physics & Surface Types
Jiggle Bones
Modeldoc Nodes
1
Custom ModelDoc nodes
Advanced Modelling
6
Bodygroups
Citizen
First Person
IKChains and Stride Retargeting
Morphs
Vertex Normals
User Interface
UI Basics
6
Custom Fonts
Embedding Websites
Enabling Pointer Events
Events and Input
UI Basics
UI with Components
Styles & Stylesheets
1
Video Backgrounds
Razor Templates
4
A Razor Overview
Aliases and SetProperty Attributes
Generic Components
Templates
Game Menus
1
Making a Custom Pause Screen
Materials & Shaders
Materials
5
Guide to Materials
Material Attributes
Material Resources
Texture Settings
Using Dynamic Expressions
Built In Shaders
2
Foliage Shader
Glass Shader
Shaders
4
Compute Shaders
Constant Buffers
Material API
Shading Model
Shader Reference
5
Anatomy of Shader Files
Getting rid of Tex2D macros
Shader Reference
Shader States
Texture Format Cheat-Sheet
Other Assets
Sounds & Audio
4
Guide to Sounds
Sound Events
Sound Occlusion
Soundscapes
Particles
5
Creating animated sprites
Creating your first particle effect
Understanding Particle Editor
Using custom sprites
Using particle systems from C#
Coding
Getting Started
5
Cheat Sheet
Learning Resources
Setting up Rider
Setting up Visual Studio
Setting up Visual Studio Code
Making Games
2
Components
GameObjects
Input
4
Commands
ConVars
Input System
Speech Recognition
Networking
7
Auth Tokens
Http Requests
Lobby System
Networked Types
Networking Basics
RPCs
WebSockets
Physics
5
Collisions
Hitboxes
Joints
Traces
Triggers
Rendering
3
Render Tags
RenderHooks
Scenes
Editor & Tools
5
Guide to Widgets
Hammer API
Hammer Gizmos
Hotload Performance
Widget Docking
VR
3
Getting Started
VR Input
VR Overlays
Misc
10
Asset Types
Attributes and Component Properties
Backend API
Code Accesslist
CPU Performance Profiling
DisplayInfo
package/find
Setting Up A Navigation Mesh
Threaded Tasks
TypeLibrary
Playing
Playing Guides
3
Default Keybinds
Proton
s&box on macOS (Experimental)
Console Commands & Variables
1
Launch Arguments
Dedicated Server
1
Dedicated Servers