(Inspector Plugin) How can I "steal"/embed default Godot property editors within my own custom editor?/use EditorProperty<type>/EditorInspector

Ideally you would write a custom property editor if you want full control over how to modify the data but you can do a lot with Object._get(), Object._set() and Object._get_property_list()

This is a somewhat convoluted and semi-working example. I ran out of time to fix a few issues but it can give you an idea:

@tool
class_name EnemyParty extends Resource

# This can come from another file
const ENEMY_LIST = ["Goblin", "Skeleton", "Bandit", "Orc", "Troll", "Spider"]

# A flag to find out if a property is stored to disk and it's a variable in a script
# this find the @export variables in a Object
const VAR_STORAGE = (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_SCRIPT_VARIABLE)

# The party
var party:Dictionary = {}
# The size
var size:int = 0


func _get(property: StringName) -> Variant:
	if property == "Party Size":
		return size

	# If it start with Party/Enemy then it's a new enemy so return an empty string
	if property.begins_with("Party/Enemy "):
		return ""

	# If it start with Party/ then is an enemy, split the property path and get the stat value
	if property.begins_with("Party/"):
		var splits = property.split("/")
		var key = splits[1]
		var field = splits[2]
		return party[key].get(field)

	return null


func _set(property: StringName, value: Variant) -> bool:
	if property == "Party Size":
		size = value
		if size == 0:
			party.clear()
		notify_property_list_changed()
		return false

	# if we are setting a value for the property that begins with Party/Enemy
	# then it's a new enemy so create a CharStats with random stats and
	# add it to the dictionary
	if property.begins_with("Party/Enemy "):
		if not party.find_key(value):
			var stats = CharStats.new()
			stats.strength = randi_range(5, 40)
			stats.dexterity = randi_range(5, 40)
			stats.luck = randi_range(5, 40)
			stats.magic = randi_range(5, 40)
			stats.spirit = randi_range(5, 40)
			stats.vitality = randi_range(5, 40)
			party[value] = stats

		notify_property_list_changed()
		return true

	# If it begins with party then it's an enemy, set the value to the stat
	if property.begins_with("Party/"):
		var splits = property.split("/")
		var key = splits[1]
		var field = splits[2]
		party[key].set(field, value)
		notify_property_list_changed()
		return true

	return false


func _get_property_list() -> Array[Dictionary]:
	var result:Array[Dictionary] = []
	# The party variable, don't show it in the inspector and save it to disk
	result.push_back({
		"name": "party",
		"type": TYPE_DICTIONARY,
		"usage": PROPERTY_USAGE_NO_EDITOR,
	})
	# The size variable, don't show it in the inspector and save it to disk
	result.push_back({
		"name": "size",
		"type": TYPE_INT,
		"usage": PROPERTY_USAGE_NO_EDITOR,
	})
	# I'm using this property as a proxy to the size variable
	result.push_back({
		"name": "Party Size",
		"type": TYPE_INT,
		"hint_string": "0,15,or_greater,suffix:enemies",
		"usage": PROPERTY_USAGE_EDITOR
	})

	# iterate over the size
	for i in size:
		# if i is less than the party size
		if i < party.size():
			# get the key from the party
			var key = party.keys()[i]
			# get the properties that we need to show
			var properties = party[key].get_property_list().filter(func(p): return p.usage & VAR_STORAGE == VAR_STORAGE)
			# for each property
			for prop in properties:
				# Add a property Party/<key>/<stat_name>
				result.push_back({
					"name": "Party/%s/%s" % [key, prop.name],
					"type": prop.type,
					"usage": PROPERTY_USAGE_EDITOR
				})
		else:
			# else i is bigger than the party size so we need to show a new enemy selection
			# Fiter the enemy list removing the ones that where added before
			var list = ENEMY_LIST.filter(func(n): return not party.has(n))
			# Push a new property Party/Enemy <index>
			result.push_back({
				"name": "Party/Enemy %s" % (i+1),
				"type": TYPE_STRING,
				"hint": PROPERTY_HINT_ENUM_SUGGESTION,
				"hint_string": ",".join(list),
				"usage": PROPERTY_USAGE_EDITOR
			})

	return result

And the CharStats script:

class_name CharStats extends Resource

@export var strength := 0
@export var dexterity := 0
@export var vitality := 0
@export var magic := 0
@export var spirit := 0
@export var luck := 0

Result:

5 Likes