Skip to content
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

Allow exporting variables of type Variant #89324

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

KoBeWi
Copy link
Member

@KoBeWi KoBeWi commented Mar 9, 2024

Inspired by godotengine/godot-proposals#9269
Closes godotengine/godot-proposals#9368

This PR allows for exporting variables of type Variant. They show a property editor similar to what Array and Dictionary are using:

JRnkfXjKv5.mp4

This allows for nice flexibility, where you can change not only the variable, but also its value and allows for better customization.

example plugin for "tri-state bool"
@tool
extends EditorPlugin

var state3 := TriStateBoolInspectorPlugin.new()

func _enter_tree() -> void:
	add_inspector_plugin(state3)


func _exit_tree() -> void:
	remove_inspector_plugin(state3)

class TriStateBoolInspectorPlugin extends EditorInspectorPlugin:
	func _can_handle(object: Object) -> bool:
		return true
	
	func _parse_property(object: Object, type: Variant.Type, name: String, hint_type: PropertyHint, hint_string: String, usage_flags: int, wide: bool) -> bool:
		if type == TYPE_NIL and hint_string == "3bool":
			add_property_editor(name, TriStateBoolEditor.new())
			return true
		return false

class TriStateBoolEditor extends EditorProperty:
	var button: TriStateCheckbox
	
	func _init() -> void:
		button = TriStateCheckbox.new()
		add_child(button)
		button.pressed.connect(func(): get_edited_object().set(get_edited_property(), button.state))
	
	func _update_property() -> void:
		button.state = get_edited_object().get(get_edited_property())

class TriStateCheckbox extends Button:
	const STATES = [null, false, true]
	
	var textures: Array[Texture2D]
	var state: Variant:
		set(s):
			state = s
			icon = textures[STATES.find(state)]
	
	func _init() -> void:
		size_flags_horizontal = SIZE_SHRINK_BEGIN
		
		for color in [Color.RED, Color.GREEN, Color.BLUE]:
			var gradient := Gradient.new()
			gradient.remove_point(1)
			gradient.colors = [color]
			
			var texture := GradientTexture2D.new()
			texture.gradient = gradient
			texture.width = 16
			texture.height = 16
			textures.append(texture)
	
	func _pressed() -> void:
		for i in STATES.size():
			if STATES[i] == state:
				state = STATES[(i + 1) % STATES.size()]
				break

Example usage:

@export_custom(0, "3bool") var boolean_hat_trick
godot.windows.editor.dev.x86_64_RzAIgcf3TZ.mp4

(the text is updated with _process().

@KoBeWi KoBeWi force-pushed the pandora's_can_of_worms branch 3 times, most recently from 4273cf7 to 72609e6 Compare March 9, 2024 19:21
@dalexeev
Copy link
Member

dalexeev commented Mar 9, 2024

I wanted to salvage/re-implement it. I think this should work like Dictionary key/value editor, the user could select the type first, then the value.

@KoBeWi
Copy link
Member Author

KoBeWi commented Mar 9, 2024

I thought about that too, but opted for a simpler solution (as the goal was customizability itself).
I can rework this into the type dropdown widget if that's better.

@KoBeWi
Copy link
Member Author

KoBeWi commented Mar 24, 2024

Ok reworked

JRnkfXjKv5.mp4

@Jan-PieterInghels
Copy link

Just checking in to see if this can be approved and merged?

@AThousandShips
Copy link
Member

We're currently in feature freeze so this won't be considered until 4.4, and it's not been decided on yet

Copy link
Member

@Calinou Calinou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good to me at a glance. Could you look into rebasing this on top of master?

PS: Is there much use in exposing Object in the list of types in the dropdown? As far as I know, you can't give it any useful value directly in the inspector.

@KoBeWi
Copy link
Member Author

KoBeWi commented Sep 24, 2024

PS: Is there much use in exposing Object in the list of types in the dropdown? As far as I know, you can't give it any useful value directly in the inspector.

You can assign it a Resource.

Comment on lines -4422 to +4424
if (export_type.is_variant() || export_type.has_no_type()) {
if (is_dict) {
// Dictionary allowed to have a variant key/value.
export_type.kind = GDScriptParser::DataType::BUILTIN;
} else {
push_error(R"(Cannot use simple "@export" annotation because the type of the initialized value can't be inferred.)", p_annotation);
return false;
}
if (export_type.has_no_type()) {
push_error(R"(Cannot use simple "@export" annotation because the type of the initialized value can't be inferred.)", p_annotation);
return false;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sus change after rebase.

@@ -997,7 +997,7 @@ void TileSourceInspectorPlugin::_confirm_change_id() {
}

bool TileSourceInspectorPlugin::can_handle(Object *p_object) {
return p_object->is_class("TileSetAtlasSourceProxyObject") || p_object->is_class("TileSetScenesCollectionProxyObject");
return p_object && (p_object->is_class("TileSetAtlasSourceProxyObject") || p_object->is_class("TileSetScenesCollectionProxyObject"));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason this crashes without null check. I did not bother investigating why, it just makes no sense. No other plugin has this problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Dynamic/Inexplicit Type Exports
5 participants