Skills/godot-ui/SKILL.md

8.2 KiB

name description license compatibility metadata
godot-ui UI systems for Godot 4.x: Control node hierarchy, containers and layout modes (Anchor, Margin, Center, Fill, Expand), theme resources, custom styles, RichTextLabel formatting, PopupMenu search, and control offset transforms. Use when building menus, HUDs, inventory screens, or any UI in Godot. MIT
godot-4.0
godot-4.1
godot-4.2
godot-4.3
godot-4.4
godot-4.5
godot-4.6
godot-4.7
author version tags created
vl4dt 0.1.0
godot
ui
control
container
theme
rich-text
2026-06-20

UI Systems for Godot 4.x

Comprehensive patterns for user interfaces, menus, and HUD elements in Godot 4.x.

Control Node Hierarchy and Containers

Container Types

Godot 4.x provides several container nodes for responsive UI layouts.

# Vertical layout
var vbox = VBoxContainer.new()
vbox.set_anchors_preset(Control.PRESET_FULL_RECT)
add_child(vbox)

# Horizontal layout
var hbox = HBoxContainer.new()
hbox.add_child(Button.new())
hbox.add_child(Button.new())
vbox.add_child(hbox)

# Grid layout
var grid = GridContainer.new()
grid.columns = 3
for i in range(9):
    var btn = Button.new()
    btn.text = str(i)
    grid.add_child(btn)
vbox.add_child(grid)

Anchor Presets

Use anchor presets for responsive positioning.

# Top-left corner, stays fixed
button.set_anchors_preset(Control.PRESET_TOP_LEFT)

# Full screen (fills parent)
panel.set_anchors_preset(Control.PRESET_FULL_RECT)

# Centered with margins
label.set_anchors_preset(Control.PRESET_CENTER)
label.offset_left = -100
label.offset_right = 100

Layout Modes (Anchor, Margin, Center, Fill, Expand)

Anchor Mode

Control how a node positions itself relative to its parent.

# Manual anchor setup
control.anchor_right = 1.0   # Right edge sticks to parent right
control.offset_right = -20   # But 20px from the edge
control.anchor_bottom = 1.0
control.offset_bottom = -20

Margin Container

Add padding around a child node.

var margin = MarginContainer.new()
margin.add_theme_constant_override("margin_left", 16)
margin.add_theme_constant_override("margin_top", 16)
margin.add_theme_constant_override("margin_right", 16)
margin.add_theme_constant_override("margin_bottom", 16)
margin.add_child(content_panel)

Center Container

Center a child node within its parent.

var center = CenterContainer.new()
center.horizontal_alignment = CenterContainer.ALIGNMENT_CENTER
center.vertical_alignment = CenterContainer.ALIGNMENT_MIDDLE
center.add_child(dialog_panel)

Fill and Expand

# Fill available space
button.expand_mode = Button.BUTTON_EXPAND_FILL  # Fill width and height
button.expand_mode = Button.BUTTON_EXPAND       # Fill width only

# Control fills parent rect
control.size_flags_horizontal = Control.SIZE_FILL
control.size_flags_vertical = Control.SIZE_FILL

Theme Resources and Custom Styles

Creating a Custom Theme

# Create theme in code
var theme = Theme.new()

# Custom font
var custom_font = FontFile.new()
custom_font.font_data = load("res://fonts/my_font.tres")
theme.add_font("Default", "Source", custom_font)
theme.default_font_size = 16

# Custom button style
var normal_style = StyleBoxTexture.new()
normal_style.texture = load("res://ui/button_normal.png")
normal_style.set_margin_left(8)
normal_style.set_margin_right(8)
normal_style.set_margin_top(4)
normal_style.set_margin_bottom(4)

var hover_style = normal_style.duplicate()
hover_style.modulate = Color(1.2, 1.2, 1.2)  # Lighter on hover

theme.add_stylebox("normal", "Button", normal_style)
theme.add_stylebox("hover", "Button", hover_style)

Theme Overrides per Control

# Override theme on individual controls
button.add_theme_color_override("font_color", Color.WHITE)
button.add_theme_font_size_override("font_size", 20)
button.add_theme_stylebox_override("normal", custom_style)

