Files
fastvoxel/src/editor/editor.rs
TheRealKaamy 880362cc03 + atomic build.sh
+ implemented editor buttons
2026-03-16 18:36:37 +03:30

126 lines
4.5 KiB
Rust

use godot::classes::EditorInterface;
use godot::classes::EditorPlugin;
use godot::classes::IEditorPlugin;
use godot::classes::ImageTexture;
use godot::classes::MenuButton;
use godot::classes::ResourceLoader;
use godot::classes::Texture2D;
use godot::classes::editor_plugin::CustomControlContainer;
use godot::prelude::*;
use crate::world::World;
#[derive(GodotClass)]
#[class(init, base=EditorPlugin, tool)]
pub struct WorldPlugin {
#[base]
base: Base<EditorPlugin>,
toolbar_button: Option<Gd<MenuButton>>,
}
#[godot_api]
impl WorldPlugin {
// Called when a menu item is selected
#[func]
fn on_menu_item_pressed(&self, id: i32) {
let mut world = match self.find_world_node() {
Some(world) => world,
None => {
godot_error!(
"No World node found in the current scene. Make sure it's named 'World' and is a child of the scene root."
);
return;
}
};
match id {
0 => {
world.call("clear_terrain", &[]);
world.call("generate_terrain", &[]);
}
1 => {
world.call("clear_terrain", &[]);
}
_ => {
godot_print!("Unknown item");
}
}
// Here you can call your actual world management methods.
// For example:
// if let Some(world_manager) = self.get_world_manager() {
// match id {
// 0 => world_manager.call("rebuild_world", &[]),
// 1 => world_manager.call("clear_world", &[]),
// 2 => world_manager.call("reload_chunks", &[]),
// _ => (),
// }
// }
}
fn find_world_node(&self) -> Option<Gd<World>> {
let editor = EditorInterface::singleton();
let scene_root = editor.get_edited_scene_root()?;
// Assuming the World node is a direct child of the root and named "World"
scene_root.try_get_node_as::<World>("World")
}
}
#[godot_api]
impl IEditorPlugin for WorldPlugin {
fn enter_tree(&mut self) {
// Create the MenuButton
let mut menu_button = MenuButton::new_alloc();
menu_button.set_text("FastVoxel"); // Button label
// menu_button.set_flat(false); // Make it look like a normal button (optional)
// --- Load an SVG icon and set it ---
// Path to your SVG file (relative to the project, e.g., in addons/fastvoxel/fastvoxel.svg)
if let Some(icon) = ResourceLoader::singleton().load("res://addons/fastvoxel/fastvoxel.svg")
{
let icon_texture: Gd<Texture2D> = icon.cast(); // Cast to Texture2D
// Get image data, resize, create new texture
let mut image = icon_texture.get_image(); // Requires Texture2D to have image data
image
.as_mut()
.expect("failed to load the fastvoxel icon")
.resize(24, 24);
let small_texture = ImageTexture::create_from_image(image.as_ref());
menu_button.set_button_icon(small_texture.as_ref());
} else {
godot_error!("Failed to load plugin icon");
}
// --- Build the dropdown menu ---
let mut popup = menu_button.get_popup(); // Returns a PopupMenu
popup.as_mut().expect("no popup").add_item("Rebuild World"); // item text, id
popup.as_mut().expect("no popup").add_item("Clear World");
popup.as_mut().expect("no popup").add_item("Reload Chunks");
// You can also add separators, submenus, etc.
// Connect the popup's "id_pressed" signal to our handler
let callable = self.base().callable("on_menu_item_pressed");
popup.unwrap().connect("id_pressed", &callable);
// Add the button to the editor's main toolbar (top row, next to play buttons)
self.base_mut().add_control_to_container(
CustomControlContainer::SPATIAL_EDITOR_MENU,
menu_button.to_godot(),
);
// Store the button reference so we can remove it later
self.toolbar_button = Some(menu_button);
}
fn exit_tree(&mut self) {
// Clean up: remove the button from the toolbar
if let Some(button) = self.toolbar_button.take() {
self.base_mut().remove_control_from_container(
CustomControlContainer::SPATIAL_EDITOR_MENU,
button.to_godot(),
);
// The button will be freed automatically when no longer referenced
}
}
}