Skip to content

Instantly share code, notes, and snippets.

@ducklin5
Last active October 22, 2023 19:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ducklin5/3a8645816c9e9bf4379d8c5045f15ce6 to your computer and use it in GitHub Desktop.
Save ducklin5/3a8645816c9e9bf4379d8c5045f15ce6 to your computer and use it in GitHub Desktop.
Godot Rust Macros
/*
* MIT License
*
* Copyright (c) 2023 Azeez Abass
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use godot::prelude::*;
#[derive(GodotClass)]
#[class(base=Object)]
pub struct SceneEntryHandler {
node: Gd<Node>
}
#[godot_api]
pub impl SceneEntryHandler {
pub fn new(node: Gd<Node>) -> Gd<Self> {
Gd::new(Self { node })
}
#[func]
fn call(&mut self) {
if let Some(scene_tree) = self.node.get_tree() {
if let Some(e_root) = scene_tree.get_edited_scene_root() {
self.node.set_owner(e_root);
self.node.set_meta("_edit_lock_".into(), Variant::from(true))
}
}
}
}
#[macro_export]
macro_rules! config_node_core {
($node:expr, $($name:expr)?) => {
$(
$node.set_name($name.into());
)?
};
}
#[macro_export]
macro_rules! config_node_props {
($node:expr, { $($key:expr => $value:expr),* }) => {
$(
$node.set_indexed($key.into(), $value );
)*
};
}
#[macro_export(local_inner_macros)]
macro_rules! config_node {
($node:expr, ) => {};
($node:expr, {$($children:tt)*} ) => {
scene!($node, { $($children)*} );
};
($node:expr, ($($props:tt)*) $($other:tt)*) => {
config_node_props!($node, { $($props)* });
config_node!($node, $($other)*);
};
($node:expr, [$($name:expr)?] $($other:tt)*) => {
$(
config_node_core!($node, $name);
)?
config_node!($node, $($other)*);
};
}
#[macro_export(local_inner_macros)]
macro_rules! add_new_child {
($root:expr, ) => {};
($root:expr, $node_type:ident $($options:tt)*) => {
{
use crate::macros::SceneEntryHandler;
let node = $node_type::new_alloc();
config_node!(node, $($options)*);
let mut base = node.upcast::<Node>();
let handler = SceneEntryHandler::new(base.clone());
base.connect(
"tree_entered".into(),
Callable::from_object_method(handler, "call")
);
$root.add_child(base);
}
};
}
#[macro_export(local_inner_macros)]
macro_rules! scene {
// TT muncher
// inner base case
(@inner $root:expr, [ $($accum:tt)* ]) => {
add_new_child!($root, $($accum)*);
};
// inner end of node_tokens handler (handles comma token)
(@inner $root:expr, [ $($accum:tt)* ] , $($rest:tt)* ) => {
add_new_child!($root, $($accum)*);
scene!(@inner $root, [] $($rest)*);
};
// inner token accummulator
(@inner $root:expr, [ $($accum:tt)* ] $current:tt $($rest:tt)* ) => {
scene!( @inner $root, [ $($accum)* $current ] $($rest)*);
};
// {} Wrapper and inner entry point
($root:expr, { $($tokens:tt)* }) => {
scene!( @inner $root, [] $($tokens)*);
};
}
pub(crate) use scene;
@ducklin5
Copy link
Author

Usage:

#[derive(GodotClass)]
#[class(base=PanelContainer)]
struct SampleUI {
    #[base]
    root: Base<PanelContainer>,
}

#[godot_api]
impl PanelContainerVirtual for SampleUI {
    fn init(mut root: Base<PanelContainer>) -> Self {
        scene!(root, {
            VBoxContainer ["Vbox1"] (
                "size.x" => Variant::from(24)
            ) {
                Label ["label1"] (
                    "text" => Variant::from("TEST")
                ) {
                    
                },
                Label  
            },
            HBoxContainer ["BoxName"]
        });

        Self { root }
    }

    fn ready(&mut self) {
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment