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 added this to the 4.x milestone Mar 9, 2024
@KoBeWi KoBeWi requested a review from a team as a code owner March 9, 2024 19:05
@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.

@KoBeWi KoBeWi force-pushed the pandora's_can_of_worms branch from ca78cdf to e2134fb Compare September 24, 2024 19:32
Comment on lines -4422 to +4536
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.

@Mickeon Mickeon requested a review from Calinou November 20, 2024 14:39
@Mickeon
Copy link
Contributor

Mickeon commented Nov 20, 2024

Interest in this PR came up again in godotengine/godot-proposals#7016 (comment) . Just for transparency and linking.

@Gnumaru
Copy link
Contributor

Gnumaru commented Jan 17, 2025

Since 4.4 beta 1 came out, we are feature freezed and this probably won't get to be merged in 4.4.

But is there anything preventing this to be merged? That is, besides the collosal work of maintainers in evaluating the enormous ammount of pull request the godot project receives every day and deciding, for non bugfix PRs, wheter it is worth or not incorporating into the engine, and if so, deciding when doing it?

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.

Tested locally (rebased on top of master a7146ef), there's a build error:

Generating core/version_generated.gen.h ...
Generating core/version_hash.gen.cpp ...
Compiling core/version_hash.gen.cpp ...
Compiling editor/scu/scu_editor_4.gen.cpp ...
Linking Static Library core/libcore.linuxbsd.editor.x86_64.a ...
Ranlib Library core/libcore.linuxbsd.editor.x86_64.a ...
In file included from editor/scu/scu_editor_4.gen.cpp:4:
./editor/editor_properties.cpp: In member function 'void EditorPropertyVariant::_notification(int)':
./editor/editor_properties.cpp:91:30: error: 'class MenuButton' has no member named 'set_icon'
   91 |                 change_type->set_icon(get_editor_theme_icon("Edit"));
      |                              ^~~~~~~~
scons: *** [editor/scu/scu_editor_4.gen.linuxbsd.editor.x86_64.o] Error 1
scons: building terminated because of errors.

@KoBeWi KoBeWi force-pushed the pandora's_can_of_worms branch from e2134fb to 36c0c6a Compare January 22, 2025 15:23
@KoBeWi KoBeWi requested a review from Calinou January 22, 2025 15:24
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.

It works now 🙂

image

(The full line of d is @export var d: Variant = null, it's cut off on the screenshot.)

Note that the Variant type hint must be specified explicitly, and you must not set a default value of null for it to work as a proper Variant export where you can change the type. I think this makes sense, as this is an uncommon use case and it's better to be explicit about it.

@KoBeWi KoBeWi modified the milestones: 4.x, 4.5 Jan 23, 2025
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
7 participants