# Apply theme to entire scene tree
add_child(button)
button.theme = theme

Theme Variation Pattern

# Create themed button scenes for consistency
# ui_button_primary.tscn, ui_button_secondary.tscn, etc.
const PRIMARY_BTN = preload("res://ui/button_primary.tscn")
const SECONDARY_BTN = preload("res://ui/button_secondary.tscn")

func create_primary_button(text: String) -> Button:
    var btn = PRIMARY_BTN.instantiate()
    btn.text = text
    return btn

RichTextLabel Formatting and Image Sizing

RichTextEffect Custom Tags

Godot 4.x supports custom rich text effects.

# Use built-in BBCode tags in RichTextLabel
rich_text_label.text = "[color=red]Alert![/color] Health is low."
rich_text_label.text = "[b]Bold[/b] and [i]italic[/i] text"
rich_text_label.text = "[img=64]res://icons/heart.png[/img] 10/20 HP"

# Custom effect for damage numbers
func _process(delta: float):
    # RichTextLabel processes BBCode every frame
    # For performance, cache formatted text when possible

Image Sizing in RichTextLabel

# Fixed size images
rich_text_label.text = "[img=32]res://icons/coin.png[/img] [img=32]res://icons/coin.png[/img]"

# Scale by font size
rich_text_label.text = "[font_size=48][img=48]res://icons/star.png[/img][/font_size]"

# Responsive sizing using theme font
func get_icon_size() -> int:
    return ThemeDB.get_font("Default").get_height() * 2

Godot 4.7 UI Features

PopupMenu Search (Godot 4.7)

Godot 4.7 adds search/filtering to PopupMenu for better UX in large menus.

# The PopupMenu now supports search filtering out of the box
# Connect the search_changed signal for custom filtering
@onready var popup_menu = $PopupMenu
@onready var search_line_edit = $SearchField

func _ready():
    search_line_edit.text_changed.connect(_on_search_changed)
    popup_menu.index_pressed.connect(_on_menu_selected)

func _on_search_changed(query: String):
    # Filter menu items based on search query
    for i in range(popup_menu.item_count):
        var item_text = popup_menu.get_item_text(i)
        popup_menu.set_item_hidden(i, not item_text.containsn(query))

Control Offset Transforms (Godot 4.7)

Godot 4.7 allows animating control offsets directly for smooth UI transitions.

# Animate panel sliding in using offset transforms
func slide_in(panel: Control):
    panel.offset_left = -300  # Start off-screen
    var tween = create_tween()
    tween.tween_property(panel, "offset_left", 0, 0.3)
    tween.set_ease(Tween.EASE_OUT)

func slide_out(panel: Control):
    var tween = create_tween()
    tween.tween_property(panel, "offset_left", -300, 0.25)
    tween.connect("finished", panel.queue_free)

# Use for: sliding menus, notifications, dialog entrances

UI Performance Tips

  • Use Container nodes instead of manually positioning controls
  • Prefer Anchor presets over manual offset adjustments when possible
  • Cache RichTextLabel formatted text when content does not change
  • Use TextureRect with STRETCH_SCALE for responsive images
  • Avoid creating/destroying UI nodes every frame — use pooling instead
  • Set visible_ratio to 0 on off-screen panels instead of queue_free()

Quick Reference: Container Nodes

Container Layout
HBoxContainer Horizontal row
VBoxContainer Vertical column
GridContainer Grid with fixed columns
CenterContainer Centered child
MarginContainer Padded child
ScrollContainer Scrollable area
AspectRatioContainer Fixed aspect ratio
Control (root) Full rect anchor

MCP Bridge Tools (Optional — Live Editor Integration)

When the MCP bridge is running (Phase 2), these tools enhance UI debugging:

  • ui_inspect — Inspect the current control tree and their properties
  • ui_preview_theme — Preview theme changes in real-time
  • ui_resize_preview — Test responsive layouts at different screen sizes
  • ui_find_control — Find controls by path or name in the scene tree

Note: MCP tools require the Godot editor to be running with the MCP plugin enabled. Skills work independently without the bridge.

References