Skip to content

Commit 747ae83

Browse files
committedMar 20, 2025
Add node for executing rhai scripts
1 parent fff0a53 commit 747ae83

File tree

15 files changed

+343
-14
lines changed

15 files changed

+343
-14
lines changed
 

‎Cargo.lock

+88
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎editor/src/messages/dialog/simple_dialogs/close_all_documents_dialog.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ impl DialogLayoutHolder for CloseAllDocumentsDialog {
3030

3131
impl LayoutHolder for CloseAllDocumentsDialog {
3232
fn layout(&self) -> Layout {
33-
let unsaved_list = "• ".to_string() + &self.unsaved_document_names.join("\n• ");
33+
let unsaved_list = "• ".to_string() + self.unsaved_document_names.join("\n• ").as_str();
3434

3535
Layout::WidgetLayout(WidgetLayout::new(vec![
3636
LayoutGroup::Row {

‎editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs

+20
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,25 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
130130
description: Cow::Borrowed("The identity node passes its data through. You can use this to organize your node graph."),
131131
properties: Some("identity_properties"),
132132
},
133+
DocumentNodeDefinition {
134+
identifier: "Script",
135+
category: "General",
136+
node_template: NodeTemplate {
137+
document_node: DocumentNode {
138+
implementation: DocumentNodeImplementation::proto("graphene_std::rhai::RhaiNode"),
139+
manual_composition: Some(concrete!(Context)),
140+
inputs: vec![NodeInput::value(TaggedValue::F64(0.), true), NodeInput::value(TaggedValue::String("input".into()), false)],
141+
..Default::default()
142+
},
143+
persistent_node_metadata: DocumentNodePersistentMetadata {
144+
input_properties: vec!["In".into(), "String".into()],
145+
output_names: vec!["Out".to_string()],
146+
..Default::default()
147+
},
148+
},
149+
description: Cow::Borrowed(""),
150+
properties: Some("script_properties"),
151+
},
133152
// TODO: Auto-generate this from its proto node macro
134153
DocumentNodeDefinition {
135154
identifier: "Monitor",
@@ -2897,6 +2916,7 @@ fn static_node_properties() -> NodeProperties {
28972916
"monitor_properties".to_string(),
28982917
Box::new(|_node_id, _context| node_properties::string_properties("The Monitor node is used by the editor to access the data flowing through it.")),
28992918
);
2919+
map.insert("script_properties".to_string(), Box::new(node_properties::script_properties));
29002920
map
29012921
}
29022922

‎editor/src/messages/portfolio/document/node_graph/node_properties.rs

+14
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ pub(crate) fn property_from_type(
266266
}
267267
}
268268
Type::Generic(_) => vec![TextLabel::new("Generic type (not supported)").widget_holder()].into(),
269+
Type::Dynamic => vec![TextLabel::new("Dynamic type (not supported)").widget_holder()].into(),
269270
Type::Fn(_, out) => return property_from_type(node_id, index, out, number_options, context),
270271
Type::Future(out) => return property_from_type(node_id, index, out, number_options, context),
271272
};
@@ -2555,3 +2556,16 @@ pub fn math_properties(node_id: NodeId, context: &mut NodePropertiesContext) ->
25552556
LayoutGroup::Row { widgets: operand_a_hint }.with_tooltip(r#""A" is fed by the value from the previous node in the primary data flow, or it is 0 if disconnected"#),
25562557
]
25572558
}
2559+
2560+
pub(crate) fn script_properties(node_id: NodeId, context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
2561+
let document_node = match get_document_node(node_id, context) {
2562+
Ok(document_node) => document_node,
2563+
Err(err) => {
2564+
log::error!("Could not get document node in script_properties: {err}");
2565+
return Vec::new();
2566+
}
2567+
};
2568+
let source = text_area_widget(document_node, node_id, 1, "Code", false);
2569+
2570+
vec![LayoutGroup::Row { widgets: source }]
2571+
}

‎editor/src/messages/portfolio/document/overlays/grid_overlays.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use graphene_std::vector::style::FillChoice;
99

1010
fn grid_overlay_rectangular(document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, spacing: DVec2) {
1111
let origin = document.snapping_state.grid.origin;
12-
let grid_color = "#".to_string() + &document.snapping_state.grid.grid_color.to_rgba_hex_srgb();
12+
let grid_color = "#".to_string() + document.snapping_state.grid.grid_color.to_rgba_hex_srgb().as_str();
1313
let Some(spacing) = GridSnapping::compute_rectangle_spacing(spacing, &document.document_ptz) else {
1414
return;
1515
};
@@ -48,7 +48,7 @@ fn grid_overlay_rectangular(document: &DocumentMessageHandler, overlay_context:
4848
// TODO: Implement this with a dashed line (`set_line_dash`), with integer spacing which is continuously adjusted to correct the accumulated error.
4949
fn grid_overlay_rectangular_dot(document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, spacing: DVec2) {
5050
let origin = document.snapping_state.grid.origin;
51-
let grid_color = "#".to_string() + &document.snapping_state.grid.grid_color.to_rgba_hex_srgb();
51+
let grid_color = "#".to_string() + document.snapping_state.grid.grid_color.to_rgba_hex_srgb().as_str();
5252
let Some(spacing) = GridSnapping::compute_rectangle_spacing(spacing, &document.document_ptz) else {
5353
return;
5454
};
@@ -82,7 +82,7 @@ fn grid_overlay_rectangular_dot(document: &DocumentMessageHandler, overlay_conte
8282
}
8383

8484
fn grid_overlay_isometric(document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, y_axis_spacing: f64, angle_a: f64, angle_b: f64) {
85-
let grid_color = "#".to_string() + &document.snapping_state.grid.grid_color.to_rgba_hex_srgb();
85+
let grid_color = "#".to_string() + document.snapping_state.grid.grid_color.to_rgba_hex_srgb().as_str();
8686
let cmp = |a: &f64, b: &f64| a.partial_cmp(b).unwrap();
8787
let origin = document.snapping_state.grid.origin;
8888
let document_to_viewport = document.navigation_handler.calculate_offset_transform(overlay_context.size / 2., &document.document_ptz);
@@ -125,7 +125,7 @@ fn grid_overlay_isometric(document: &DocumentMessageHandler, overlay_context: &m
125125
}
126126

127127
fn grid_overlay_isometric_dot(document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, y_axis_spacing: f64, angle_a: f64, angle_b: f64) {
128-
let grid_color = "#".to_string() + &document.snapping_state.grid.grid_color.to_rgba_hex_srgb();
128+
let grid_color = "#".to_string() + document.snapping_state.grid.grid_color.to_rgba_hex_srgb().as_str();
129129
let cmp = |a: &f64, b: &f64| a.partial_cmp(b).unwrap();
130130
let origin = document.snapping_state.grid.origin;
131131
let document_to_viewport = document.navigation_handler.calculate_offset_transform(overlay_context.size / 2., &document.document_ptz);

‎editor/src/messages/tool/tool_messages/text_tool.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ impl Fsm for TextToolFsmState {
473473
if far.x != 0. && far.y != 0. {
474474
let quad = Quad::from_box([DVec2::ZERO, far]);
475475
let transformed_quad = document.metadata().transform_to_viewport(tool_data.layer) * quad;
476-
overlay_context.quad(transformed_quad, Some(&("#".to_string() + &fill_color)));
476+
overlay_context.quad(transformed_quad, Some(&("#".to_string() + fill_color.as_str())));
477477
}
478478
}
479479

@@ -488,11 +488,11 @@ impl Fsm for TextToolFsmState {
488488
for layer in document.intersect_quad_no_artboards(quad, input) {
489489
overlay_context.quad(
490490
Quad::from_box(document.metadata().bounding_box_viewport(layer).unwrap_or([DVec2::ZERO; 2])),
491-
Some(&("#".to_string() + &fill_color)),
491+
Some(&("#".to_string() + fill_color.as_str())),
492492
);
493493
}
494494

495-
overlay_context.quad(quad, Some(&("#".to_string() + &fill_color)));
495+
overlay_context.quad(quad, Some(&("#".to_string() + fill_color.as_str())));
496496
}
497497

498498
// TODO: implement bounding box for multiple layers

‎editor/src/node_graph_executor.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -668,10 +668,10 @@ impl NodeGraphExecutor {
668668
..
669669
} = export_config;
670670

671-
let file_suffix = &format!(".{file_type:?}").to_lowercase();
671+
let file_suffix = format!(".{file_type:?}").to_lowercase();
672672
let name = match file_name.ends_with(FILE_SAVE_SUFFIX) {
673-
true => file_name.replace(FILE_SAVE_SUFFIX, file_suffix),
674-
false => file_name + file_suffix,
673+
true => file_name.replace(FILE_SAVE_SUFFIX, &file_suffix),
674+
false => file_name + file_suffix.as_str(),
675675
};
676676

677677
if file_type == FileType::Svg {

‎node-graph/gcore/src/registry.rs

+25
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,31 @@ impl<I, O> DowncastBothNode<I, O> {
198198
}
199199
}
200200
}
201+
/// Boxes the input and downcasts the output.
202+
/// Wraps around a node taking Box<dyn DynAny> and returning Box<dyn DynAny>
203+
#[derive(Clone)]
204+
pub struct DowncastNoneNode {
205+
node: SharedNodeContainer,
206+
}
207+
impl<'input> Node<'input, Any<'input>> for DowncastNoneNode {
208+
type Output = FutureAny<'input>;
209+
#[inline]
210+
fn eval(&'input self, input: Any<'input>) -> Self::Output {
211+
self.node.eval(input)
212+
}
213+
fn reset(&self) {
214+
self.node.reset();
215+
}
216+
217+
fn serialize(&self) -> Option<std::sync::Arc<dyn core::any::Any + Send + Sync>> {
218+
self.node.serialize()
219+
}
220+
}
221+
impl DowncastNoneNode {
222+
pub const fn new(node: SharedNodeContainer) -> Self {
223+
Self { node }
224+
}
225+
}
201226
pub struct FutureWrapperNode<Node> {
202227
node: Node,
203228
}

‎node-graph/gcore/src/types.rs

+15
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ pub enum Type {
207207
Fn(Box<Type>, Box<Type>),
208208
/// Represents a future which promises to return the inner type.
209209
Future(Box<Type>),
210+
Dynamic,
210211
}
211212

212213
impl Default for Type {
@@ -258,6 +259,15 @@ impl Type {
258259
_ => None,
259260
}
260261
}
262+
pub fn fn_fut_output(&self) -> Option<&Type> {
263+
match self {
264+
Type::Fn(_, second) => match second.as_ref() {
265+
Type::Future(fut) => Some(fut),
266+
_ => None,
267+
},
268+
_ => None,
269+
}
270+
}
261271

262272
pub fn function(input: &Type, output: &Type) -> Type {
263273
Type::Fn(Box::new(input.clone()), Box::new(output.clone()))
@@ -281,6 +291,7 @@ impl Type {
281291
Self::Concrete(ty) => Some(ty.size),
282292
Self::Fn(_, _) => None,
283293
Self::Future(_) => None,
294+
Self::Dynamic => None,
284295
}
285296
}
286297

@@ -290,6 +301,7 @@ impl Type {
290301
Self::Concrete(ty) => Some(ty.align),
291302
Self::Fn(_, _) => None,
292303
Self::Future(_) => None,
304+
Self::Dynamic => None,
293305
}
294306
}
295307

@@ -299,6 +311,7 @@ impl Type {
299311
Self::Concrete(_) => self,
300312
Self::Fn(_, output) => output.nested_type(),
301313
Self::Future(output) => output.nested_type(),
314+
Self::Dynamic => self,
302315
}
303316
}
304317
}
@@ -320,6 +333,7 @@ impl core::fmt::Debug for Type {
320333
Self::Concrete(arg0) => write!(f, "Concrete<{}>", format_type(&arg0.name)),
321334
Self::Fn(arg0, arg1) => write!(f, "{arg0:?} → {arg1:?}"),
322335
Self::Future(arg0) => write!(f, "Future<{arg0:?}>"),
336+
Self::Dynamic => write!(f, "Dynamic"),
323337
}
324338
}
325339
}
@@ -331,6 +345,7 @@ impl std::fmt::Display for Type {
331345
Type::Concrete(ty) => write!(f, "{}", format_type(&ty.name)),
332346
Type::Fn(input, output) => write!(f, "{input} → {output}"),
333347
Type::Future(ty) => write!(f, "Future<{ty}>"),
348+
Self::Dynamic => write!(f, "Dynamic"),
334349
}
335350
}
336351
}

0 commit comments

Comments
 (0)
Failed to load comments.