-
-
Notifications
You must be signed in to change notification settings - Fork 23.6k
Add @export_tool_button annotation for easily creating inspector buttons.
#96290
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
If I may recommend, |
I wanted to provide all avenues of approach on the first draft of this as I wasn't 100% sure how acceptable the parameterization would be. I'm happy to drop it from this PR, just say when. |
|
I think both exposing undoredo and passing it as argument is a bit redundant. Though it's a bit tricky, because referring to EditorUndoRedoManager or EditorInterface in your script will break it once the project is exported. If you want a game script with helper tool button, your undoredo needs to be untyped. Which I guess would be a valid reason to pass it as argument 🤔 From what I see, the argument is optional, so it needs to be better documented.
Well, since #90130 exists, and will likely get merged sooner, I think you can remove the new method and write the docs as if the other PR was merged already. |
|
Putting this by 4.4 because the prior PR was by 4.4. |
c8a72c0 to
25376f4
Compare
This comment was marked as resolved.
This comment was marked as resolved.
607244e to
b06e404
Compare
|
Thanks for the review @KoBeWi! I believe I tackled all of your comments, let me know if any of the changes missed the mark. Here is the updated documentation (it grows!): I think that tidies up the convoluted word order while still being at the correct verbosity to make sure people are aware of the caveats of exporting a scene with a Interestingly the way I plan to use this is actually to automatically remove the offending nodes (that are in the Maybe there are other ways to handle resolving editor only doodads. For now I think this solution suits me and is worth pushing. |
|
"non-tools build" is a confusing term. Exported project is always "non-tools".
My idea for this problem was recognizing "editor scripts" and warning the user if they are referenced outside addons folder. Automatically removing nodes is rather unexpected behavior. |
19cf6d9 to
7b3c01f
Compare
It would be good to somehow bind arguments to the called function, especially in the dynamic case to permit multiple functions to hit the same end-point. I am not sure how to do this in a satisfying fashion. In your comment, what argument is unable to be passed here normally?
Annotating a callable does appeal to me more than an otherwise magic method name string even if it is a little wordy. I might try changing it to do that instead. @dalexeev Is this what you meant by using property hints, in b53967b? My original use case was quite minimal, I just need to be able to trigger something cleanly in the editor, so when I salvaged this I just got it working to the point that I could use it for myself. I do like this, not using a property hint feels like an oversight. Code from the picture@tool
extends Node2D
@export var first:int = 123
@export_tool_button("test")
@export_tool_button("test_disabled")
@export var last:int = 42
func _validate_property(property: Dictionary) -> void:
if property.name == "@tool_button_test": # hide the test button
property.usage = property.usage & ~PROPERTY_USAGE_EDITOR
if property.name == "@tool_button_test_disabled":
property.usage = property.usage | PROPERTY_USAGE_READ_ONLY
func _get_property_list() -> Array[Dictionary]:
return [{
"name": "cool_dynamic_tool_button",
"type": TYPE_NIL,
"hint": PROPERTY_HINT_TOOL_BUTTON,
"hint_string": "dynamic_button",
}]
func test():
print("toot")
func test_disabled():
print("can't touch this")
func dynamic_button():
print("dynamic button") |
Unfortunately, I realized that this could be problematic.
I think something like this would work, although it doesn't allow you to bind arbitrary arguments, unlike @tool_button(method: StringName, text: String = "", icon: StringName = "", arg: String = "")
# hint string format: <method>[,<text>[,<icon>[,<arg>]]]
@export_tool_button(&"test", "Click me", &"", "123")
func test(_undo_redo, arg):
prints("Hello world!", arg) |
b53967b to
d92eda0
Compare
d92eda0 to
cab3454
Compare
There are too many caveats to annotate I think I'm done with the changes necessary for property hint backing and dynamic addition of tool buttons! With the addition of the This now more than meets my own needs, let me know if I've missed/broken anything. @tool
extends Sprite2D
@export var first:int = 123
@export_tool_button(&"test_hidden")
@export_tool_button(&"test_disabled", "Disabled", &"Stop")
@export_tool_button(&"test_undoredo", "", &"UndoRedo")
@export_tool_button(&"set_modulate", "Make Green", &"ColorRect", "Color(0, 1, 0, 1)")
@export_tool_button(&"set_modulate", "Clear Modulation", &"Clear", "Color(1, 1, 1, 1)")
@export var last:int = 42
func _validate_property(property: Dictionary) -> void:
if property.name == "@tool_button_test_hidden": # hide the test button
property.usage = property.usage & ~PROPERTY_USAGE_EDITOR
if property.name == "@tool_button_test_disabled":
property.usage = property.usage | PROPERTY_USAGE_READ_ONLY
func _get_property_list() -> Array[Dictionary]:
var properties:Array[Dictionary]
for i in 3:
properties.append({
"name": "cool_dynamic_tool_button_%d" % i,
"type": TYPE_NIL,
"hint": PROPERTY_HINT_TOOL_BUTTON,
"hint_string": "test_dynamic,%s,Variant,Vector2(123, %d)" % ["Dynamic Button %d" % i, i],
})
return properties
func test_hidden():
print("toot")
func test_disabled():
print("can't touch this")
func test_undoredo(undo_redo:EditorUndoRedoManager):
prints("undoredo", undo_redo)
func test_dynamic(what:Variant):
prints("dynamic button", type_string(typeof(what)), typeof(what), what) |
|
@Macksaur Thank you for your patience! I'm sorry to make you do so much work, but I think the current version is overcomplicated, especially using I tried to change it to be as simple and functional as possible by removing unnecessary arguments and checks (since Godot is quite dynamic with callables). Please see dalexeev@0827064 and if you agree with this approach, feel free to rebase your PR branch using the commit. Updated testing script@tool
extends Sprite2D
@export var first: int = 123
@export_tool_button("Hidden") var hidden_action = test_hidden
@export_tool_button("Disabled", "Stop") var stop_action = test_disabled
@export_tool_button("UndoRedo", "UndoRedo") var undoredo_action = test_undoredo
@export_tool_button("Make Green", "ColorRect")
var make_green_action = set_self_modulate.bind(Color.GREEN)
@export_tool_button("Clear Modulation", "Clear")
var clear_modulation_action = set_self_modulate.bind(Color.WHITE)
@export var last: int = 42
func _validate_property(property: Dictionary) -> void:
if property.name == "hidden_action": # hide the test button
property.usage = property.usage & ~PROPERTY_USAGE_EDITOR
if property.name == "stop_action":
property.usage = property.usage | PROPERTY_USAGE_READ_ONLY
func _get_property_list() -> Array[Dictionary]:
var properties:Array[Dictionary]
for i in 3:
properties.append({
"name": "cool_dynamic_tool_button_%d" % i,
"type": TYPE_CALLABLE,
"hint": PROPERTY_HINT_TOOL_BUTTON,
"hint_string": "Dynamic Button %d" % i,
"usage": PROPERTY_USAGE_EDITOR,
})
return properties
func _get(property: StringName) -> Variant:
if property.begins_with("cool_dynamic_tool_button_"):
return test_dynamic.bind(property.trim_prefix("cool_dynamic_tool_button_"))
return null
func test_hidden():
print("toot")
func test_disabled():
print("can't touch this")
func test_undoredo():
prints("undoredo", EditorInterface.get_editor_undo_redo())
func test_dynamic(what):
prints("dynamic button", type_string(typeof(what)), typeof(what), what)Another example@tool
extends Node
@export_tool_button("Test") var test_action = test.bind(1)
func test(x):
print(x)
test_action = test.bind(x + 1) |
2c422d5 to
645a230
Compare
@tool_button annotation for easily creating inspector buttons.@export_tool_button annotation for easily creating inspector buttons.
645a230 to
7cc20ac
Compare
dalexeev
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. @Macksaur Thanks again for your work and patience!
…ttons Co-authored-by: jordi <[email protected]> Co-authored-by: K. S. Ernest (iFire) Lee <[email protected]> Co-authored-by: Mack <[email protected]>
7cc20ac to
85dfd89
Compare
|
Thanks @Macksaur, @jordi-star, @fire, @dalexeev, and everyone involved in the thorough review and testing! |
|
I'm currently looking into bringing this to C# ✌️ |
|
Not sure how the workflow for documenting new feature works, but in case it wasn't considered I wanted to mention that these pages need to be updated with information about the new annotation: GDScript exported properties - https://docs.godotengine.org/en/latest/tutorials/scripting/gdscript/gdscript_exports.html |
|
Try reloading the scene. |
|
@Kartopod Thanks for the reminder to add docs. I think the process for ensuring that manual docs are added for new PRs is currently a little inconsistent. In this case I've made an issue to track it: godotengine/godot-docs#10303, which anyone can contribute. If it doesn't get a volunteer contributor before 4.4, I'll make the changes myself |





Supersedes: #78355
Supersedes: #59289
Bugsquad edit: The syntax below is not the one that was merged in the end, see #96290 (comment) for updated examples.