Extending the Editor with Plugins in Godot

Embark on a journey to harness the true power of Godot with editor plugins! Revolutionize your workflow with bespoke tools, efficient shortcuts, and personalized menu options. Delve deep into the art of plugin creation and unlock the boundless potential of Godot with ease. By Eric Van de Kerckhove.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 2 of 5 of this article. Click here to view the first page.

Handling Node Selection

For the visibility button plugin, you won’t need the _enter_tree and _exit_tree functions so delete them. You’ll be handling the initialization and cleanup with other functions. Now add the function below in the place of the removed ones:

func _handles(object) -> bool:
    return object is Node

Godot calls the _handles function when you select an object. The Object class is the base class for all other classes in Godot. This function returns true if the selected object can be handled by your plugin. In this case, the plugin only edits nodes, so it returns true if the selected object is a Node class, or derives from it.

You’ll need to keep track of the selected node yourself, so add a new variable above the _handles function named node_to_edit:

var node_to_edit : Node

With this variable in place, add the _edit function below the _handles function:

func _edit(object: Object) -> void: # 1
    if !object: # 2
        return

    node_to_edit = object # 3

The _edit function is called by Godot right after the _handles function returns true. It requests the editor to edit the given object and it’s the perfect place to store a reference to the selected object. Here’s an overview of what’s happening here:

  1. The _edit function gets passed the selected object, a Node in case of this plugin.
  2. There’s a possibility that the selected object is null, so you need to check if it’s not. If it’s null, return from the function and don’t do anything.
  3. Store a reference to the selected object for later use.

To check if this code is working correctly, add a temporary print statement at the end of the _edit function:

print(node_to_edit)

Now save the script and try selecting some nodes in the scene tree. You should see the name of the selected node in the console.

Selecting nodes, their names are shown in the console

As you can see, the plugin already works!

Note: When you select a root node like Main in this case, the console will call _edit twice. Thankfully, this won’t affect the functionality of the plugin.

Now remove or comment out the print statement you’ve added and save the script again. The last function to bring it all together is the _make_visible function, add it below the _edit function:

func _make_visible(visible: bool) -> void: # 1
    if visible: # 2
        _add_button()
    else: # 3
        _remove_button()

Like the _edit function, Godot calls the _make_visible function after the _handles function returns true. It handles the showing and hiding of the plugin UI. It also gets called when disabling the plugin. When showing the button, you’ll create it and add it to the toolbar. When hiding the button, you’ll remove it from the toolbar and destroy it. This is an alternative to using the _enter_tree and _exit_tree functions for initialization and cleanup.
Here’s the code above in more detail:

  1. The _make_visible function gets passed a boolean value, visible to tell the UI to show or hide.
  2. If visible is true, add the button to the toolbar via the _add_button function.
  3. If visible is false, remove the button from the toolbar via the _remove_button function.

After adding the code, you’ll get some errors as you haven’t added the _add_button and _remove_button functions yet. Add these empty functions to get rid of the errors:

func _add_button() -> void:
    pass


func _remove_button() -> void:
    pass

These will act as placeholders for now. In the next section you’ll add the logic.

Adding the Button

Instead of using the editor to create a button, you’ll do so by code. To get started, you’ll need a variable to store a reference to the button. Add a new variable below var node_to_edit : Node:

var visibility_button : Button

With that out of the way, replace the pass keyword in the _add_button function with the code below:

# 1
if visibility_button:
    return

# 2
visibility_button = Button.new()
visibility_button.text = "Toggle visibility"
visibility_button.focus_mode = Control.FOCUS_NONE
visibility_button.flat = true

# 3
visibility_button.pressed.connect(_on_button_pressed)

# 4
add_control_to_container(EditorPlugin.CONTAINER_CANVAS_EDITOR_MENU, \
visibility_button)

Most of the code above is creating the button and setting it up. Here’s a breakdown:

  1. Check if the button already exists. If it does, return and don’t do anything.
  2. Create a new button and set its properties:
    • Set the text of the button to “Toggle visibility”
    • Disable the button’s focus mode so it doesn’t steal focus from the editor
    • Make the button appear flat like the other buttons in the toolbar
  3. Connect the button’s pressed signal to the _on_button_pressed function, which doesn’t exist yet.
  4. Add the button to the toolbar using the add_control_to_container function. This function takes two parameters: the container to add the button to and the button itself.

There are a lot more places you can add controls to besides the toolbar. Godot has two enums for this as part of the EditorPlugin class: CustomControlContainer and DockSlot. You can find a full list in the EditorPlugin page of the documentation. You can experiment with these to find a suitable place to add your control(s).

Next up is defining what should happen when you click the button. This is the easiest part! It’s the same code as you’d expect to use in a game. Add the function below:

func _on_button_pressed() -> void:
    node_to_edit.visible = !node_to_edit.visible

This changes the visible property of the selected node to be the opposite of what it was before.
With the code you have set up now, you can already test out the plugin. Open the Project Settings menu and restart the plugin via the Plugins tab by toggling its Enable status off and on.

Toggle plugin enable checkbox

Next, open the 2D screen and select any node. You should see the button you defined above appear in the toolbar. When you click it, the selected node gets hidden or shown depending on its current state. Pretty neat!

Button can hide nodes

Before patting yourself on the back too much, don’t forget about the cleanup. Right now, a new button will get added to the toolbar each time you restart the plugin and select a node. Deselecting a node won’t do anything either, so that’s not ideal.

Way too many buttons

A quick reload of the project fixes this, but you should probably handle this the correct way. :]
To fix this, you’ll need to implement the _remove_button function so the button gets properly removed. Open the script again and remove the pass keyword from the _remove_button function. Now add the code below in its place:

# 1
if visibility_button:
    # 2
    remove_control_from_container(EditorPlugin.CONTAINER_CANVAS_EDITOR_MENU, \
    visibility_button)
    # 3
    visibility_button.queue_free()
    visibility_button = null

This will remove the button from the toolbar and free its memory. Here’s a more detailed look at the code:

  1. Check if the button exists. If there’s no button, nothing can be removed so don’t do anything.
  2. Remove the button from the toolbar using the remove_control_from_container function.
  3. Destroy the button from memory using the queue_free function. Also clear the reference to it by setting it to null, forgetting to do this will cause a memory leak.

Save the script and try selecting and deselecting some nodes in the scene while in the 2D screen. The button appears and disappears as it should now.

The button appears when a node is selected

Your first plugin is now complete, well done. You can adjust the code to make it do all sorts of useful things. If you want, you can add more than one control to the toolbar at once and add labels to show more information about the selected node.

While great, this way of writing plugins does come with a huge limitation: it only works with a single node at a time. If you try selecting more than one node at once, the button disappears from the toolbar. For some plugins, this won’t matter, but I bet you already have some plugin ideas whirling around in your head that require editing many nodes at the same time.
If you want to discover another way of writing plugins, read on!