How To / Can I Override A Virtual Function From Another Script

Godot Version

4.4

Question

I am working on something in which I want to override the Control’s _make_custom_tooltip function, I know you can do this by assigning a script to the object and then override it there. But I was wondering if I could do it like you can with a node’s properties like;

%ControlNode._make_custom_tooltip = Callable(self, "some_function")

Is something like this possible so I don’t have to create and give this node a new script just for one function?

Thanks.

No, it’s not possible. You’ll need to give it a script.

Ah okay, hopefully it will be at some point as this would be very useful for only overriding a couple functions without giving it an entire script.

Thanks.

You could create a generic script that overrides things for you. For example I’m working on an InteractionComponent3D that has a custom_highlight_material exported variable. If it’s left blank it does nothing. If I drag a material into it, whenever the object is highlighted by the player it uses that shader instead of the default.

In the same way you could add an exported boolean say. Then your script would look like:

@export var has_custom_tooltip = false

_ready() -> void
    if has_custom_tooltip:
        self.tooltip_text = _make_custom_tooltip()

You could also create an ENUM and match/case on it. It’s not as elegant as the solution you want, but it works.

I mean, it’s technically possible in a totally hacky way that will break stuff for sure… It’s kinda a poor man trait system

extends Node

const SCRIPT = """
extends {{extend_class}}

var make_custom_tooltip_callable:Callable

func _make_custom_tooltip(str):
	if make_custom_tooltip_callable:
		return make_custom_tooltip_callable.call(str)

	return null
"""

func _ready() -> void:
	for child in get_children():
		if child is Control:
			# Get the node's script
			var scr = child.get_script()

			var cls = ""
			if scr is GDScript:
				# extend that class
				cls = '"%s"' % scr.resource_path
			else:
				# hope it's not a C# or GDExtension script and get the base class of the node
				cls = child.get_class()

			# Create a new script
			scr = GDScript.new()
			# Set its source code with the correct class
			scr.source_code = SCRIPT.format({extend_class = cls}, "{{_}}")
			# Reload implementation
			scr.reload()
			# And set the new script to the child
			child.set_script(scr)

			# Now we can "override" that
			child.make_custom_tooltip_callable = func(str):
				var hbox = HBoxContainer.new()
				var tex = TextureRect.new()
				var lbl = Label.new()
				tex.texture = preload("res://icon.svg")
				tex.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
				tex.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
				tex.custom_minimum_size = Vector2(32, 32)
				lbl.text = "CUSTOM %s" % str
				hbox.add_child(tex)
				hbox.add_child(lbl)
				return hbox

Result:

The right ColorRect had already an script attached.