Fluent Controls
A thin typed wrapper around Godot Control nodes and style resources for constructing UI from GDScript with a fluent API.
var root: Control = UI.control()\
.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)\
.children([
UI.label().text("Inventory"),
UI.button()
.text("Close")
.tap(func(button: Button): close_button = button),
])\
.unwrap()
Installation
Copy addons/fluent_controls into the addons directory of a Godot 4.7 project and enable FluentControls under Project Settings > Plugins.
If the global UI name conflicts with another script class in the consuming project, rename class_name UI near the top of addons/fluent_controls/ui.gd.
The repository can also be checked out as a Git submodule inside the consuming project. The distributable addon remains entirely contained in addons/fluent_controls.
Usage
UI.button(), UI.label(), and the other constructors expose the corresponding Godot objects through a fluent API.
var button: Button = UI.button()\
.text("Play")\
.disabled(false)\
.unwrap()
In addition to APIs corresponding to Godot properties and methods, Fluent Controls provides a small set of addon-specific APIs.
children()
Adds multiple child Control nodes inside the chain.
var menu := UI.vbox_container()\
.children([
UI.label().text("Menu"),
UI.button().text("Play"),
UI.button().text("Quit"),
])\
.unwrap()
tap()
Accesses the wrapped Godot object without ending the chain.
This is useful for storing references, connecting signals, or using regular Godot APIs directly.
var close_button: Button
var panel := UI.panel_container()\
.children([
UI.button()
.text("Close")
.tap(func(button: Button):
close_button = button
button.pressed.connect(close_menu)
),
])\
.unwrap()
unwrap()
Returns the wrapped Godot object.
var label: Label = UI.label()\
.text("Hello")\
.unwrap()
set_property()
Sets a property using a Godot property path, including indexed property paths.
UI.button()\
.set_property("theme_override_constants/outline_size", 2)
This is useful for properties that are inconvenient to access through the regular API.
Existing Controls
An existing Control object can also be passed in, such as UI.button(button).
var button := Button.new()
UI.button(button)\
.text("Continue")\
.disabled(false)
This makes it possible to use the same fluent API with nodes from an existing scene or controls created elsewhere.
Composing Controls
children(), add_child_to(), and add_children_to() can be used to build Control hierarchies.
var root := UI.control()
UI.label()\
.text("Inventory")\
.add_child_to(root)
UI.vbox_container()\
.children([
UI.button().text("Use"),
UI.button().text("Drop"),
])\
.add_child_to(root)
Supported Classes
The following classes are currently supported:
ControlButtonCheckBoxCheckButtonOptionButtonLabelRichTextLabelLineEditTextEditPanelColorRectTextureRectNinePatchRectVBoxContainerHBoxContainerGridContainerMarginContainerScrollContainerCenterContainerPanelContainerTabContainerHSeparatorProgressBarSpinBoxHSliderVSliderStyleBoxEmptyStyleBoxFlatStyleBoxLineStyleBoxTexture
Supported classes are defined in TARGET_CLASSES in generator.gd.
Additional Godot classes can be added there and generated to extend the available API.
Names can also be customized when needed.
&"VBoxContainer": "vbox",
generates:
UI.vbox()
Development
To regenerate the API:
godot --headless --path . --script res://generator.gd
To check whether the generated output is up to date:
godot --headless --path . --script res://generator.gd -- --check
To run the tests:
godot --headless --path . --script res://tests/test_ui.gd
Changelog for version v0.1.0
No changelog provided for this version.