Install
$ agentstack add skill-jame581-godotprompter-godot-ui ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Godot UI — Control Nodes, Themes & Layout
All examples target Godot 4.3+ with no deprecated APIs. GDScript is shown first, then C#.
> Related skills: responsive-ui for multi-resolution scaling, hud-system for in-game HUD patterns, dialogue-system for dialogue UI presentation, tween-animation for UI transition and animation effects.
1. Control Node Hierarchy
How Control Differs from Node2D
Control is the base class for all UI nodes. It lives in a separate branch of the scene tree from Node2D/Node3D and has a fundamentally different layout model.
| Feature | Node2D | Control | |---|---|---| | Position model | World-space position (pixels from parent) | Anchor + offset relative to parent rect | | Size | No intrinsic size | Has size, minimum_size, custom_minimum_size | | Theme | None | Inherits and overrides Theme resources | | Focus | Not applicable | Built-in focus system (focus_mode, grab_focus()) | | Mouse events | Manual via _input | gui_input, mouse_entered, mouse_exited | | Layout helpers | None | Container subclasses auto-arrange children |
Control as Base Class
Every UI widget (Button, Label, LineEdit, etc.) extends Control. The key properties defined on Control itself are:
anchor_left,anchor_top,anchor_right,anchor_bottom— fractional values (0.0–1.0) relative to the parent's rectoffset_left,offset_top,offset_right,offset_bottom— pixel offsets applied after the anchor is resolvedsize_flags_horizontal,size_flags_vertical— how the node participates inContainerlayouttheme— aThemeresource; ifnullthe node walks up the tree to find the nearest ancestor with onefocus_mode— controls whether the node can receive keyboard/gamepad focus
Place UI nodes inside a CanvasLayer (or directly under the scene root's built-in canvas) so they always render on top of the 3D/2D world and are not affected by Camera transforms.
2. Common Container Nodes
| Container | Purpose | When to Use | |---|---|---| | VBoxContainer | Stacks children vertically, top to bottom | Lists, option rows, vertical menus | | HBoxContainer | Stacks children horizontally, left to right | Toolbars, stat rows, horizontal nav | | GridContainer | Arranges children in a fixed-column grid | Inventory grids, key-binding tables | | MarginContainer | Adds padding around a single child | Wrapping any node to give it breathing room | | PanelContainer | Draws a StyleBox background, then lays out children | Card UI, dialog boxes, HUD panels | | ScrollContainer | Makes its single child scrollable; clips overflow | Long lists, logs, scrollable settings | | TabContainer | Stacks children as named tabs; shows one at a time | Settings screens, multi-section panels |
Sizing tips:
- Set
size_flags_horizontal = SIZE_EXPAND_FILLon children that should fill available space. - Use
custom_minimum_sizeto prevent a child from collapsing to zero. MarginContainerreads margin from the theme propertymargin_*; override at runtime withadd_theme_constant_override("margin_left", 16).
3. Anchors & Margins
How Anchor Presets Work
An anchor is a point on the parent rect expressed as a fraction (0 = top/left edge, 1 = bottom/right edge). Godot resolves the final pixel position of each edge as:
final_left = parent_width * anchor_left + offset_left
final_top = parent_height * anchor_top + offset_top
final_right = parent_width * anchor_right + offset_right
final_bottom = parent_height * anchor_bottom + offset_bottom
The editor exposes built-in presets:
| Preset | Anchor values | Use case | |---|---|---| | Full Rect | L=0, T=0, R=1, B=1 | Overlay / fill parent — most common for root UI | | Center | L=0.5, T=0.5, R=0.5, B=0.5 | Fixed-size widget centred in parent | | Top Left | L=0, T=0, R=0, B=0 | Fixed-size widget pinned to top-left corner | | Top Right | L=1, T=0, R=1, B=0 | Fixed-size widget pinned to top-right corner | | Bottom Center | L=0.5, T=1, R=0.5, B=1 | HUD element anchored to bottom centre |
Setting Anchors in Code
GDScript:
# Fill parent completely (equivalent to "Full Rect" preset)
$Panel.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
# Anchor to top-right corner, fixed 200x60 size
$HUDLabel.set_anchors_and_offsets_preset(Control.PRESET_TOP_RIGHT)
$HUDLabel.size = Vector2(200.0, 60.0)
# Fine-tune with a 16 px margin from the right and top edges
$HUDLabel.offset_right = -16.0
$HUDLabel.offset_top = 16.0
# Custom responsive anchor: right half of screen, full height
$SidePanel.anchor_left = 0.5
$SidePanel.anchor_top = 0.0
$SidePanel.anchor_right = 1.0
$SidePanel.anchor_bottom = 1.0
$SidePanel.offset_left = 0.0
$SidePanel.offset_top = 0.0
$SidePanel.offset_right = 0.0
$SidePanel.offset_bottom = 0.0
C#:
// Fill parent completely
GetNode("Panel").SetAnchorsAndOffsetsPreset(Control.LayoutPreset.FullRect);
// Anchor to top-right corner
var label = GetNode("HUDLabel");
label.SetAnchorsAndOffsetsPreset(Control.LayoutPreset.TopRight);
label.Size = new Vector2(200f, 60f);
label.OffsetRight = -16f;
label.OffsetTop = 16f;
// Custom anchor: right half of screen
var panel = GetNode("SidePanel");
panel.AnchorLeft = 0.5f;
panel.AnchorTop = 0.0f;
panel.AnchorRight = 1.0f;
panel.AnchorBottom = 1.0f;
panel.OffsetLeft = 0f;
panel.OffsetTop = 0f;
panel.OffsetRight = 0f;
panel.OffsetBottom = 0f;
Anchor vs Offset
- Anchor determines where on the parent the node's edges track. It is resolution-independent.
- Offset is a fixed pixel value added after the anchor. It does not scale with the parent.
For fully responsive layout, keep offsets at 0 and let anchors do the work. Add small fixed offsets only for cosmetic margins (e.g., a 16 px gutter from an edge).
4. Theme System
A Theme resource centralizes fonts, colors, and StyleBoxes. Apply at the root and let inheritance do the work; use theme_override_* only for one-off tweaks. StyleBoxFlat covers most flat-design needs (bg_color, border_color, corner_radius, border_width); StyleBoxTexture for textured backgrounds.
> See [references/theme-system.md](references/theme-system.md) for the full Theme resource creation walk-through, StyleBoxFlat properties, font overrides, theme inheritance rules, and per-node theme_override_* methods.
5. Focus & Navigation
Focus modes (FOCUS_NONE, FOCUS_CLICK, FOCUS_ALL) gate keyboard/gamepad navigation. Use focus_neighbor_top / _bottom / _left / _right to wire navigation chains, or rely on automatic spatial detection. Call grab_focus() on the first interactive element when a menu opens.
> See [references/focus-and-navigation.md](references/focus-and-navigation.md) for focus mode details, focus_neighbor chain examples, gamepad/keyboard input handling, and grab_focus patterns.
6. Common UI Patterns
Three canonical scenes: a main menu (centered VBoxContainer with title + button list), a settings screen with tabs (TabContainer + child panels per category), and a pause menu overlay (full-rect ColorRect background + centered options panel, paused via get_tree().paused = true).
> See [references/ui-patterns.md](references/ui-patterns.md) for the full scene-tree fragments and GDScript wiring for each pattern.
7. Signals
Button.pressed for clicks, Control.gui_input for raw events on a node, Control.mouse_entered / mouse_exited for hover. Connect in _ready() or via the Inspector's Node panel.
> See [references/signals.md](references/signals.md) for the complete signal catalog and signal-driven UI update patterns.
8. FoldableContainer (Godot 4.5+)
FoldableContainer is a new built-in Container node introduced in Godot 4.5. It provides accordion-style collapsible sections with a toggle header, eliminating the boilerplate of manually wiring a Button to show/hide a child VBoxContainer.
Basic Usage
# In a script that builds UI dynamically:
func _ready() -> void:
var foldable := FoldableContainer.new()
foldable.title = "Advanced Settings"
foldable.folded = false # start expanded
var label := Label.new()
label.text = "This content can be collapsed."
foldable.add_child(label)
var slider := HSlider.new()
slider.min_value = 0.0
slider.max_value = 1.0
slider.value = 0.5
foldable.add_child(slider)
add_child(foldable)
# Listen for toggle events:
func _ready() -> void:
var foldable := $FoldableContainer
foldable.folding_changed.connect(_on_section_toggled)
func _on_section_toggled(is_folded: bool) -> void:
print("Section is now: ", "folded" if is_folded else "expanded")
public override void _Ready()
{
var foldable = new FoldableContainer
{
Title = "Advanced Settings",
Folded = false
};
var label = new Label { Text = "This content can be collapsed." };
foldable.AddChild(label);
var slider = new HSlider { MinValue = 0.0, MaxValue = 1.0, Value = 0.5 };
foldable.AddChild(slider);
AddChild(foldable);
// Listen for toggle:
foldable.FoldingChanged += OnSectionToggled;
}
private void OnSectionToggled(bool isFolded)
{
GD.Print("Section is now: ", isFolded ? "folded" : "expanded");
}
Key Properties
| Property | Type | Purpose | |----------|------|---------| | title | String | Text shown in the toggle header | | folded | bool | true = content hidden, false = content visible | | title_alignment | HorizontalAlignment | Align the title text within the header |
Signal
| Signal | Signature | When emitted | |--------|-----------|--------------| | folding_changed | (folded: bool) | Emitted whenever the fold state toggles |
> Replaces boilerplate: Prior to Godot 4.5, accordion sections required a Button + VBoxContainer + signal connection. FoldableContainer handles all of this in one node.
9. Stacked Label Effects (Godot 4.5+)
In Godot 4.5, Label and RichTextLabel support multiple layered text effects simultaneously — for example, stacking two outline effects at different widths and colors, or combining a shadow with a glow. Previously, achieving multiple outline layers required duplicating Label nodes and layering them manually.
# In the inspector: Label → Theme Overrides → Constants
# Add multiple outline layers by stacking VisualShaderNodeTextureParameter entries
# in the Theme, or configure via add_theme_* overrides at runtime.
# Example: thick outer outline + thin inner outline via theme overrides
func apply_stacked_outlines(label: Label) -> void:
# Outer outline — wide, dark
label.add_theme_constant_override("outline_size", 6)
label.add_theme_color_override("font_outline_color", Color(0.0, 0.0, 0.0, 0.9))
# Shadow (counts as a second layered effect)
label.add_theme_constant_override("shadow_offset_x", 2)
label.add_theme_constant_override("shadow_offset_y", 2)
label.add_theme_color_override("font_shadow_color", Color(0.0, 0.0, 0.0, 0.5))
public void ApplyStackedOutlines(Label label)
{
// Outer outline — wide, dark
label.AddThemeConstantOverride("outline_size", 6);
label.AddThemeColorOverride("font_outline_color", new Color(0f, 0f, 0f, 0.9f));
// Shadow (second layered effect)
label.AddThemeConstantOverride("shadow_offset_x", 2);
label.AddThemeConstantOverride("shadow_offset_y", 2);
label.AddThemeColorOverride("font_shadow_color", new Color(0f, 0f, 0f, 0.5f));
}
For RichTextLabel, stacked effects can also be applied using BBCode in combination with theme overrides:
# RichTextLabel with multiple outline-style effects via BBCode + theme
$RichTextLabel.text = "[outline size=4 color=#000000]Level Up![/outline]"
# Additional layers are set via theme overrides on the node as above.
> Editor workflow: Stacked effects are most easily configured via Theme Editor → Label → Constants or by adding multiple FontFile-style outline passes in the Font resource. The runtime API (add_theme_*_override) above works for dynamic scenarios.
10. Checklist
- [ ] Root UI
Controlhas anchor preset Full Rect (or appropriate preset for the layout) - [ ] All interactive widgets (
Button,LineEdit,Slider) havefocus_mode = FOCUS_ALL - [ ] Decorative nodes (
Label,TextureRect) havefocus_mode = FOCUS_NONE - [ ] Focus neighbours are wired for non-linear layouts so gamepad navigation wraps correctly
- [ ]
grab_focus()called on the first interactive widget in_ready()for each screen - [ ] Pause menu root
Controlhasprocess_mode = PROCESS_MODE_ALWAYS - [ ] One
Themeresource assigned at the screen root — not duplicated on every child - [ ]
StyleBoxFlatused instead of image assets for simple solid-colour panels - [ ]
add_theme_*_override()used for per-node overrides rather than assigning a whole newTheme - [ ] Containers (
VBoxContainer,HBoxContainer, etc.) used for layout instead of manualpositionvalues - [ ]
custom_minimum_sizeset on widgets that must not collapse to zero - [ ] Slider and volume code uses
linear_to_db/db_to_linear— not raw linear values mapped to audio bus - [ ] Signals connected in
_ready()(or via the editor); no polling of UI state in_process - [ ] Tab order in
TabContainermatches logical reading / navigation order - [ ] Accordion-style collapsible panels use
FoldableContainerinstead of manual Button + VBoxContainer wiring (Godot 4.5+) - [ ] Multiple outline/shadow layers on
Label/RichTextLabeluse stacked theme overrides instead of duplicated nodes (Godot 4.5+)
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: jame581
- Source: jame581/GodotPrompter
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.