The inspector dock allows you to create custom widgets to edit properties through plugins.
This can be beneficial when working with custom datatypes and resources, although you can
use the feature to change the inspector widgets for built-in types. You can design custom
controls for specific properties, entire objects, and even separate controls associated
with particular datatypes.
For more info, see
docs.godotengine.org.
The example
in the Godot docs in Rust. It will replace integer input with a button that creates a random value.
Before (int input):
After (button):
Add this dependency to Rust with the shell in the same directory as Cargo.toml.
Add the following imports at the beginning of the file:
#![allow(unused)]fnmain() {
use godot::classes::{
Button, EditorInspectorPlugin, EditorPlugin, EditorProperty, IEditorInspectorPlugin,
IEditorPlugin, IEditorProperty,
};
use godot::global;
use godot::prelude::*;
use rand::Rng;
}
Since Rust is a statically typed language, we will proceed in reverse order unlike in Godot documentation, to avoid encountering errors unnecessarily.
After that, we need to add an implementation for the trait IEditorProperty:
#![allow(unused)]fnmain() {
#[godot_api]impl IEditorProperty for RandomIntEditor {
fnenter_tree(&mutself) {
// Create button element.letmut button = Button::new_alloc();
// Add handler for this button, handle_press will be define in another impl.
button.connect("pressed", self.base().callable("handle_press"));
button.set_text("Randomize");
// Save pointer to the button into struct.self.button = Some(button.clone());
self.base_mut().add_child(button.upcast());
}
fnexit_tree(&mutself) {
// Remove element from inspector when this plugin unmount:ifletSome(button) = self.button.take() {
self.base_mut().remove_child(button.upcast());
} else {
// Log error if button disappeared before
godot_error!("Button wasn't found in exit_tree");
}
}
}
}
Let's add a handler for the button:
#![allow(unused)]fnmain() {
#[godot_api]impl RandomIntEditor {
#[func]fnhandle_press(&mutself) {
// Update value by button click:// - Take property name, randomize number.// - Send property name and random number to Godot engine to update value.// - Update button text.let property_name = self.base().get_edited_property();
let num = rand::thread_rng().gen_range(0..100);
godot_print!("Randomize! {num} for {property_name}");
self.base_mut()
.emit_changed(property_name, num.to_variant());
ifletSome(mut button) = self.button.clone() {
let text = format!("Randomize: {num}");
button.set_text(&text);
} else {
// Print error of something went wrong
godot_error!("Button wasn't found in handle_press");
}
}
}
}
To add a property editor (which we implemented earlier), you need to implement the IEditorInspectorPlugin trait:
#![allow(unused)]fnmain() {
#[godot_api]impl IEditorInspectorPlugin for RandomInspectorPlugin {
fnparse_property(
&mutself,
_object: Gd<Object>, // object that is being inspected
value_type: VariantType,
name: GString,
_hint_type: global::PropertyHint,
_hit_string: GString,
_flags: global::PropertyUsageFlags,
_wide: bool,
) -> bool {
if value_type == VariantType::INT {
self.base_mut()
.add_property_editor(name, RandomIntEditor::new_alloc().upcast());
returntrue;
}
false
}
// This method says Godot that this plugin handle the object if it returns truefncan_handle(&self, object: Gd<Object>) -> bool {
// This plugin handle only Node2D and object that extends it
object.is_class("Node2D")
}
}
}
If parse_property returns true, the editor plugin will be created and replace the current
representation; if not, it's necessary to return false.
This allows you to control where and how processing is done by this plugin.
Only one thing left to do: define the editor plugin that will kick off all this magic!
This can be a generic EditorPlugin or a more specific InspectorEditorPlugin, depending
on what you want to achieve.
#![allow(unused)]fnmain() {
#[godot_api]impl IEditorPlugin for RustEditorPlugin {
fnenter_tree(&mutself) {
// Create our inspector plugin and save it.let plugin = RandomInspectorPlugin::new_gd();
self.random_inspector = plugin.clone();
self.base_mut().add_inspector_plugin(plugin.upcast());
}
fnexit_tree(&mutself) {
// Remove inspector plugin when editor plugin leaves scene tree.let plugin = self.random_inspector.clone();
self.base_mut().remove_inspector_plugin(plugin.upcast());
}
}
}
Troubleshooting
Sometimes after compilation, you may encounter errors or panic. Most likely, all you need to do is simply restart the Godot Editor.
Example error:
Initialize godot-rust (API v4.2.stable.official, runtime v4.2.2.stable.official)
ERROR: Cannot get class 'RandomInspectorPlugin'.
at: (core/object/class_db.cpp:392)
ERROR: Cannot get class 'RandomInspectorPlugin'.
at: (core/object/class_db.cpp:392)