I made a demo project for Godot 4+ here: Demo on Gitlab
So, you're making a Node and you need a "go" button in the Inspector to run your function and actually do stuff. Enter the Button. Yeah; no. Nope! Godot says Nogo!
At this point we all give up and use some form of boolean toggle:
@export var press_me:bool = false:
set(b):
press_me = false
my_fancy_func()
It's not great looking, nor does it make sense. A Button is the way to go. Took me ages, but with some luck I have a solution. There are a few sections to it:
One: The Basic Plugin files
"res://addons/YourAddonNameHere/plugin.gd"
@tool
extends EditorPlugin
var inspector_plugin
func _enter_tree() -> void:
if Engine.is_editor_hint():
# Add our custom Go button!
inspector_plugin = preload("inspector_button_plugin.gd").new()
add_inspector_plugin(inspector_plugin)
func _exit_tree() -> void:
remove_inspector_plugin(inspector_plugin)
func _get_plugin_name() -> String:
return "MyWhateveraddon"
"res://addons/YourAddonNameHere/plugin.cfg"
[plugin]
name="MyWhateveraddon"
description="Faster than light travel!"
author="<you>"
version="0.0.1"
script="plugin.gd"
Two: The Script that makes the Button(s)
"res://addons/YourAddonNameHere/inspector_button_plugin.gd"
This script hooks-into the parser and so can seek certain @ tags. It can then replace strings at the last moment—so we use this to intercept an @export var and return a button instead.
extends EditorInspectorPlugin
var InspectorToolButton = preload("inspector_button.gd")
var button_text : String
func _can_handle(object) -> bool:
return true
func _parse_property(
object: Object, type: Variant.Type,
name: String, hint_type: PropertyHint,
hint_string: String, usage_flags, wide: bool):
if name.begins_with("go_"):
var s = str(name.split("go_")[1])
s = s.replace("_", " ")
s = "Press to %s" % s
add_custom_control( InspectorToolButton.new(object, s) )
return true #Returning true removes the built-in editor for this property
return false # else leave it
Three: The Script that is a Button
"res://addons/YourAddonNameHere/inspector_button.gd"
extends MarginContainer
var object: Object
func _init(obj: Object, text:String):
object = obj
size_flags_horizontal = Control.SIZE_SHRINK_CENTER
var button := Button.new()
add_child(button)
button.size_flags_horizontal = SIZE_EXPAND_FILL
button.text = text
button.button_down.connect(object._on_button_pressed.bind(text))
Four and Final: Your own Script
At the place where you want the button to appear, in your code, do this:
# This is your own script code
@export var go_run_my_fancy_func:String
#To capture the button press, add the connected signal func:
func _on_button_pressed(text:String):
if text.to_upper() == "PRESS TO RUN MY FANCY FUNC":
my_fancy_func()
Last thoughts
Don't foget to activate the plugin.
If there are any bugs, please let me know on Mastodon or Cohost, or on Gitlab!
That's it!
HTH
/d