Skip to content

Commit

Permalink
Fixed linting issue
Browse files Browse the repository at this point in the history
  • Loading branch information
snatvb committed Jun 9, 2024
1 parent 18c9c79 commit d39fcac
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 132 deletions.
2 changes: 1 addition & 1 deletion src/recipes/editor-plugin/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,4 @@ Use an [`is_editor_hint` guard][api-engine-iseditorhint] if you don't want some
[api-engine-iseditorhint]: https://godot-rust.github.io/docs/gdext/master/godot/engine/struct.Engine.html#method.is_editor_hint
[wiki-guard-csci]: https://en.wikipedia.org/wiki/Guard_(computer_science)
```
```
284 changes: 153 additions & 131 deletions src/recipes/editor-plugin/inspector-plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@

# Inspector plugins

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.
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.

This is implementation of [Godot docs](https://docs.godotengine.org/en/stable/tutorials/plugins/editor/inspector_plugins.html) on rust.
This is implementation of
[Godot docs](https://docs.godotengine.org/en/stable/tutorials/plugins/editor/inspector_plugins.html) on Rust.

Before:

Expand All @@ -19,19 +24,22 @@ After:

![After](./images/after.png)

Add requires to rust (in rust folder, where is Cargo.toml):
```sh
Add requires to Rust (in Rust folder, where is Cargo.toml):

```bash
cargo add rand
```

Add file `addon.rs` and import it in `lib.rs`:
```rs

```rust
// file: lib.rs
mod addon;
```

Add the following imports at the beginning of the file
```rs

```rust
use godot::{
classes,
engine::{
Expand All @@ -46,10 +54,12 @@ 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.


## Add Property Editor

To begin with, let's define the editor for properties:
```rs

```rust
#[derive(GodotClass)]
#[class(tool, init, base=EditorProperty)]
struct RandomNumberEditor {
Expand All @@ -59,7 +69,8 @@ struct RandomNumberEditor {
```

After that, we need to add an implementation for the trait `IEditorProperty`:
```rs

```rust
#[godot_api]
impl IEditorProperty for RandomNumberEditor {
fn enter_tree(&mut self) {
Expand Down Expand Up @@ -89,7 +100,8 @@ impl IEditorProperty for RandomNumberEditor {
```

Let's add a handler for the button:
```rs

```rust
#[godot_api]
impl RandomNumberEditor {
#[func]
Expand All @@ -114,11 +126,13 @@ impl RandomNumberEditor {
}
```


## Add Inspector plugin

Now we need to connect this editor to fields with an integer type.
To do this, we need to create an EditorInspectorPlugin.
```rs

```rust
#[derive(GodotClass)]
#[class(tool, init, base=EditorInspectorPlugin)]
struct RandomInspectorPlugin {
Expand All @@ -127,7 +141,8 @@ struct RandomInspectorPlugin {
```

IEditorInspectorPlugin implementation:
```rs

```rust
#[godot_api]
impl IEditorInspectorPlugin for RandomInspectorPlugin {
fn parse_property(
Expand Down Expand Up @@ -155,14 +170,19 @@ impl IEditorInspectorPlugin for RandomInspectorPlugin {
}
```

If `parse_property` returns `true`, the editor will be created and replace the current representation; if not, it's necessary to return `false`. This allows for specific control over where and how processing occurs.
If `parse_property` returns `true`, the editor will be created and replace the current
representation; if not, it's necessary to return `false`.
This allows for specific control over where and how processing occurs.


## Add Editor 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.
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.


```rs
```rust
#[derive(GodotClass)]
#[class(tool, init, editor_plugin, base=EditorPlugin)]
struct RustEditorPlugin {
Expand All @@ -171,7 +191,7 @@ struct RustEditorPlugin {
}
```

```rs
```rust
#[godot_api]
impl IEditorPlugin for RustEditorPlugin {
fn enter_tree(&mut self) {
Expand All @@ -188,9 +208,11 @@ impl IEditorPlugin for RustEditorPlugin {
}
```


```admonish danger title="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:

```log
Expand All @@ -201,121 +223,121 @@ ERROR: Cannot get class 'RandomInspectorPlugin'.
at: (core/object/class_db.cpp:392)
```


# Full code

<details>
<summary>addon.rs</summary>

```rs
use godot::{
classes,
engine::{
Button, EditorInspectorPlugin, EditorPlugin, EditorProperty, IEditorInspectorPlugin,
IEditorPlugin, IEditorProperty,
},
global,
prelude::*,
};
use rand::Rng;

#[derive(GodotClass)]
#[class(tool, init, editor_plugin, base=EditorPlugin)]
struct RustEditorPlugin {
base: Base<EditorPlugin>,
random_inspector: Gd<RandomInspectorPlugin>,
}

#[godot_api]
impl IEditorPlugin for RustEditorPlugin {
fn enter_tree(&mut self) {
self.random_inspector = RandomInspectorPlugin::new_gd();
self.to_gd()
.add_inspector_plugin(self.random_inspector.to_variant().to());
}

fn exit_tree(&mut self) {
self.to_gd()
.remove_inspector_plugin(self.random_inspector.to_variant().to());
}
}

#[derive(GodotClass)]
#[class(tool, init, base=EditorInspectorPlugin)]
struct RandomInspectorPlugin {
base: Base<EditorInspectorPlugin>,
}

#[godot_api]
impl IEditorInspectorPlugin for RandomInspectorPlugin {
fn parse_property(
&mut self,
_: Gd<classes::Object>,
type_: VariantType,
name: GString,
_: global::PropertyHint,
_: GString,
_: global::PropertyUsageFlags,
_: bool,
) -> bool {
if type_ == VariantType::INT {
self.base_mut()
.add_property_editor(name, RandomNumberEditor::new_alloc().to_variant().to());
return true;
}

false
}

fn can_handle(&self, _: Gd<classes::Object>) -> bool {
true
}
}

#[derive(GodotClass)]
#[class(tool, init, base=EditorProperty)]
struct RandomNumberEditor {
base: Base<EditorProperty>,
button: Option<Gd<Button>>,
}

#[godot_api]
impl RandomNumberEditor {
#[func]
fn handle_press(&mut self) {
let property_name = self.base().get_edited_property();
let num = rand::thread_rng().gen_range(0..100);
godot_print!("Randomize! {} for {}", num, property_name);
self.base_mut()
.emit_changed(property_name, num.to_variant().to());
if let Some(mut button) = self.button.clone() {
let text = format!("Randomize: {}", num);
button.set_text(text.into());
} else {
godot_error!("Button wasn't found in handle_press");
}
}
}

#[godot_api]
impl IEditorProperty for RandomNumberEditor {
fn enter_tree(&mut self) {
let mut button = Button::new_alloc();
button.connect(
"pressed".into(),
self.base().callable("handle_press".to_godot()),
);
button.set_text("Randomize".into());
self.base_mut().add_child(button.to_variant().to());
self.button = Some(button);
}

fn exit_tree(&mut self) {
if let Some(button) = self.button.take() {
self.base_mut().remove_child(button.to_variant().to());
} else {
godot_error!("Button wasn't found in exit_tree");
}
}
}
```
</details>

```rust
// file: addon.rs

use godot::{
classes,
engine::{
Button, EditorInspectorPlugin, EditorPlugin, EditorProperty, IEditorInspectorPlugin,
IEditorPlugin, IEditorProperty,
},
global,
prelude::*,
};
use rand::Rng;

#[derive(GodotClass)]
#[class(tool, init, editor_plugin, base=EditorPlugin)]
struct RustEditorPlugin {
base: Base<EditorPlugin>,
random_inspector: Gd<RandomInspectorPlugin>,
}

#[godot_api]
impl IEditorPlugin for RustEditorPlugin {
fn enter_tree(&mut self) {
self.random_inspector = RandomInspectorPlugin::new_gd();
self.to_gd()
.add_inspector_plugin(self.random_inspector.to_variant().to());
}

fn exit_tree(&mut self) {
self.to_gd()
.remove_inspector_plugin(self.random_inspector.to_variant().to());
}
}

#[derive(GodotClass)]
#[class(tool, init, base=EditorInspectorPlugin)]
struct RandomInspectorPlugin {
base: Base<EditorInspectorPlugin>,
}

#[godot_api]
impl IEditorInspectorPlugin for RandomInspectorPlugin {
fn parse_property(
&mut self,
_: Gd<classes::Object>,
type_: VariantType,
name: GString,
_: global::PropertyHint,
_: GString,
_: global::PropertyUsageFlags,
_: bool,
) -> bool {
if type_ == VariantType::INT {
self.base_mut()
.add_property_editor(name, RandomNumberEditor::new_alloc().to_variant().to());
return true;
}

false
}

fn can_handle(&self, _: Gd<classes::Object>) -> bool {
true
}
}

#[derive(GodotClass)]
#[class(tool, init, base=EditorProperty)]
struct RandomNumberEditor {
base: Base<EditorProperty>,
button: Option<Gd<Button>>,
}

#[godot_api]
impl RandomNumberEditor {
#[func]
fn handle_press(&mut self) {
let property_name = self.base().get_edited_property();
let num = rand::thread_rng().gen_range(0..100);
godot_print!("Randomize! {} for {}", num, property_name);
self.base_mut()
.emit_changed(property_name, num.to_variant().to());
if let Some(mut button) = self.button.clone() {
let text = format!("Randomize: {}", num);
button.set_text(text.into());
} else {
godot_error!("Button wasn't found in handle_press");
}
}
}

#[godot_api]
impl IEditorProperty for RandomNumberEditor {
fn enter_tree(&mut self) {
let mut button = Button::new_alloc();
button.connect(
"pressed".into(),
self.base().callable("handle_press".to_godot()),
);
button.set_text("Randomize".into());
self.base_mut().add_child(button.to_variant().to());
self.button = Some(button);
}

fn exit_tree(&mut self) {
if let Some(button) = self.button.take() {
self.base_mut().remove_child(button.to_variant().to());
} else {
godot_error!("Button wasn't found in exit_tree");
}
}
}
```

0 comments on commit d39fcac

Please sign in to comment.