Skip to main content

azul_layout/widgets/
node_graph.rs

1use alloc::vec::Vec;
2use core::fmt;
3
4use azul_core::{
5    callbacks::{CoreCallback, CoreCallbackData, Update},
6    dom::{Dom, EventFilter, HoverEventFilter, IdOrClass, IdOrClass::Class, IdOrClassVec},
7    geom::{LogicalPosition, LogicalRect, LogicalSize, PhysicalSizeU32},
8    gl::Texture,
9    menu::{Menu, MenuItem, StringMenuItem},
10    refany::{OptionRefAny, RefAny},
11    resources::{ImageRef, RawImageFormat},
12    svg::{SvgPath, SvgPathElement, SvgStrokeStyle, TessellatedGPUSvgNode},
13    window::CursorPosition::InWindow,
14};
15use azul_css::{
16    dynamic_selector::{CssPropertyWithConditions, CssPropertyWithConditionsVec},
17    props::{
18        basic::*,
19        layout::*,
20        property::{CssProperty, *},
21        style::*,
22    },
23    *,
24};
25
26use crate::{
27    callbacks::{Callback, CallbackInfo},
28    extra::coloru_from_str,
29    widgets::{
30        check_box::{CheckBox, CheckBoxOnToggleCallbackType, CheckBoxState},
31        color_input::{ColorInput, ColorInputOnValueChangeCallbackType, ColorInputState},
32        file_input::{FileInput, FileInputOnPathChangeCallbackType, FileInputState},
33        number_input::{NumberInput, NumberInputOnFocusLostCallbackType, NumberInputState},
34        text_input::{TextInput, TextInputOnFocusLostCallbackType, TextInputState},
35    },
36};
37
38/// Same as the NodeGraph but without generics and without the actual data
39#[derive(Debug, Clone)]
40#[repr(C)]
41pub struct NodeGraph {
42    pub node_types: NodeTypeIdInfoMapVec,
43    pub input_output_types: InputOutputTypeIdInfoMapVec,
44    pub nodes: NodeIdNodeMapVec,
45    pub allow_multiple_root_nodes: bool,
46    pub offset: LogicalPosition,
47    pub style: NodeGraphStyle,
48    pub callbacks: NodeGraphCallbacks,
49    pub add_node_str: AzString,
50    pub scale_factor: f32,
51}
52
53impl Default for NodeGraph {
54    fn default() -> Self {
55        Self {
56            node_types: NodeTypeIdInfoMapVec::from_const_slice(&[]),
57            input_output_types: InputOutputTypeIdInfoMapVec::from_const_slice(&[]),
58            nodes: NodeIdNodeMapVec::from_const_slice(&[]),
59            allow_multiple_root_nodes: false,
60            offset: LogicalPosition::zero(),
61            style: NodeGraphStyle::Default,
62            callbacks: NodeGraphCallbacks::default(),
63            add_node_str: AzString::from_const_str(""),
64            scale_factor: 1.0,
65        }
66    }
67}
68
69impl NodeGraph {
70    /// Generates a new NodeId that is unique in the graph
71    pub fn generate_unique_node_id(&self) -> NodeGraphNodeId {
72        NodeGraphNodeId {
73            inner: self
74                .nodes
75                .iter()
76                .map(|i| i.node_id.inner)
77                .max()
78                .unwrap_or(0)
79                .saturating_add(1),
80        }
81    }
82}
83
84#[derive(Debug, Clone)]
85#[repr(C)]
86pub struct NodeTypeIdInfoMap {
87    pub node_type_id: NodeTypeId,
88    pub node_type_info: NodeTypeInfo,
89}
90
91impl_vec!(
92    NodeTypeIdInfoMap,
93    NodeTypeIdInfoMapVec,
94    NodeTypeIdInfoMapVecDestructor,
95    NodeTypeIdInfoMapVecDestructorType
96);
97impl_vec_clone!(
98    NodeTypeIdInfoMap,
99    NodeTypeIdInfoMapVec,
100    NodeTypeIdInfoMapVecDestructor
101);
102impl_vec_mut!(NodeTypeIdInfoMap, NodeTypeIdInfoMapVec);
103impl_vec_debug!(NodeTypeIdInfoMap, NodeTypeIdInfoMapVec);
104
105#[derive(Debug, Clone)]
106#[repr(C)]
107pub struct InputOutputTypeIdInfoMap {
108    pub io_type_id: InputOutputTypeId,
109    pub io_info: InputOutputInfo,
110}
111
112impl_vec!(
113    InputOutputTypeIdInfoMap,
114    InputOutputTypeIdInfoMapVec,
115    InputOutputTypeIdInfoMapVecDestructor,
116    InputOutputTypeIdInfoMapVecDestructorType
117);
118impl_vec_clone!(
119    InputOutputTypeIdInfoMap,
120    InputOutputTypeIdInfoMapVec,
121    InputOutputTypeIdInfoMapVecDestructor
122);
123impl_vec_mut!(InputOutputTypeIdInfoMap, InputOutputTypeIdInfoMapVec);
124impl_vec_debug!(InputOutputTypeIdInfoMap, InputOutputTypeIdInfoMapVec);
125
126#[derive(Debug, Clone)]
127#[repr(C)]
128pub struct NodeIdNodeMap {
129    pub node_id: NodeGraphNodeId,
130    pub node: Node,
131}
132
133impl_vec!(
134    NodeIdNodeMap,
135    NodeIdNodeMapVec,
136    NodeIdNodeMapVecDestructor,
137    NodeIdNodeMapVecDestructorType
138);
139impl_vec_clone!(NodeIdNodeMap, NodeIdNodeMapVec, NodeIdNodeMapVecDestructor);
140impl_vec_mut!(NodeIdNodeMap, NodeIdNodeMapVec);
141impl_vec_debug!(NodeIdNodeMap, NodeIdNodeMapVec);
142
143#[derive(Debug, Copy, Clone)]
144#[repr(C)]
145pub enum NodeGraphStyle {
146    Default,
147    // to be extended
148}
149
150#[derive(Default, Debug, Clone)]
151#[repr(C)]
152pub struct NodeGraphCallbacks {
153    pub on_node_added: OptionOnNodeAdded,
154    pub on_node_removed: OptionOnNodeRemoved,
155    pub on_node_dragged: OptionOnNodeDragged,
156    pub on_node_graph_dragged: OptionOnNodeGraphDragged,
157    pub on_node_connected: OptionOnNodeConnected,
158    pub on_node_input_disconnected: OptionOnNodeInputDisconnected,
159    pub on_node_output_disconnected: OptionOnNodeOutputDisconnected,
160    pub on_node_field_edited: OptionOnNodeFieldEdited,
161}
162
163pub type OnNodeAddedCallbackType = extern "C" fn(
164    refany: RefAny,
165    info: CallbackInfo,
166    new_node_type: NodeTypeId,
167    new_node_id: NodeGraphNodeId,
168    new_node_position: NodePosition,
169) -> Update;
170impl_widget_callback!(
171    OnNodeAdded,
172    OptionOnNodeAdded,
173    OnNodeAddedCallback,
174    OnNodeAddedCallbackType
175);
176
177pub type OnNodeRemovedCallbackType =
178    extern "C" fn(refany: RefAny, info: CallbackInfo, node_id_to_remove: NodeGraphNodeId) -> Update;
179impl_widget_callback!(
180    OnNodeRemoved,
181    OptionOnNodeRemoved,
182    OnNodeRemovedCallback,
183    OnNodeRemovedCallbackType
184);
185
186pub type OnNodeGraphDraggedCallbackType =
187    extern "C" fn(refany: RefAny, info: CallbackInfo, drag_amount: GraphDragAmount) -> Update;
188impl_widget_callback!(
189    OnNodeGraphDragged,
190    OptionOnNodeGraphDragged,
191    OnNodeGraphDraggedCallback,
192    OnNodeGraphDraggedCallbackType
193);
194
195pub type OnNodeDraggedCallbackType = extern "C" fn(
196    refany: RefAny,
197    info: CallbackInfo,
198    node_dragged: NodeGraphNodeId,
199    drag_amount: NodeDragAmount,
200) -> Update;
201impl_widget_callback!(
202    OnNodeDragged,
203    OptionOnNodeDragged,
204    OnNodeDraggedCallback,
205    OnNodeDraggedCallbackType
206);
207
208pub type OnNodeConnectedCallbackType = extern "C" fn(
209    refany: RefAny,
210    info: CallbackInfo,
211    input: NodeGraphNodeId,
212    input_index: usize,
213    output: NodeGraphNodeId,
214    output_index: usize,
215) -> Update;
216impl_widget_callback!(
217    OnNodeConnected,
218    OptionOnNodeConnected,
219    OnNodeConnectedCallback,
220    OnNodeConnectedCallbackType
221);
222
223pub type OnNodeInputDisconnectedCallbackType = extern "C" fn(
224    refany: RefAny,
225    info: CallbackInfo,
226    input: NodeGraphNodeId,
227    input_index: usize,
228) -> Update;
229impl_widget_callback!(
230    OnNodeInputDisconnected,
231    OptionOnNodeInputDisconnected,
232    OnNodeInputDisconnectedCallback,
233    OnNodeInputDisconnectedCallbackType
234);
235
236pub type OnNodeOutputDisconnectedCallbackType = extern "C" fn(
237    refany: RefAny,
238    info: CallbackInfo,
239    output: NodeGraphNodeId,
240    output_index: usize,
241) -> Update;
242impl_widget_callback!(
243    OnNodeOutputDisconnected,
244    OptionOnNodeOutputDisconnected,
245    OnNodeOutputDisconnectedCallback,
246    OnNodeOutputDisconnectedCallbackType
247);
248
249pub type OnNodeFieldEditedCallbackType = extern "C" fn(
250    refany: RefAny,
251    info: CallbackInfo,
252    node_id: NodeGraphNodeId,
253    field_id: usize,
254    node_type: NodeTypeId,
255    new_value: NodeTypeFieldValue,
256) -> Update;
257impl_widget_callback!(
258    OnNodeFieldEdited,
259    OptionOnNodeFieldEdited,
260    OnNodeFieldEditedCallback,
261    OnNodeFieldEditedCallbackType
262);
263
264#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
265#[repr(C)]
266pub struct InputOutputTypeId {
267    pub inner: u64,
268}
269
270impl_vec!(
271    InputOutputTypeId,
272    InputOutputTypeIdVec,
273    InputOutputTypeIdVecDestructor,
274    InputOutputTypeIdVecDestructorType
275);
276impl_vec_clone!(
277    InputOutputTypeId,
278    InputOutputTypeIdVec,
279    InputOutputTypeIdVecDestructor
280);
281impl_vec_mut!(InputOutputTypeId, InputOutputTypeIdVec);
282impl_vec_debug!(InputOutputTypeId, InputOutputTypeIdVec);
283
284#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
285#[repr(C)]
286pub struct NodeTypeId {
287    pub inner: u64,
288}
289
290#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
291#[repr(C)]
292pub struct NodeGraphNodeId {
293    pub inner: u64,
294}
295
296#[derive(Debug, Clone)]
297#[repr(C)]
298pub struct Node {
299    pub node_type: NodeTypeId,
300    pub position: NodePosition,
301    pub fields: NodeTypeFieldVec,
302    pub connect_in: InputConnectionVec,
303    pub connect_out: OutputConnectionVec,
304}
305
306#[derive(Debug, Clone)]
307#[repr(C)]
308pub struct NodeTypeField {
309    pub key: AzString,
310    pub value: NodeTypeFieldValue,
311}
312
313impl_vec!(
314    NodeTypeField,
315    NodeTypeFieldVec,
316    NodeTypeFieldVecDestructor,
317    NodeTypeFieldVecDestructorType
318);
319impl_vec_clone!(NodeTypeField, NodeTypeFieldVec, NodeTypeFieldVecDestructor);
320impl_vec_debug!(NodeTypeField, NodeTypeFieldVec);
321impl_vec_mut!(NodeTypeField, NodeTypeFieldVec);
322
323#[derive(Debug, Clone)]
324#[repr(C, u8)]
325pub enum NodeTypeFieldValue {
326    TextInput(AzString),
327    NumberInput(f32),
328    CheckBox(bool),
329    ColorInput(ColorU),
330    FileInput(OptionString),
331}
332
333#[derive(Debug, Clone)]
334#[repr(C)]
335pub struct InputConnection {
336    pub input_index: usize,
337    pub connects_to: OutputNodeAndIndexVec,
338}
339
340impl_vec!(
341    InputConnection,
342    InputConnectionVec,
343    InputConnectionVecDestructor,
344    InputConnectionVecDestructorType
345);
346impl_vec_clone!(
347    InputConnection,
348    InputConnectionVec,
349    InputConnectionVecDestructor
350);
351impl_vec_debug!(InputConnection, InputConnectionVec);
352impl_vec_mut!(InputConnection, InputConnectionVec);
353
354#[derive(Debug, Clone)]
355#[repr(C)]
356pub struct OutputNodeAndIndex {
357    pub node_id: NodeGraphNodeId,
358    pub output_index: usize,
359}
360
361impl_vec!(
362    OutputNodeAndIndex,
363    OutputNodeAndIndexVec,
364    OutputNodeAndIndexVecDestructor,
365    OutputNodeAndIndexVecDestructorType
366);
367impl_vec_clone!(
368    OutputNodeAndIndex,
369    OutputNodeAndIndexVec,
370    OutputNodeAndIndexVecDestructor
371);
372impl_vec_debug!(OutputNodeAndIndex, OutputNodeAndIndexVec);
373impl_vec_mut!(OutputNodeAndIndex, OutputNodeAndIndexVec);
374
375#[derive(Debug, Clone)]
376#[repr(C)]
377pub struct OutputConnection {
378    pub output_index: usize,
379    pub connects_to: InputNodeAndIndexVec,
380}
381
382impl_vec!(
383    OutputConnection,
384    OutputConnectionVec,
385    OutputConnectionVecDestructor,
386    OutputConnectionVecDestructorType
387);
388impl_vec_clone!(
389    OutputConnection,
390    OutputConnectionVec,
391    OutputConnectionVecDestructor
392);
393impl_vec_debug!(OutputConnection, OutputConnectionVec);
394impl_vec_mut!(OutputConnection, OutputConnectionVec);
395
396#[derive(Debug, Clone, PartialEq)]
397#[repr(C)]
398pub struct InputNodeAndIndex {
399    pub node_id: NodeGraphNodeId,
400    pub input_index: usize,
401}
402
403impl_vec!(
404    InputNodeAndIndex,
405    InputNodeAndIndexVec,
406    InputNodeAndIndexVecDestructor,
407    InputNodeAndIndexVecDestructorType
408);
409impl_vec_clone!(
410    InputNodeAndIndex,
411    InputNodeAndIndexVec,
412    InputNodeAndIndexVecDestructor
413);
414impl_vec_debug!(InputNodeAndIndex, InputNodeAndIndexVec);
415impl_vec_mut!(InputNodeAndIndex, InputNodeAndIndexVec);
416
417#[derive(Debug, Clone)]
418#[repr(C)]
419pub struct NodeTypeInfo {
420    /// Whether this node type is a "root" type
421    pub is_root: bool,
422    /// Name of the node type
423    pub node_type_name: AzString,
424    /// List of inputs for this node
425    pub inputs: InputOutputTypeIdVec,
426    /// List of outputs for this node
427    pub outputs: InputOutputTypeIdVec,
428}
429
430#[derive(Debug, Clone)]
431#[repr(C)]
432pub struct InputOutputInfo {
433    /// Data type of this input / output
434    pub data_type: AzString,
435    /// Which color to use for the input / output
436    pub color: ColorU,
437}
438
439/// Things only relevant to the display of the node in an interactive editor
440/// - such as x and y position in the node graph, name, etc.
441#[derive(Debug, Copy, Clone)]
442#[repr(C)]
443pub struct NodePosition {
444    /// X Position of the node
445    pub x: f32,
446    /// Y Position of the node
447    pub y: f32,
448}
449
450#[derive(Debug, Copy, Clone, PartialEq, Eq)]
451#[repr(C)]
452pub enum NodeGraphError {
453    /// MIME type is not the same (for example: connection "spatialdata/point"
454    /// with a node that expects "spatialdata/line")
455    NodeMimeTypeMismatch,
456    /// Invalid index when accessing a node in / output
457    NodeInvalidIndex,
458    /// The in-/ output matching encountered a non-existing hash to a node that doesn't exist
459    NodeInvalidNode,
460    /// Root node is missing from the graph tree
461    NoRootNode,
462}
463
464impl fmt::Display for NodeGraphError {
465    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
466        use self::NodeGraphError::*;
467        match self {
468            NodeMimeTypeMismatch => write!(f, "MIME type mismatch"),
469            NodeInvalidIndex => write!(f, "Invalid node index"),
470            NodeInvalidNode => write!(f, "Invalid node"),
471            NoRootNode => write!(f, "No root node found"),
472        }
473    }
474}
475
476#[derive(Debug, Copy, Clone, PartialEq)]
477#[repr(C)]
478pub struct GraphDragAmount {
479    pub x: f32,
480    pub y: f32,
481}
482
483#[derive(Debug, Copy, Clone, PartialEq)]
484#[repr(C)]
485pub struct NodeDragAmount {
486    pub x: f32,
487    pub y: f32,
488}
489
490impl NodeGraph {
491    pub fn swap_with_default(&mut self) -> Self {
492        let mut default = Self::default();
493        ::core::mem::swap(&mut default, self);
494        default
495    }
496
497    /// Connects the current nodes input with another nodes output
498    ///
499    /// ## Inputs
500    ///
501    /// - `output_node_id`: The ID of the output node (index in the NodeGraphs internal BTree)
502    /// - `output_index`: The index of the output *on the output node*
503    /// - `input_node_id`: Same as output_node_id, but for the input node
504    /// - `input_index`: Same as output_index, but for the input node
505    ///
506    /// ## Returns
507    ///
508    /// One of:
509    ///
510    /// - `NodeGraphError::NodeInvalidNode(n)`: The node at `` of the inputs does not exist
511    /// - `NodeGraphError::NodeInvalidIndex(n, u)`: One node has an invalid `output` or `input`
512    ///   index
513    /// - `NodeGraphError::NodeMimeTypeMismatch`: The types of two connected `outputs` and `inputs`
514    ///   isn't the same
515    /// - `Ok`: The insertion of the new node went well.
516    fn connect_input_output(
517        &mut self,
518        input_node_id: NodeGraphNodeId,
519        input_index: usize,
520        output_node_id: NodeGraphNodeId,
521        output_index: usize,
522    ) -> Result<(), NodeGraphError> {
523        // Verify that the node type of the connection matches
524        let _ =
525            self.verify_nodetype_match(output_node_id, output_index, input_node_id, input_index)?;
526
527        // connect input -> output
528        if let Some(input_node) = self
529            .nodes
530            .as_mut()
531            .iter_mut()
532            .find(|i| i.node_id == input_node_id)
533        {
534            if let Some(position) = input_node
535                .node
536                .connect_in
537                .as_ref()
538                .iter()
539                .position(|i| i.input_index == input_index)
540            {
541                input_node.node.connect_in.as_mut()[position]
542                    .connects_to
543                    .push(OutputNodeAndIndex {
544                        node_id: output_node_id,
545                        output_index,
546                    });
547            } else {
548                input_node.node.connect_in.push(InputConnection {
549                    input_index,
550                    connects_to: vec![OutputNodeAndIndex {
551                        node_id: output_node_id,
552                        output_index,
553                    }]
554                    .into(),
555                })
556            }
557        } else {
558            return Err(NodeGraphError::NodeInvalidNode);
559        }
560
561        // connect output -> input
562        if let Some(output_node) = self
563            .nodes
564            .as_mut()
565            .iter_mut()
566            .find(|i| i.node_id == output_node_id)
567        {
568            if let Some(position) = output_node
569                .node
570                .connect_out
571                .as_ref()
572                .iter()
573                .position(|i| i.output_index == output_index)
574            {
575                output_node.node.connect_out.as_mut()[position]
576                    .connects_to
577                    .push(InputNodeAndIndex {
578                        node_id: input_node_id,
579                        input_index,
580                    });
581            } else {
582                output_node.node.connect_out.push(OutputConnection {
583                    output_index,
584                    connects_to: vec![InputNodeAndIndex {
585                        node_id: input_node_id,
586                        input_index,
587                    }]
588                    .into(),
589                })
590            }
591        } else {
592            return Err(NodeGraphError::NodeInvalidNode);
593        }
594
595        Ok(())
596    }
597
598    /// Disconnect an input if it is connected to an output
599    ///
600    /// # Inputs
601    ///
602    /// - `input_node_id`: The ID of the input node (index in the NodeGraphs internal BTree)
603    /// - `input_index`: The index of the input *on the input node*
604    ///
605    /// # Returns
606    ///
607    /// - `Err(NodeGraphError::NodeInvalidNode(n))`: The node at index `input_node_id` does not
608    ///   exist
609    /// - `Err(NodeGraphError::NodeInvalidIndex(n, u))`: One node has an invalid `input` or `output`
610    ///   index
611    /// - `Err(NodeGraphError::NodeMimeTypeMismatch)`: The types of two connected `input` and
612    ///   `output` does not match
613    /// - `Ok(())`: The insertion of the new node went well.
614    fn disconnect_input(
615        &mut self,
616        input_node_id: NodeGraphNodeId,
617        input_index: usize,
618    ) -> Result<(), NodeGraphError> {
619        let output_connections = {
620            let input_node = self
621                .nodes
622                .as_ref()
623                .iter()
624                .find(|i| i.node_id == input_node_id)
625                .ok_or(NodeGraphError::NodeInvalidNode)?;
626
627            match input_node
628                .node
629                .connect_in
630                .iter()
631                .find(|i| i.input_index == input_index)
632            {
633                None => return Ok(()),
634                Some(s) => s.connects_to.clone(),
635            }
636        };
637
638        // for every output that this input was connected to...
639        for OutputNodeAndIndex {
640            node_id,
641            output_index,
642        } in output_connections.as_ref().iter()
643        {
644            let output_node_id = *node_id;
645            let output_index = *output_index;
646
647            // verify that the node type of the connection matches
648            let _ = self.verify_nodetype_match(
649                output_node_id,
650                output_index,
651                input_node_id,
652                input_index,
653            )?;
654
655            // disconnect input -> output
656
657            if let Some(input_node) = self
658                .nodes
659                .as_mut()
660                .iter_mut()
661                .find(|i| i.node_id == input_node_id)
662            {
663                if let Some(position) = input_node
664                    .node
665                    .connect_in
666                    .iter()
667                    .position(|i| i.input_index == input_index)
668                {
669                    input_node.node.connect_in.remove(position);
670                }
671            } else {
672                return Err(NodeGraphError::NodeInvalidNode);
673            }
674
675            if let Some(output_node) = self
676                .nodes
677                .as_mut()
678                .iter_mut()
679                .find(|i| i.node_id == output_node_id)
680            {
681                if let Some(position) = output_node
682                    .node
683                    .connect_out
684                    .iter()
685                    .position(|i| i.output_index == output_index)
686                {
687                    output_node.node.connect_out.remove(position);
688                }
689            } else {
690                return Err(NodeGraphError::NodeInvalidNode);
691            }
692        }
693
694        Ok(())
695    }
696
697    /// Disconnect an output if it is connected to an output
698    ///
699    /// # Inputs
700    ///
701    /// - `output_node_id`: The ID of the input node (index in the NodeGraphs internal BTree)
702    /// - `output_index`: The index of the input *on the input node*
703    ///
704    /// # Returns
705    ///
706    /// - `Err(NodeGraphError::NodeInvalidNode(n))`: The node at index `output_node_id` does not
707    ///   exist
708    /// - `Err(NodeGraphError::NodeInvalidIndex(n, u))`: One node has an invalid `input` or `output`
709    ///   index
710    /// - `Err(NodeGraphError::NodeMimeTypeMismatch)`: The types of two connected `input` and
711    ///   `output` does not match
712    /// - `Ok(())`: The insertion of the new node went well.
713    fn disconnect_output(
714        &mut self,
715        output_node_id: NodeGraphNodeId,
716        output_index: usize,
717    ) -> Result<(), NodeGraphError> {
718        let input_connections = {
719            let output_node = self
720                .nodes
721                .as_ref()
722                .iter()
723                .find(|i| i.node_id == output_node_id)
724                .ok_or(NodeGraphError::NodeInvalidNode)?;
725
726            match output_node
727                .node
728                .connect_out
729                .iter()
730                .find(|i| i.output_index == output_index)
731            {
732                None => return Ok(()),
733                Some(s) => s.connects_to.clone(),
734            }
735        };
736
737        for InputNodeAndIndex {
738            node_id,
739            input_index,
740        } in input_connections.iter()
741        {
742            let input_node_id = *node_id;
743            let input_index = *input_index;
744
745            // verify that the node type of the connection matches
746            let _ = self.verify_nodetype_match(
747                output_node_id,
748                output_index,
749                input_node_id,
750                input_index,
751            )?;
752
753            if let Some(output_node) = self
754                .nodes
755                .as_mut()
756                .iter_mut()
757                .find(|i| i.node_id == output_node_id)
758            {
759                if let Some(position) = output_node
760                    .node
761                    .connect_out
762                    .iter()
763                    .position(|i| i.output_index == output_index)
764                {
765                    output_node.node.connect_out.remove(position);
766                }
767            } else {
768                return Err(NodeGraphError::NodeInvalidNode);
769            }
770
771            if let Some(input_node) = self
772                .nodes
773                .as_mut()
774                .iter_mut()
775                .find(|i| i.node_id == input_node_id)
776            {
777                if let Some(position) = input_node
778                    .node
779                    .connect_in
780                    .iter()
781                    .position(|i| i.input_index == input_index)
782                {
783                    input_node.node.connect_in.remove(position);
784                }
785            } else {
786                return Err(NodeGraphError::NodeInvalidNode);
787            }
788        }
789
790        Ok(())
791    }
792
793    /// Verifies that the node types of two connections match
794    fn verify_nodetype_match(
795        &self,
796        output_node_id: NodeGraphNodeId,
797        output_index: usize,
798        input_node_id: NodeGraphNodeId,
799        input_index: usize,
800    ) -> Result<(), NodeGraphError> {
801        let output_node = self
802            .nodes
803            .iter()
804            .find(|i| i.node_id == output_node_id)
805            .ok_or(NodeGraphError::NodeInvalidNode)?;
806
807        let output_node_type = self
808            .node_types
809            .iter()
810            .find(|i| i.node_type_id == output_node.node.node_type)
811            .ok_or(NodeGraphError::NodeInvalidNode)?;
812
813        let output_type = output_node_type
814            .node_type_info
815            .outputs
816            .as_ref()
817            .get(output_index)
818            .copied()
819            .ok_or(NodeGraphError::NodeInvalidIndex)?;
820
821        let input_node = self
822            .nodes
823            .iter()
824            .find(|i| i.node_id == input_node_id)
825            .ok_or(NodeGraphError::NodeInvalidNode)?;
826
827        let input_node_type = self
828            .node_types
829            .iter()
830            .find(|i| i.node_type_id == input_node.node.node_type)
831            .ok_or(NodeGraphError::NodeInvalidNode)?;
832
833        let input_type = input_node_type
834            .node_type_info
835            .inputs
836            .as_ref()
837            .get(input_index)
838            .copied()
839            .ok_or(NodeGraphError::NodeInvalidIndex)?;
840
841        // Input / Output do not have the same TypeId
842        if input_type != output_type {
843            return Err(NodeGraphError::NodeMimeTypeMismatch);
844        }
845
846        Ok(())
847    }
848
849    pub fn dom(self) -> Dom {
850        static NODEGRAPH_CLASS: &[IdOrClass] = &[Class(AzString::from_const_str("nodegraph"))];
851
852        static NODEGRAPH_BACKGROUND: &[StyleBackgroundContent] = &[StyleBackgroundContent::Image(
853            AzString::from_const_str("nodegraph-background"),
854        )];
855
856        static NODEGRAPH_NODES_CONTAINER_CLASS: &[IdOrClass] =
857            &[Class(AzString::from_const_str("nodegraph-nodes-container"))];
858
859        static NODEGRAPH_NODES_CONTAINER_PROPS: &[CssPropertyWithConditions] = &[
860            CssPropertyWithConditions::simple(CssProperty::flex_grow(LayoutFlexGrow::const_new(1))),
861            CssPropertyWithConditions::simple(CssProperty::position(LayoutPosition::Absolute)),
862        ];
863
864        let nodegraph_wrapper_props = vec![
865            CssPropertyWithConditions::simple(CssProperty::overflow_x(LayoutOverflow::Hidden)),
866            CssPropertyWithConditions::simple(CssProperty::overflow_y(LayoutOverflow::Hidden)),
867            CssPropertyWithConditions::simple(CssProperty::flex_grow(LayoutFlexGrow::const_new(1))),
868            CssPropertyWithConditions::simple(CssProperty::background_content(
869                StyleBackgroundContentVec::from_const_slice(NODEGRAPH_BACKGROUND),
870            )),
871            CssPropertyWithConditions::simple(CssProperty::background_repeat(
872                vec![StyleBackgroundRepeat::PatternRepeat].into(),
873            )),
874            CssPropertyWithConditions::simple(CssProperty::background_position(
875                vec![StyleBackgroundPosition {
876                    horizontal: BackgroundPositionHorizontal::Exact(PixelValue::const_px(0)),
877                    vertical: BackgroundPositionVertical::Exact(PixelValue::const_px(0)),
878                }]
879                .into(),
880            )),
881        ];
882
883        let nodegraph_props = vec![
884            CssPropertyWithConditions::simple(CssProperty::overflow_x(LayoutOverflow::Hidden)),
885            CssPropertyWithConditions::simple(CssProperty::overflow_y(LayoutOverflow::Hidden)),
886            CssPropertyWithConditions::simple(CssProperty::flex_grow(LayoutFlexGrow::const_new(1))),
887            CssPropertyWithConditions::simple(CssProperty::position(LayoutPosition::Relative)),
888        ];
889
890        let node_connection_marker = RefAny::new(NodeConnectionMarkerDataset {});
891
892        let node_graph_local_dataset = RefAny::new(NodeGraphLocalDataset {
893            node_graph: self.clone(), // TODO: expensive
894            last_input_or_output_clicked: None,
895            active_node_being_dragged: None,
896            node_connection_marker: node_connection_marker.clone(),
897            callbacks: self.callbacks.clone(),
898        });
899
900        let context_menu = Menu::create(
901            vec![MenuItem::String(
902                StringMenuItem::create(self.add_node_str.clone()).with_children(
903                    self.node_types
904                        .iter()
905                        .map(
906                            |NodeTypeIdInfoMap {
907                                 node_type_id,
908                                 node_type_info,
909                             }| {
910                                let context_menu_local_dataset =
911                                    RefAny::new(ContextMenuEntryLocalDataset {
912                                        node_type: *node_type_id,
913                                        // RefAny<NodeGraphLocalDataset>
914                                        backref: node_graph_local_dataset.clone(),
915                                    });
916
917                                MenuItem::String(
918                                    StringMenuItem::create(
919                                        node_type_info.node_type_name.clone().into(),
920                                    )
921                                    .with_callback(
922                                        context_menu_local_dataset,
923                                        nodegraph_context_menu_click as usize,
924                                    ),
925                                )
926                            },
927                        )
928                        .collect::<Vec<_>>()
929                        .into(),
930                ),
931            )]
932            .into(),
933        );
934
935        Dom::create_div()
936            .with_css_props(nodegraph_wrapper_props.into())
937            .with_context_menu(context_menu)
938            .with_children(
939                vec![Dom::create_div()
940                    .with_ids_and_classes(IdOrClassVec::from_const_slice(NODEGRAPH_CLASS))
941                    .with_css_props(nodegraph_props.into())
942                    .with_callbacks(
943                        vec![
944                            CoreCallbackData {
945                                event: EventFilter::Hover(HoverEventFilter::MouseOver),
946                                refany: node_graph_local_dataset.clone(),
947                                callback: CoreCallback {
948                                    cb: nodegraph_drag_graph_or_nodes as usize as usize,
949                                    ctx: OptionRefAny::None,
950                                },
951                            },
952                            CoreCallbackData {
953                                event: EventFilter::Hover(HoverEventFilter::LeftMouseUp),
954                                refany: node_graph_local_dataset.clone(),
955                                callback: CoreCallback {
956                                    cb: nodegraph_unset_active_node as usize as usize,
957                                    ctx: OptionRefAny::None,
958                                },
959                            },
960                        ]
961                        .into(),
962                    )
963                    .with_children({
964                        vec![
965                            // connections
966                            render_connections(&self, node_connection_marker),
967                            // nodes
968                            self.nodes
969                                .iter()
970                                .filter_map(|NodeIdNodeMap { node_id, node }| {
971                                    let node_type_info = self
972                                        .node_types
973                                        .iter()
974                                        .find(|i| i.node_type_id == node.node_type)?;
975                                    let node_local_dataset = NodeLocalDataset {
976                                        node_id: *node_id,
977                                        backref: node_graph_local_dataset.clone(),
978                                    };
979
980                                    Some(render_node(
981                                        node,
982                                        (self.offset.x, self.offset.y),
983                                        &node_type_info.node_type_info,
984                                        node_local_dataset,
985                                        self.scale_factor,
986                                    ))
987                                })
988                                .collect::<Dom>()
989                                .with_ids_and_classes(IdOrClassVec::from_const_slice(
990                                    NODEGRAPH_NODES_CONTAINER_CLASS,
991                                ))
992                                .with_css_props(CssPropertyWithConditionsVec::from_const_slice(
993                                    NODEGRAPH_NODES_CONTAINER_PROPS,
994                                )),
995                        ]
996                        .into()
997                    })]
998                .into(),
999            )
1000            .with_dataset(Some(node_graph_local_dataset).into())
1001    }
1002}
1003
1004// dataset set on the top-level nodegraph node,
1005// containing all the state of the node graph
1006struct NodeGraphLocalDataset {
1007    node_graph: NodeGraph,
1008    last_input_or_output_clicked: Option<(NodeGraphNodeId, InputOrOutput)>,
1009    // Ref<NodeLocalDataSet> - used as a marker for getting the visual node ID
1010    active_node_being_dragged: Option<(NodeGraphNodeId, RefAny)>,
1011    node_connection_marker: RefAny, // Ref<NodeConnectionMarkerDataset>
1012    callbacks: NodeGraphCallbacks,
1013}
1014
1015struct ContextMenuEntryLocalDataset {
1016    node_type: NodeTypeId,
1017    backref: RefAny, // RefAny<NodeGraphLocalDataset>
1018}
1019
1020struct NodeConnectionMarkerDataset {}
1021
1022struct NodeLocalDataset {
1023    node_id: NodeGraphNodeId,
1024    backref: RefAny, // RefAny<NodeGraphLocalDataset>
1025}
1026
1027#[derive(Debug, Copy, Clone)]
1028enum InputOrOutput {
1029    Input(usize),
1030    Output(usize),
1031}
1032
1033struct NodeInputOutputLocalDataset {
1034    io_id: InputOrOutput,
1035    backref: RefAny, // RefAny<NodeLocalDataset>
1036}
1037
1038struct NodeFieldLocalDataset {
1039    field_idx: usize,
1040    backref: RefAny, // RefAny<NodeLocalDataset>
1041}
1042
1043#[derive(Copy, Clone)]
1044struct ConnectionLocalDataset {
1045    out_node_id: NodeGraphNodeId,
1046    out_idx: usize,
1047    in_node_id: NodeGraphNodeId,
1048    in_idx: usize,
1049    swap_vert: bool,
1050    swap_horz: bool,
1051    color: ColorU,
1052}
1053
1054fn render_node(
1055    node: &Node,
1056    graph_offset: (f32, f32),
1057    node_info: &NodeTypeInfo,
1058    mut node_local_dataset: NodeLocalDataset,
1059    scale_factor: f32,
1060) -> Dom {
1061    use azul_core::dom::{
1062        CssPropertyWithConditions, CssPropertyWithConditionsVec, Dom, DomVec, IdOrClass,
1063        IdOrClass::Class, IdOrClassVec,
1064    };
1065    use azul_css::*;
1066
1067    const STRING_9416190750059025162: AzString = AzString::from_const_str("Material Icons");
1068    const STRING_16146701490593874959: AzString = AzString::from_const_str("sans-serif");
1069    const STYLE_BACKGROUND_CONTENT_524016094839686509_ITEMS: &[StyleBackgroundContent] =
1070        &[StyleBackgroundContent::Color(ColorU {
1071            r: 34,
1072            g: 34,
1073            b: 34,
1074            a: 255,
1075        })];
1076    const STYLE_BACKGROUND_CONTENT_10430246856047584562_ITEMS: &[StyleBackgroundContent] =
1077        &[StyleBackgroundContent::LinearGradient(LinearGradient {
1078            direction: Direction::FromTo(DirectionCorners {
1079                dir_from: DirectionCorner::Left,
1080                dir_to: DirectionCorner::Right,
1081            }),
1082            extend_mode: ExtendMode::Clamp,
1083            stops: NormalizedLinearColorStopVec::from_const_slice(
1084                LINEAR_COLOR_STOP_4373556077110009258_ITEMS,
1085            ),
1086        })];
1087    const STYLE_BACKGROUND_CONTENT_11535310356736632656_ITEMS: &[StyleBackgroundContent] =
1088        &[StyleBackgroundContent::RadialGradient(RadialGradient {
1089            shape: Shape::Ellipse,
1090            extend_mode: ExtendMode::Clamp,
1091            position: StyleBackgroundPosition {
1092                horizontal: BackgroundPositionHorizontal::Left,
1093                vertical: BackgroundPositionVertical::Top,
1094            },
1095            size: RadialGradientSize::FarthestCorner,
1096            stops: NormalizedLinearColorStopVec::from_const_slice(
1097                LINEAR_COLOR_STOP_15596411095679453272_ITEMS,
1098            ),
1099        })];
1100    const STYLE_BACKGROUND_CONTENT_11936041127084538304_ITEMS: &[StyleBackgroundContent] =
1101        &[StyleBackgroundContent::LinearGradient(LinearGradient {
1102            direction: Direction::FromTo(DirectionCorners {
1103                dir_from: DirectionCorner::Right,
1104                dir_to: DirectionCorner::Left,
1105            }),
1106            extend_mode: ExtendMode::Clamp,
1107            stops: NormalizedLinearColorStopVec::from_const_slice(
1108                LINEAR_COLOR_STOP_4373556077110009258_ITEMS,
1109            ),
1110        })];
1111    const STYLE_BACKGROUND_CONTENT_15813232491335471489_ITEMS: &[StyleBackgroundContent] =
1112        &[StyleBackgroundContent::Color(ColorU {
1113            r: 0,
1114            g: 0,
1115            b: 0,
1116            a: 85,
1117        })];
1118    const STYLE_BACKGROUND_CONTENT_17648039690071193942_ITEMS: &[StyleBackgroundContent] =
1119        &[StyleBackgroundContent::LinearGradient(LinearGradient {
1120            direction: Direction::FromTo(DirectionCorners {
1121                dir_from: DirectionCorner::Top,
1122                dir_to: DirectionCorner::Bottom,
1123            }),
1124            extend_mode: ExtendMode::Clamp,
1125            stops: NormalizedLinearColorStopVec::from_const_slice(
1126                LINEAR_COLOR_STOP_7397113864565941600_ITEMS,
1127            ),
1128        })];
1129    const STYLE_TRANSFORM_347117342922946953_ITEMS: &[StyleTransform] =
1130        &[StyleTransform::Translate(StyleTransformTranslate2D {
1131            x: PixelValue::const_px(200),
1132            y: PixelValue::const_px(100),
1133        })];
1134    const STYLE_TRANSFORM_14683950870521466298_ITEMS: &[StyleTransform] =
1135        &[StyleTransform::Translate(StyleTransformTranslate2D {
1136            x: PixelValue::const_px(240),
1137            y: PixelValue::const_px(-10),
1138        })];
1139    const STYLE_FONT_FAMILY_8122988506401935406_ITEMS: &[StyleFontFamily] =
1140        &[StyleFontFamily::System(STRING_16146701490593874959)];
1141    const STYLE_FONT_FAMILY_11383897783350685780_ITEMS: &[StyleFontFamily] =
1142        &[StyleFontFamily::System(STRING_9416190750059025162)];
1143    const LINEAR_COLOR_STOP_4373556077110009258_ITEMS: &[NormalizedLinearColorStop] = &[
1144        NormalizedLinearColorStop {
1145            offset: PercentageValue::const_new(20),
1146            color: ColorU {
1147                r: 0,
1148                g: 0,
1149                b: 0,
1150                a: 204,
1151            },
1152        },
1153        NormalizedLinearColorStop {
1154            offset: PercentageValue::const_new(100),
1155            color: ColorU {
1156                r: 0,
1157                g: 0,
1158                b: 0,
1159                a: 0,
1160            },
1161        },
1162    ];
1163    const LINEAR_COLOR_STOP_7397113864565941600_ITEMS: &[NormalizedLinearColorStop] = &[
1164        NormalizedLinearColorStop {
1165            offset: PercentageValue::const_new(0),
1166            color: ColorU {
1167                r: 229,
1168                g: 57,
1169                b: 53,
1170                a: 255,
1171            },
1172        },
1173        NormalizedLinearColorStop {
1174            offset: PercentageValue::const_new(100),
1175            color: ColorU {
1176                r: 227,
1177                g: 93,
1178                b: 91,
1179                a: 255,
1180            },
1181        },
1182    ];
1183    const LINEAR_COLOR_STOP_15596411095679453272_ITEMS: &[NormalizedLinearColorStop] = &[
1184        NormalizedLinearColorStop {
1185            offset: PercentageValue::const_new(0),
1186            color: ColorU {
1187                r: 47,
1188                g: 49,
1189                b: 54,
1190                a: 255,
1191            },
1192        },
1193        NormalizedLinearColorStop {
1194            offset: PercentageValue::const_new(50),
1195            color: ColorU {
1196                r: 47,
1197                g: 49,
1198                b: 54,
1199                a: 255,
1200            },
1201        },
1202        NormalizedLinearColorStop {
1203            offset: PercentageValue::const_new(100),
1204            color: ColorU {
1205                r: 32,
1206                g: 34,
1207                b: 37,
1208                a: 255,
1209            },
1210        },
1211    ];
1212
1213    const CSS_MATCH_10339190304804100510_PROPERTIES: &[CssPropertyWithConditions] = &[
1214        // .node_output_wrapper
1215        CssPropertyWithConditions::simple(CssProperty::Display(LayoutDisplayValue::Exact(
1216            LayoutDisplay::Flex,
1217        ))),
1218        CssPropertyWithConditions::simple(CssProperty::FlexDirection(
1219            LayoutFlexDirectionValue::Exact(LayoutFlexDirection::Column),
1220        )),
1221        CssPropertyWithConditions::simple(CssProperty::Left(LayoutLeftValue::Exact(LayoutLeft {
1222            inner: PixelValue::const_px(0),
1223        }))),
1224        CssPropertyWithConditions::simple(CssProperty::OverflowX(LayoutOverflowValue::Exact(
1225            LayoutOverflow::Visible,
1226        ))),
1227        CssPropertyWithConditions::simple(CssProperty::OverflowY(LayoutOverflowValue::Exact(
1228            LayoutOverflow::Visible,
1229        ))),
1230        CssPropertyWithConditions::simple(CssProperty::Position(LayoutPositionValue::Exact(
1231            LayoutPosition::Absolute,
1232        ))),
1233    ];
1234    const CSS_MATCH_10339190304804100510: CssPropertyWithConditionsVec =
1235        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_10339190304804100510_PROPERTIES);
1236
1237    const CSS_MATCH_11452431279102104133_PROPERTIES: &[CssPropertyWithConditions] = &[
1238        // .node_input_connection_label
1239        CssPropertyWithConditions::simple(CssProperty::FontFamily(StyleFontFamilyVecValue::Exact(
1240            StyleFontFamilyVec::from_const_slice(STYLE_FONT_FAMILY_8122988506401935406_ITEMS),
1241        ))),
1242        CssPropertyWithConditions::simple(CssProperty::FontSize(StyleFontSizeValue::Exact(
1243            StyleFontSize {
1244                inner: PixelValue::const_px(12),
1245            },
1246        ))),
1247        CssPropertyWithConditions::simple(CssProperty::Height(LayoutHeightValue::Exact(
1248            LayoutHeight::Px(PixelValue::const_px(15)),
1249        ))),
1250        CssPropertyWithConditions::simple(CssProperty::TextAlign(StyleTextAlignValue::Exact(
1251            StyleTextAlign::Right,
1252        ))),
1253        CssPropertyWithConditions::simple(CssProperty::Width(LayoutWidthValue::Exact(
1254            LayoutWidth::Px(PixelValue::const_px(100)),
1255        ))),
1256    ];
1257    const CSS_MATCH_11452431279102104133: CssPropertyWithConditionsVec =
1258        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_11452431279102104133_PROPERTIES);
1259
1260    const CSS_MATCH_1173826950760010563_PROPERTIES: &[CssPropertyWithConditions] = &[
1261        // .node_configuration_field_value:focus
1262        CssPropertyWithConditions::simple(CssProperty::BorderTopColor(
1263            StyleBorderTopColorValue::Exact(StyleBorderTopColor {
1264                inner: ColorU {
1265                    r: 0,
1266                    g: 131,
1267                    b: 176,
1268                    a: 119,
1269                },
1270            }),
1271        )),
1272        CssPropertyWithConditions::simple(CssProperty::BorderRightColor(
1273            StyleBorderRightColorValue::Exact(StyleBorderRightColor {
1274                inner: ColorU {
1275                    r: 0,
1276                    g: 131,
1277                    b: 176,
1278                    a: 119,
1279                },
1280            }),
1281        )),
1282        CssPropertyWithConditions::simple(CssProperty::BorderLeftColor(
1283            StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
1284                inner: ColorU {
1285                    r: 0,
1286                    g: 131,
1287                    b: 176,
1288                    a: 119,
1289                },
1290            }),
1291        )),
1292        CssPropertyWithConditions::simple(CssProperty::BorderBottomColor(
1293            StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
1294                inner: ColorU {
1295                    r: 0,
1296                    g: 131,
1297                    b: 176,
1298                    a: 119,
1299                },
1300            }),
1301        )),
1302        CssPropertyWithConditions::simple(CssProperty::BorderTopStyle(
1303            StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
1304                inner: BorderStyle::Solid,
1305            }),
1306        )),
1307        CssPropertyWithConditions::simple(CssProperty::BorderRightStyle(
1308            StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
1309                inner: BorderStyle::Solid,
1310            }),
1311        )),
1312        CssPropertyWithConditions::simple(CssProperty::BorderLeftStyle(
1313            StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
1314                inner: BorderStyle::Solid,
1315            }),
1316        )),
1317        CssPropertyWithConditions::simple(CssProperty::BorderBottomStyle(
1318            StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
1319                inner: BorderStyle::Solid,
1320            }),
1321        )),
1322        CssPropertyWithConditions::simple(CssProperty::BorderTopWidth(
1323            LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
1324                inner: PixelValue::const_px(1),
1325            }),
1326        )),
1327        CssPropertyWithConditions::simple(CssProperty::BorderRightWidth(
1328            LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
1329                inner: PixelValue::const_px(1),
1330            }),
1331        )),
1332        CssPropertyWithConditions::simple(CssProperty::BorderLeftWidth(
1333            LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
1334                inner: PixelValue::const_px(1),
1335            }),
1336        )),
1337        CssPropertyWithConditions::simple(CssProperty::BorderBottomWidth(
1338            LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
1339                inner: PixelValue::const_px(1),
1340            }),
1341        )),
1342        // .node_configuration_field_value
1343        CssPropertyWithConditions::simple(CssProperty::AlignItems(LayoutAlignItemsValue::Exact(
1344            LayoutAlignItems::Center,
1345        ))),
1346        CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
1347            StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
1348                STYLE_BACKGROUND_CONTENT_524016094839686509_ITEMS,
1349            )),
1350        )),
1351        CssPropertyWithConditions::simple(CssProperty::BorderTopColor(
1352            StyleBorderTopColorValue::Exact(StyleBorderTopColor {
1353                inner: ColorU {
1354                    r: 54,
1355                    g: 57,
1356                    b: 63,
1357                    a: 255,
1358                },
1359            }),
1360        )),
1361        CssPropertyWithConditions::simple(CssProperty::BorderRightColor(
1362            StyleBorderRightColorValue::Exact(StyleBorderRightColor {
1363                inner: ColorU {
1364                    r: 54,
1365                    g: 57,
1366                    b: 63,
1367                    a: 255,
1368                },
1369            }),
1370        )),
1371        CssPropertyWithConditions::simple(CssProperty::BorderLeftColor(
1372            StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
1373                inner: ColorU {
1374                    r: 54,
1375                    g: 57,
1376                    b: 63,
1377                    a: 255,
1378                },
1379            }),
1380        )),
1381        CssPropertyWithConditions::simple(CssProperty::BorderBottomColor(
1382            StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
1383                inner: ColorU {
1384                    r: 54,
1385                    g: 57,
1386                    b: 63,
1387                    a: 255,
1388                },
1389            }),
1390        )),
1391        CssPropertyWithConditions::simple(CssProperty::BorderTopStyle(
1392            StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
1393                inner: BorderStyle::Solid,
1394            }),
1395        )),
1396        CssPropertyWithConditions::simple(CssProperty::BorderRightStyle(
1397            StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
1398                inner: BorderStyle::Solid,
1399            }),
1400        )),
1401        CssPropertyWithConditions::simple(CssProperty::BorderLeftStyle(
1402            StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
1403                inner: BorderStyle::Solid,
1404            }),
1405        )),
1406        CssPropertyWithConditions::simple(CssProperty::BorderBottomStyle(
1407            StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
1408                inner: BorderStyle::Solid,
1409            }),
1410        )),
1411        CssPropertyWithConditions::simple(CssProperty::BorderTopWidth(
1412            LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
1413                inner: PixelValue::const_px(1),
1414            }),
1415        )),
1416        CssPropertyWithConditions::simple(CssProperty::BorderRightWidth(
1417            LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
1418                inner: PixelValue::const_px(1),
1419            }),
1420        )),
1421        CssPropertyWithConditions::simple(CssProperty::BorderLeftWidth(
1422            LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
1423                inner: PixelValue::const_px(1),
1424            }),
1425        )),
1426        CssPropertyWithConditions::simple(CssProperty::BorderBottomWidth(
1427            LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
1428                inner: PixelValue::const_px(1),
1429            }),
1430        )),
1431        CssPropertyWithConditions::simple(CssProperty::FlexGrow(LayoutFlexGrowValue::Exact(
1432            LayoutFlexGrow {
1433                inner: FloatValue::const_new(1),
1434            },
1435        ))),
1436        CssPropertyWithConditions::simple(CssProperty::TextAlign(StyleTextAlignValue::Exact(
1437            StyleTextAlign::Left,
1438        ))),
1439    ];
1440    const CSS_MATCH_1173826950760010563: CssPropertyWithConditionsVec =
1441        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_1173826950760010563_PROPERTIES);
1442
1443    const CSS_MATCH_1198521124955124418_PROPERTIES: &[CssPropertyWithConditions] = &[
1444        // .node_configuration_field_label
1445        CssPropertyWithConditions::simple(CssProperty::AlignItems(LayoutAlignItemsValue::Exact(
1446            LayoutAlignItems::Center,
1447        ))),
1448        CssPropertyWithConditions::simple(CssProperty::FlexGrow(LayoutFlexGrowValue::Exact(
1449            LayoutFlexGrow {
1450                inner: FloatValue::const_new(1),
1451            },
1452        ))),
1453        CssPropertyWithConditions::simple(CssProperty::MaxWidth(LayoutMaxWidthValue::Exact(
1454            LayoutMaxWidth {
1455                inner: PixelValue::const_px(120),
1456            },
1457        ))),
1458        CssPropertyWithConditions::simple(CssProperty::PaddingLeft(LayoutPaddingLeftValue::Exact(
1459            LayoutPaddingLeft {
1460                inner: PixelValue::const_px(10),
1461            },
1462        ))),
1463        CssPropertyWithConditions::simple(CssProperty::TextAlign(StyleTextAlignValue::Exact(
1464            StyleTextAlign::Left,
1465        ))),
1466    ];
1467    const CSS_MATCH_1198521124955124418: CssPropertyWithConditionsVec =
1468        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_1198521124955124418_PROPERTIES);
1469
1470    const CSS_MATCH_12038890904436132038_PROPERTIES: &[CssPropertyWithConditions] = &[
1471        // .node_output_connection_label_wrapper
1472        CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
1473            StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
1474                STYLE_BACKGROUND_CONTENT_10430246856047584562_ITEMS,
1475            )),
1476        )),
1477        CssPropertyWithConditions::simple(CssProperty::PaddingLeft(LayoutPaddingLeftValue::Exact(
1478            LayoutPaddingLeft {
1479                inner: PixelValue::const_px(5),
1480            },
1481        ))),
1482    ];
1483    const CSS_MATCH_12038890904436132038: CssPropertyWithConditionsVec =
1484        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_12038890904436132038_PROPERTIES);
1485
1486    const CSS_MATCH_12400244273289328300_PROPERTIES: &[CssPropertyWithConditions] = &[
1487        // .node_output_container
1488        CssPropertyWithConditions::simple(CssProperty::Display(LayoutDisplayValue::Exact(
1489            LayoutDisplay::Flex,
1490        ))),
1491        CssPropertyWithConditions::simple(CssProperty::FlexDirection(
1492            LayoutFlexDirectionValue::Exact(LayoutFlexDirection::Row),
1493        )),
1494        CssPropertyWithConditions::simple(CssProperty::MarginTop(LayoutMarginTopValue::Exact(
1495            LayoutMarginTop {
1496                inner: PixelValue::const_px(10),
1497            },
1498        ))),
1499    ];
1500    const CSS_MATCH_12400244273289328300: CssPropertyWithConditionsVec =
1501        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_12400244273289328300_PROPERTIES);
1502
1503    const CSS_MATCH_14906563417280941890_PROPERTIES: &[CssPropertyWithConditions] = &[
1504        // .outputs
1505        CssPropertyWithConditions::simple(CssProperty::FlexGrow(LayoutFlexGrowValue::Exact(
1506            LayoutFlexGrow {
1507                inner: FloatValue::const_new(0),
1508            },
1509        ))),
1510        CssPropertyWithConditions::simple(CssProperty::OverflowX(LayoutOverflowValue::Exact(
1511            LayoutOverflow::Visible,
1512        ))),
1513        CssPropertyWithConditions::simple(CssProperty::OverflowY(LayoutOverflowValue::Exact(
1514            LayoutOverflow::Visible,
1515        ))),
1516        CssPropertyWithConditions::simple(CssProperty::Position(LayoutPositionValue::Exact(
1517            LayoutPosition::Relative,
1518        ))),
1519        CssPropertyWithConditions::simple(CssProperty::Width(LayoutWidthValue::Exact(
1520            LayoutWidth::Px(PixelValue::const_px(0)),
1521        ))),
1522    ];
1523    const CSS_MATCH_14906563417280941890: CssPropertyWithConditionsVec =
1524        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_14906563417280941890_PROPERTIES);
1525
1526    const CSS_MATCH_16946967739775705757_PROPERTIES: &[CssPropertyWithConditions] = &[
1527        // .inputs
1528        CssPropertyWithConditions::simple(CssProperty::FlexGrow(LayoutFlexGrowValue::Exact(
1529            LayoutFlexGrow {
1530                inner: FloatValue::const_new(0),
1531            },
1532        ))),
1533        CssPropertyWithConditions::simple(CssProperty::OverflowX(LayoutOverflowValue::Exact(
1534            LayoutOverflow::Visible,
1535        ))),
1536        CssPropertyWithConditions::simple(CssProperty::OverflowY(LayoutOverflowValue::Exact(
1537            LayoutOverflow::Visible,
1538        ))),
1539        CssPropertyWithConditions::simple(CssProperty::Position(LayoutPositionValue::Exact(
1540            LayoutPosition::Relative,
1541        ))),
1542        CssPropertyWithConditions::simple(CssProperty::Width(LayoutWidthValue::Exact(
1543            LayoutWidth::Px(PixelValue::const_px(0)),
1544        ))),
1545    ];
1546    const CSS_MATCH_16946967739775705757: CssPropertyWithConditionsVec =
1547        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_16946967739775705757_PROPERTIES);
1548
1549    const CSS_MATCH_1739273067404038547_PROPERTIES: &[CssPropertyWithConditions] = &[
1550        // .node_label
1551        CssPropertyWithConditions::simple(CssProperty::FontSize(StyleFontSizeValue::Exact(
1552            StyleFontSize {
1553                inner: PixelValue::const_px(18),
1554            },
1555        ))),
1556        CssPropertyWithConditions::simple(CssProperty::Height(LayoutHeightValue::Exact(
1557            LayoutHeight::Px(PixelValue::const_px(50)),
1558        ))),
1559        CssPropertyWithConditions::simple(CssProperty::PaddingLeft(LayoutPaddingLeftValue::Exact(
1560            LayoutPaddingLeft {
1561                inner: PixelValue::const_px(5),
1562            },
1563        ))),
1564        CssPropertyWithConditions::simple(CssProperty::PaddingTop(LayoutPaddingTopValue::Exact(
1565            LayoutPaddingTop {
1566                inner: PixelValue::const_px(10),
1567            },
1568        ))),
1569    ];
1570    const CSS_MATCH_1739273067404038547: CssPropertyWithConditionsVec =
1571        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_1739273067404038547_PROPERTIES);
1572
1573    const CSS_MATCH_2008162367868363199_PROPERTIES: &[CssPropertyWithConditions] = &[
1574        // .node_output_connection_label
1575        CssPropertyWithConditions::simple(CssProperty::FontFamily(StyleFontFamilyVecValue::Exact(
1576            StyleFontFamilyVec::from_const_slice(STYLE_FONT_FAMILY_8122988506401935406_ITEMS),
1577        ))),
1578        CssPropertyWithConditions::simple(CssProperty::FontSize(StyleFontSizeValue::Exact(
1579            StyleFontSize {
1580                inner: PixelValue::const_px(12),
1581            },
1582        ))),
1583        CssPropertyWithConditions::simple(CssProperty::Height(LayoutHeightValue::Exact(
1584            LayoutHeight::Px(PixelValue::const_px(15)),
1585        ))),
1586        CssPropertyWithConditions::simple(CssProperty::TextAlign(StyleTextAlignValue::Exact(
1587            StyleTextAlign::Left,
1588        ))),
1589        CssPropertyWithConditions::simple(CssProperty::Width(LayoutWidthValue::Exact(
1590            LayoutWidth::Px(PixelValue::const_px(100)),
1591        ))),
1592    ];
1593    const CSS_MATCH_2008162367868363199: CssPropertyWithConditionsVec =
1594        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_2008162367868363199_PROPERTIES);
1595
1596    const CSS_MATCH_2639191696846875011_PROPERTIES: &[CssPropertyWithConditions] = &[
1597        // .node_configuration_field_container
1598        CssPropertyWithConditions::simple(CssProperty::FlexDirection(
1599            LayoutFlexDirectionValue::Exact(LayoutFlexDirection::Column),
1600        )),
1601        CssPropertyWithConditions::simple(CssProperty::PaddingTop(LayoutPaddingTopValue::Exact(
1602            LayoutPaddingTop {
1603                inner: PixelValue::const_px(3),
1604            },
1605        ))),
1606        CssPropertyWithConditions::simple(CssProperty::PaddingBottom(
1607            LayoutPaddingBottomValue::Exact(LayoutPaddingBottom {
1608                inner: PixelValue::const_px(3),
1609            }),
1610        )),
1611        CssPropertyWithConditions::simple(CssProperty::PaddingLeft(LayoutPaddingLeftValue::Exact(
1612            LayoutPaddingLeft {
1613                inner: PixelValue::const_px(5),
1614            },
1615        ))),
1616        CssPropertyWithConditions::simple(CssProperty::PaddingRight(
1617            LayoutPaddingRightValue::Exact(LayoutPaddingRight {
1618                inner: PixelValue::const_px(5),
1619            }),
1620        )),
1621    ];
1622    const CSS_MATCH_2639191696846875011: CssPropertyWithConditionsVec =
1623        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_2639191696846875011_PROPERTIES);
1624
1625    const CSS_MATCH_3354247437065914166_PROPERTIES: &[CssPropertyWithConditions] = &[
1626        // .node_body
1627        CssPropertyWithConditions::simple(CssProperty::FlexDirection(
1628            LayoutFlexDirectionValue::Exact(LayoutFlexDirection::Row),
1629        )),
1630        CssPropertyWithConditions::simple(CssProperty::Position(LayoutPositionValue::Exact(
1631            LayoutPosition::Relative,
1632        ))),
1633    ];
1634    const CSS_MATCH_3354247437065914166: CssPropertyWithConditionsVec =
1635        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_3354247437065914166_PROPERTIES);
1636
1637    const CSS_MATCH_4700400755767504372_PROPERTIES: &[CssPropertyWithConditions] = &[
1638        // .node_input_connection_label_wrapper
1639        CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
1640            StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
1641                STYLE_BACKGROUND_CONTENT_11936041127084538304_ITEMS,
1642            )),
1643        )),
1644        CssPropertyWithConditions::simple(CssProperty::PaddingRight(
1645            LayoutPaddingRightValue::Exact(LayoutPaddingRight {
1646                inner: PixelValue::const_px(5),
1647            }),
1648        )),
1649    ];
1650    const CSS_MATCH_4700400755767504372: CssPropertyWithConditionsVec =
1651        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_4700400755767504372_PROPERTIES);
1652
1653    const CSS_MATCH_705881630351954657_PROPERTIES: &[CssPropertyWithConditions] = &[
1654        // .node_input_wrapper
1655        CssPropertyWithConditions::simple(CssProperty::Display(LayoutDisplayValue::Exact(
1656            LayoutDisplay::Flex,
1657        ))),
1658        CssPropertyWithConditions::simple(CssProperty::FlexDirection(
1659            LayoutFlexDirectionValue::Exact(LayoutFlexDirection::Column),
1660        )),
1661        CssPropertyWithConditions::simple(CssProperty::OverflowX(LayoutOverflowValue::Exact(
1662            LayoutOverflow::Visible,
1663        ))),
1664        CssPropertyWithConditions::simple(CssProperty::OverflowY(LayoutOverflowValue::Exact(
1665            LayoutOverflow::Visible,
1666        ))),
1667        CssPropertyWithConditions::simple(CssProperty::Position(LayoutPositionValue::Exact(
1668            LayoutPosition::Absolute,
1669        ))),
1670        CssPropertyWithConditions::simple(CssProperty::Right(LayoutRightValue::Exact(
1671            LayoutRight {
1672                inner: PixelValue::const_px(0),
1673            },
1674        ))),
1675    ];
1676    const CSS_MATCH_705881630351954657: CssPropertyWithConditionsVec =
1677        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_705881630351954657_PROPERTIES);
1678
1679    const CSS_MATCH_7395766480280098891_PROPERTIES: &[CssPropertyWithConditions] = &[
1680        // .node_close_button
1681        CssPropertyWithConditions::simple(CssProperty::AlignItems(LayoutAlignItemsValue::Exact(
1682            LayoutAlignItems::Center,
1683        ))),
1684        CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
1685            StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
1686                STYLE_BACKGROUND_CONTENT_17648039690071193942_ITEMS,
1687            )),
1688        )),
1689        CssPropertyWithConditions::simple(CssProperty::BorderTopColor(
1690            StyleBorderTopColorValue::Exact(StyleBorderTopColor {
1691                inner: ColorU {
1692                    r: 255,
1693                    g: 255,
1694                    b: 255,
1695                    a: 153,
1696                },
1697            }),
1698        )),
1699        CssPropertyWithConditions::simple(CssProperty::BorderRightColor(
1700            StyleBorderRightColorValue::Exact(StyleBorderRightColor {
1701                inner: ColorU {
1702                    r: 255,
1703                    g: 255,
1704                    b: 255,
1705                    a: 153,
1706                },
1707            }),
1708        )),
1709        CssPropertyWithConditions::simple(CssProperty::BorderLeftColor(
1710            StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
1711                inner: ColorU {
1712                    r: 255,
1713                    g: 255,
1714                    b: 255,
1715                    a: 153,
1716                },
1717            }),
1718        )),
1719        CssPropertyWithConditions::simple(CssProperty::BorderBottomColor(
1720            StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
1721                inner: ColorU {
1722                    r: 255,
1723                    g: 255,
1724                    b: 255,
1725                    a: 153,
1726                },
1727            }),
1728        )),
1729        CssPropertyWithConditions::simple(CssProperty::BorderTopStyle(
1730            StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
1731                inner: BorderStyle::Solid,
1732            }),
1733        )),
1734        CssPropertyWithConditions::simple(CssProperty::BorderRightStyle(
1735            StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
1736                inner: BorderStyle::Solid,
1737            }),
1738        )),
1739        CssPropertyWithConditions::simple(CssProperty::BorderLeftStyle(
1740            StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
1741                inner: BorderStyle::Solid,
1742            }),
1743        )),
1744        CssPropertyWithConditions::simple(CssProperty::BorderBottomStyle(
1745            StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
1746                inner: BorderStyle::Solid,
1747            }),
1748        )),
1749        CssPropertyWithConditions::simple(CssProperty::BorderTopWidth(
1750            LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
1751                inner: PixelValue::const_px(1),
1752            }),
1753        )),
1754        CssPropertyWithConditions::simple(CssProperty::BorderRightWidth(
1755            LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
1756                inner: PixelValue::const_px(1),
1757            }),
1758        )),
1759        CssPropertyWithConditions::simple(CssProperty::BorderLeftWidth(
1760            LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
1761                inner: PixelValue::const_px(1),
1762            }),
1763        )),
1764        CssPropertyWithConditions::simple(CssProperty::BorderBottomWidth(
1765            LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
1766                inner: PixelValue::const_px(1),
1767            }),
1768        )),
1769        CssPropertyWithConditions::simple(CssProperty::BoxShadowLeft(StyleBoxShadowValue::Exact(
1770            StyleBoxShadow {
1771                offset_x: PixelValueNoPercent {
1772                    inner: PixelValue::const_px(0),
1773                },
1774                offset_y: PixelValueNoPercent {
1775                    inner: PixelValue::const_px(0),
1776                },
1777                color: ColorU {
1778                    r: 229,
1779                    g: 57,
1780                    b: 53,
1781                    a: 255,
1782                },
1783                blur_radius: PixelValueNoPercent {
1784                    inner: PixelValue::const_px(2),
1785                },
1786                spread_radius: PixelValueNoPercent {
1787                    inner: PixelValue::const_px(0),
1788                },
1789                clip_mode: BoxShadowClipMode::Outset,
1790            },
1791        ))),
1792        CssPropertyWithConditions::simple(CssProperty::BoxShadowRight(StyleBoxShadowValue::Exact(
1793            StyleBoxShadow {
1794                offset_x: PixelValueNoPercent {
1795                    inner: PixelValue::const_px(0),
1796                },
1797                offset_y: PixelValueNoPercent {
1798                    inner: PixelValue::const_px(0),
1799                },
1800                color: ColorU {
1801                    r: 229,
1802                    g: 57,
1803                    b: 53,
1804                    a: 255,
1805                },
1806                blur_radius: PixelValueNoPercent {
1807                    inner: PixelValue::const_px(2),
1808                },
1809                spread_radius: PixelValueNoPercent {
1810                    inner: PixelValue::const_px(0),
1811                },
1812                clip_mode: BoxShadowClipMode::Outset,
1813            },
1814        ))),
1815        CssPropertyWithConditions::simple(CssProperty::BoxShadowTop(StyleBoxShadowValue::Exact(
1816            StyleBoxShadow {
1817                offset_x: PixelValueNoPercent {
1818                    inner: PixelValue::const_px(0),
1819                },
1820                offset_y: PixelValueNoPercent {
1821                    inner: PixelValue::const_px(0),
1822                },
1823                color: ColorU {
1824                    r: 229,
1825                    g: 57,
1826                    b: 53,
1827                    a: 255,
1828                },
1829                blur_radius: PixelValueNoPercent {
1830                    inner: PixelValue::const_px(2),
1831                },
1832                spread_radius: PixelValueNoPercent {
1833                    inner: PixelValue::const_px(0),
1834                },
1835                clip_mode: BoxShadowClipMode::Outset,
1836            },
1837        ))),
1838        CssPropertyWithConditions::simple(CssProperty::BoxShadowBottom(
1839            StyleBoxShadowValue::Exact(StyleBoxShadow {
1840                offset_x: PixelValueNoPercent {
1841                    inner: PixelValue::const_px(0),
1842                },
1843                offset_y: PixelValueNoPercent {
1844                    inner: PixelValue::const_px(0),
1845                },
1846                color: ColorU {
1847                    r: 229,
1848                    g: 57,
1849                    b: 53,
1850                    a: 255,
1851                },
1852                blur_radius: PixelValueNoPercent {
1853                    inner: PixelValue::const_px(2),
1854                },
1855                spread_radius: PixelValueNoPercent {
1856                    inner: PixelValue::const_px(0),
1857                },
1858                clip_mode: BoxShadowClipMode::Outset,
1859            }),
1860        )),
1861        CssPropertyWithConditions::simple(CssProperty::Cursor(StyleCursorValue::Exact(
1862            StyleCursor::Pointer,
1863        ))),
1864        CssPropertyWithConditions::simple(CssProperty::FontFamily(StyleFontFamilyVecValue::Exact(
1865            StyleFontFamilyVec::from_const_slice(STYLE_FONT_FAMILY_11383897783350685780_ITEMS),
1866        ))),
1867        CssPropertyWithConditions::simple(CssProperty::Height(LayoutHeightValue::Exact(
1868            LayoutHeight::Px(PixelValue::const_px(20)),
1869        ))),
1870        CssPropertyWithConditions::simple(CssProperty::Position(LayoutPositionValue::Exact(
1871            LayoutPosition::Absolute,
1872        ))),
1873        CssPropertyWithConditions::simple(CssProperty::TextAlign(StyleTextAlignValue::Exact(
1874            StyleTextAlign::Center,
1875        ))),
1876        CssPropertyWithConditions::simple(CssProperty::Transform(StyleTransformVecValue::Exact(
1877            StyleTransformVec::from_const_slice(STYLE_TRANSFORM_14683950870521466298_ITEMS),
1878        ))),
1879        CssPropertyWithConditions::simple(CssProperty::Width(LayoutWidthValue::Exact(
1880            LayoutWidth::Px(PixelValue::const_px(20)),
1881        ))),
1882    ];
1883    const CSS_MATCH_7395766480280098891: CssPropertyWithConditionsVec =
1884        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_7395766480280098891_PROPERTIES);
1885
1886    const CSS_MATCH_7432473243011547380_PROPERTIES: &[CssPropertyWithConditions] = &[
1887        // .node_content_wrapper
1888        CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
1889            StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
1890                STYLE_BACKGROUND_CONTENT_15813232491335471489_ITEMS,
1891            )),
1892        )),
1893        CssPropertyWithConditions::simple(CssProperty::BoxShadowLeft(StyleBoxShadowValue::Exact(
1894            StyleBoxShadow {
1895                offset_x: PixelValueNoPercent {
1896                    inner: PixelValue::const_px(0),
1897                },
1898                offset_y: PixelValueNoPercent {
1899                    inner: PixelValue::const_px(0),
1900                },
1901                color: ColorU {
1902                    r: 0,
1903                    g: 0,
1904                    b: 0,
1905                    a: 255,
1906                },
1907                blur_radius: PixelValueNoPercent {
1908                    inner: PixelValue::const_px(4),
1909                },
1910                spread_radius: PixelValueNoPercent {
1911                    inner: PixelValue::const_px(0),
1912                },
1913                clip_mode: BoxShadowClipMode::Inset,
1914            },
1915        ))),
1916        CssPropertyWithConditions::simple(CssProperty::BoxShadowRight(StyleBoxShadowValue::Exact(
1917            StyleBoxShadow {
1918                offset_x: PixelValueNoPercent {
1919                    inner: PixelValue::const_px(0),
1920                },
1921                offset_y: PixelValueNoPercent {
1922                    inner: PixelValue::const_px(0),
1923                },
1924                color: ColorU {
1925                    r: 0,
1926                    g: 0,
1927                    b: 0,
1928                    a: 255,
1929                },
1930                blur_radius: PixelValueNoPercent {
1931                    inner: PixelValue::const_px(4),
1932                },
1933                spread_radius: PixelValueNoPercent {
1934                    inner: PixelValue::const_px(0),
1935                },
1936                clip_mode: BoxShadowClipMode::Inset,
1937            },
1938        ))),
1939        CssPropertyWithConditions::simple(CssProperty::BoxShadowTop(StyleBoxShadowValue::Exact(
1940            StyleBoxShadow {
1941                offset_x: PixelValueNoPercent {
1942                    inner: PixelValue::const_px(0),
1943                },
1944                offset_y: PixelValueNoPercent {
1945                    inner: PixelValue::const_px(0),
1946                },
1947                color: ColorU {
1948                    r: 0,
1949                    g: 0,
1950                    b: 0,
1951                    a: 255,
1952                },
1953                blur_radius: PixelValueNoPercent {
1954                    inner: PixelValue::const_px(4),
1955                },
1956                spread_radius: PixelValueNoPercent {
1957                    inner: PixelValue::const_px(0),
1958                },
1959                clip_mode: BoxShadowClipMode::Inset,
1960            },
1961        ))),
1962        CssPropertyWithConditions::simple(CssProperty::BoxShadowBottom(
1963            StyleBoxShadowValue::Exact(StyleBoxShadow {
1964                offset_x: PixelValueNoPercent {
1965                    inner: PixelValue::const_px(0),
1966                },
1967                offset_y: PixelValueNoPercent {
1968                    inner: PixelValue::const_px(0),
1969                },
1970                color: ColorU {
1971                    r: 0,
1972                    g: 0,
1973                    b: 0,
1974                    a: 255,
1975                },
1976                blur_radius: PixelValueNoPercent {
1977                    inner: PixelValue::const_px(4),
1978                },
1979                spread_radius: PixelValueNoPercent {
1980                    inner: PixelValue::const_px(0),
1981                },
1982                clip_mode: BoxShadowClipMode::Inset,
1983            }),
1984        )),
1985        CssPropertyWithConditions::simple(CssProperty::FlexGrow(LayoutFlexGrowValue::Exact(
1986            LayoutFlexGrow {
1987                inner: FloatValue::const_new(1),
1988            },
1989        ))),
1990    ];
1991    const CSS_MATCH_7432473243011547380: CssPropertyWithConditionsVec =
1992        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_7432473243011547380_PROPERTIES);
1993
1994    const CSS_MATCH_9863994880298313101_PROPERTIES: &[CssPropertyWithConditions] = &[
1995        // .node_input_container
1996        CssPropertyWithConditions::simple(CssProperty::Display(LayoutDisplayValue::Exact(
1997            LayoutDisplay::Flex,
1998        ))),
1999        CssPropertyWithConditions::simple(CssProperty::FlexDirection(
2000            LayoutFlexDirectionValue::Exact(LayoutFlexDirection::Row),
2001        )),
2002        CssPropertyWithConditions::simple(CssProperty::MarginTop(LayoutMarginTopValue::Exact(
2003            LayoutMarginTop {
2004                inner: PixelValue::const_px(10),
2005            },
2006        ))),
2007    ];
2008    const CSS_MATCH_9863994880298313101: CssPropertyWithConditionsVec =
2009        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_9863994880298313101_PROPERTIES);
2010
2011    // NODE RENDER FUNCTION BEGIN
2012
2013    let node_transform = StyleTransformTranslate2D {
2014        x: PixelValue::px(graph_offset.0 + node.position.x),
2015        y: PixelValue::px(graph_offset.1 + node.position.y),
2016    };
2017
2018    // get names and colors for inputs / outputs
2019    let inputs = node_info
2020        .inputs
2021        .iter()
2022        .filter_map(|io_id| {
2023            let node_graph_ref = node_local_dataset
2024                .backref
2025                .downcast_ref::<NodeGraphLocalDataset>()?;
2026            let io_info = node_graph_ref
2027                .node_graph
2028                .input_output_types
2029                .iter()
2030                .find(|i| i.io_type_id == *io_id)?;
2031            Some((
2032                io_info.io_info.data_type.clone(),
2033                io_info.io_info.color.clone(),
2034            ))
2035        })
2036        .collect::<Vec<_>>();
2037
2038    let outputs = node_info
2039        .outputs
2040        .iter()
2041        .filter_map(|io_id| {
2042            let node_graph_ref = node_local_dataset
2043                .backref
2044                .downcast_ref::<NodeGraphLocalDataset>()?;
2045            let io_info = node_graph_ref
2046                .node_graph
2047                .input_output_types
2048                .iter()
2049                .find(|i| i.io_type_id == *io_id)?;
2050            Some((
2051                io_info.io_info.data_type.clone(),
2052                io_info.io_info.color.clone(),
2053            ))
2054        })
2055        .collect::<Vec<_>>();
2056
2057    let node_local_dataset = RefAny::new(node_local_dataset);
2058
2059    Dom::create_div()
2060    .with_css_props(vec![
2061        CssPropertyWithConditions::simple(CssProperty::Position(LayoutPositionValue::Exact(
2062            LayoutPosition::Absolute,
2063        ))),
2064    ].into())
2065    .with_children(vec![
2066        Dom::create_div()
2067        .with_callbacks(vec![
2068           CoreCallbackData {
2069               event: EventFilter::Hover(HoverEventFilter::LeftMouseDown),
2070               refany: node_local_dataset.clone(),
2071               callback: CoreCallback { cb: nodegraph_set_active_node as usize, ctx: OptionRefAny::None },
2072           },
2073        ].into())
2074        .with_css_props(vec![
2075           // .node_graph_node
2076           CssPropertyWithConditions::simple(CssProperty::OverflowX(
2077               LayoutOverflowValue::Exact(LayoutOverflow::Visible)
2078           )),
2079           CssPropertyWithConditions::simple(CssProperty::Position(LayoutPositionValue::Exact(
2080               LayoutPosition::Relative,
2081           ))),
2082           CssPropertyWithConditions::simple(CssProperty::OverflowY(
2083               LayoutOverflowValue::Exact(LayoutOverflow::Visible)
2084           )),
2085           CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
2086               StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
2087                   STYLE_BACKGROUND_CONTENT_11535310356736632656_ITEMS,
2088               )),
2089           )),
2090           CssPropertyWithConditions::simple(CssProperty::BorderTopColor(
2091               StyleBorderTopColorValue::Exact(StyleBorderTopColor {
2092                   inner: ColorU {
2093                       r: 0,
2094                       g: 180,
2095                       b: 219,
2096                       a: 255,
2097                   },
2098               }),
2099           )),
2100           CssPropertyWithConditions::simple(CssProperty::BorderRightColor(
2101               StyleBorderRightColorValue::Exact(StyleBorderRightColor {
2102                   inner: ColorU {
2103                       r: 0,
2104                       g: 180,
2105                       b: 219,
2106                       a: 255,
2107                   },
2108               }),
2109           )),
2110           CssPropertyWithConditions::simple(CssProperty::BorderLeftColor(
2111               StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
2112                   inner: ColorU {
2113                       r: 0,
2114                       g: 180,
2115                       b: 219,
2116                       a: 255,
2117                   },
2118               }),
2119           )),
2120           CssPropertyWithConditions::simple(CssProperty::BorderBottomColor(
2121               StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
2122                   inner: ColorU {
2123                       r: 0,
2124                       g: 180,
2125                       b: 219,
2126                       a: 255,
2127                   },
2128               }),
2129           )),
2130           CssPropertyWithConditions::simple(CssProperty::BorderTopStyle(
2131               StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
2132                   inner: BorderStyle::Solid,
2133               }),
2134           )),
2135           CssPropertyWithConditions::simple(CssProperty::BorderRightStyle(
2136               StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
2137                   inner: BorderStyle::Solid,
2138               }),
2139           )),
2140           CssPropertyWithConditions::simple(CssProperty::BorderLeftStyle(
2141               StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
2142                   inner: BorderStyle::Solid,
2143               }),
2144           )),
2145           CssPropertyWithConditions::simple(CssProperty::BorderBottomStyle(
2146               StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
2147                   inner: BorderStyle::Solid,
2148               }),
2149           )),
2150           CssPropertyWithConditions::simple(CssProperty::BorderTopWidth(
2151               LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
2152                   inner: PixelValue::const_px(1),
2153               }),
2154           )),
2155           CssPropertyWithConditions::simple(CssProperty::BorderRightWidth(
2156               LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
2157                   inner: PixelValue::const_px(1),
2158               }),
2159           )),
2160           CssPropertyWithConditions::simple(CssProperty::BorderLeftWidth(
2161               LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
2162                   inner: PixelValue::const_px(1),
2163               }),
2164           )),
2165           CssPropertyWithConditions::simple(CssProperty::BorderBottomWidth(
2166               LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
2167                   inner: PixelValue::const_px(1),
2168               }),
2169           )),
2170           CssPropertyWithConditions::simple(CssProperty::BoxShadowLeft(StyleBoxShadowValue::Exact(
2171               StyleBoxShadow {
2172                   offset_x: PixelValueNoPercent { inner: PixelValue::const_px(0) }, offset_y: PixelValueNoPercent { inner: PixelValue::const_px(0) },
2173                   color: ColorU {
2174                       r: 0,
2175                       g: 131,
2176                       b: 176,
2177                       a: 119,
2178                   },
2179                   blur_radius: PixelValueNoPercent {
2180                       inner: PixelValue::const_px(3),
2181                   },
2182                   spread_radius: PixelValueNoPercent {
2183                       inner: PixelValue::const_px(0),
2184                   },
2185                   clip_mode: BoxShadowClipMode::Outset,
2186               },
2187           ))),
2188           CssPropertyWithConditions::simple(CssProperty::BoxShadowRight(StyleBoxShadowValue::Exact(
2189               StyleBoxShadow {
2190                   offset_x: PixelValueNoPercent { inner: PixelValue::const_px(0) }, offset_y: PixelValueNoPercent { inner: PixelValue::const_px(0) },
2191                   color: ColorU {
2192                       r: 0,
2193                       g: 131,
2194                       b: 176,
2195                       a: 119,
2196                   },
2197                   blur_radius: PixelValueNoPercent {
2198                       inner: PixelValue::const_px(3),
2199                   },
2200                   spread_radius: PixelValueNoPercent {
2201                       inner: PixelValue::const_px(0),
2202                   },
2203                   clip_mode: BoxShadowClipMode::Outset,
2204               },
2205           ))),
2206           CssPropertyWithConditions::simple(CssProperty::BoxShadowTop(StyleBoxShadowValue::Exact(
2207               StyleBoxShadow {
2208                   offset_x: PixelValueNoPercent { inner: PixelValue::const_px(0) }, offset_y: PixelValueNoPercent { inner: PixelValue::const_px(0) },
2209                   color: ColorU {
2210                       r: 0,
2211                       g: 131,
2212                       b: 176,
2213                       a: 119,
2214                   },
2215                   blur_radius: PixelValueNoPercent {
2216                       inner: PixelValue::const_px(3),
2217                   },
2218                   spread_radius: PixelValueNoPercent {
2219                       inner: PixelValue::const_px(0),
2220                   },
2221                   clip_mode: BoxShadowClipMode::Outset,
2222               },
2223           ))),
2224           CssPropertyWithConditions::simple(CssProperty::BoxShadowBottom(
2225               StyleBoxShadowValue::Exact(StyleBoxShadow {
2226                   offset_x: PixelValueNoPercent { inner: PixelValue::const_px(0) }, offset_y: PixelValueNoPercent { inner: PixelValue::const_px(0) },
2227                   color: ColorU {
2228                       r: 0,
2229                       g: 131,
2230                       b: 176,
2231                       a: 119,
2232                   },
2233                   blur_radius: PixelValueNoPercent {
2234                       inner: PixelValue::const_px(3),
2235                   },
2236                   spread_radius: PixelValueNoPercent {
2237                       inner: PixelValue::const_px(0),
2238                   },
2239                   clip_mode: BoxShadowClipMode::Outset,
2240               }),
2241           )),
2242           CssPropertyWithConditions::simple(CssProperty::TextColor(StyleTextColorValue::Exact(
2243               StyleTextColor {
2244                   inner: ColorU {
2245                       r: 255,
2246                       g: 255,
2247                       b: 255,
2248                       a: 255,
2249                   },
2250               },
2251           ))),
2252
2253           CssPropertyWithConditions::simple(CssProperty::Display(LayoutDisplayValue::Exact(
2254               LayoutDisplay::Block
2255           ))),
2256           CssPropertyWithConditions::simple(CssProperty::FontFamily(StyleFontFamilyVecValue::Exact(
2257               StyleFontFamilyVec::from_const_slice(STYLE_FONT_FAMILY_8122988506401935406_ITEMS),
2258           ))),
2259           CssPropertyWithConditions::simple(CssProperty::PaddingTop(LayoutPaddingTopValue::Exact(
2260               LayoutPaddingTop {
2261                   inner: PixelValue::const_px(10),
2262               },
2263           ))),
2264           CssPropertyWithConditions::simple(CssProperty::PaddingBottom(
2265               LayoutPaddingBottomValue::Exact(LayoutPaddingBottom {
2266                   inner: PixelValue::const_px(10),
2267               }),
2268           )),
2269           CssPropertyWithConditions::simple(CssProperty::PaddingLeft(LayoutPaddingLeftValue::Exact(
2270               LayoutPaddingLeft {
2271                   inner: PixelValue::const_px(10),
2272               },
2273           ))),
2274           CssPropertyWithConditions::simple(CssProperty::PaddingRight(
2275               LayoutPaddingRightValue::Exact(LayoutPaddingRight {
2276                   inner: PixelValue::const_px(10),
2277               }),
2278           )),
2279           CssPropertyWithConditions::simple(CssProperty::Transform(StyleTransformVecValue::Exact(
2280               if scale_factor != 1.0 {
2281                    vec![
2282                         StyleTransform::Translate(node_transform),
2283                         StyleTransform::ScaleX(PercentageValue::new(scale_factor * 100.0)),
2284                         StyleTransform::ScaleY(PercentageValue::new(scale_factor * 100.0)),
2285                    ]
2286               } else {
2287                    vec![
2288                         StyleTransform::Translate(node_transform)
2289                    ]
2290               }.into()
2291           ))),
2292           CssPropertyWithConditions::simple(CssProperty::Width(LayoutWidthValue::Exact(
2293               LayoutWidth::Px(PixelValue::const_px(250),),
2294           ))),
2295        ].into())
2296        .with_ids_and_classes({
2297           const IDS_AND_CLASSES_4480169002427296613: &[IdOrClass] =
2298               &[Class(AzString::from_const_str("node_graph_node"))];
2299           IdOrClassVec::from_const_slice(IDS_AND_CLASSES_4480169002427296613)
2300        })
2301        .with_children(DomVec::from_vec(vec![
2302           Dom::create_text(AzString::from_const_str("X"))
2303               .with_css_props(CSS_MATCH_7395766480280098891)
2304               .with_callbacks(vec![
2305                   CoreCallbackData {
2306                       event: EventFilter::Hover(HoverEventFilter::MouseUp),
2307                       refany: node_local_dataset.clone(),
2308                       callback: CoreCallback { cb: nodegraph_delete_node as usize, ctx: OptionRefAny::None },
2309                   },
2310               ].into())
2311               .with_ids_and_classes({
2312                   const IDS_AND_CLASSES_7122017923389407516: &[IdOrClass] =
2313                       &[Class(AzString::from_const_str("node_close_button"))];
2314                   IdOrClassVec::from_const_slice(IDS_AND_CLASSES_7122017923389407516)
2315               }),
2316           Dom::create_text(node_info.node_type_name.clone())
2317               .with_css_props(CSS_MATCH_1739273067404038547)
2318               .with_ids_and_classes({
2319                   const IDS_AND_CLASSES_15777790571346582635: &[IdOrClass] =
2320                       &[Class(AzString::from_const_str("node_label"))];
2321                   IdOrClassVec::from_const_slice(IDS_AND_CLASSES_15777790571346582635)
2322               }),
2323           Dom::create_div()
2324               .with_css_props(CSS_MATCH_3354247437065914166)
2325               .with_ids_and_classes({
2326                   const IDS_AND_CLASSES_5590500152394859708: &[IdOrClass] =
2327                       &[Class(AzString::from_const_str("node_body"))];
2328                   IdOrClassVec::from_const_slice(IDS_AND_CLASSES_5590500152394859708)
2329               })
2330               .with_children(DomVec::from_vec(vec![
2331                   Dom::create_div()
2332                       .with_css_props(CSS_MATCH_16946967739775705757)
2333                       .with_ids_and_classes({
2334                           const IDS_AND_CLASSES_3626404106673061698: &[IdOrClass] =
2335                               &[Class(AzString::from_const_str("inputs"))];
2336                           IdOrClassVec::from_const_slice(IDS_AND_CLASSES_3626404106673061698)
2337                       })
2338                       .with_children(DomVec::from_vec(vec![Dom::create_div()
2339                           .with_css_props(CSS_MATCH_705881630351954657)
2340                           .with_ids_and_classes({
2341                               const IDS_AND_CLASSES_12825690349660780627: &[IdOrClass] =
2342                                   &[Class(AzString::from_const_str("node_input_wrapper"))];
2343                               IdOrClassVec::from_const_slice(
2344                                   IDS_AND_CLASSES_12825690349660780627,
2345                               )
2346                           })
2347                           .with_children(DomVec::from_vec(
2348                               inputs
2349                               .into_iter()
2350                               .enumerate()
2351                               .map(|(io_id, (input_label, input_color))| {
2352                                   use self::InputOrOutput::*;
2353
2354                                   Dom::create_div()
2355                                       .with_css_props(CSS_MATCH_9863994880298313101)
2356                                       .with_ids_and_classes({
2357                                           const IDS_AND_CLASSES_5020681879750641508:
2358                                               &[IdOrClass] = &[Class(AzString::from_const_str(
2359                                               "node_input_container",
2360                                           ))];
2361                                           IdOrClassVec::from_const_slice(
2362                                               IDS_AND_CLASSES_5020681879750641508,
2363                                           )
2364                                       })
2365                                       .with_children(DomVec::from_vec(vec![
2366                                           Dom::create_div()
2367                                               .with_css_props(
2368                                                   CSS_MATCH_4700400755767504372,
2369                                               )
2370                                               .with_ids_and_classes({
2371                                                   const IDS_AND_CLASSES_9154857442066749879:
2372                                                       &[IdOrClass] =
2373                                                       &[Class(AzString::from_const_str(
2374                                                           "node_input_connection_label_wrapper",
2375                                                       ))];
2376                                                   IdOrClassVec::from_const_slice(
2377                                                       IDS_AND_CLASSES_9154857442066749879,
2378                                                   )
2379                                               })
2380                                               .with_children(DomVec::from_vec(vec![Dom::create_text(
2381                                                   input_label.clone(),
2382                                               )
2383                                               .with_css_props(
2384                                                   CSS_MATCH_11452431279102104133,
2385                                               )
2386                                               .with_ids_and_classes({
2387                                                   const IDS_AND_CLASSES_16291496011772407931:
2388                                                       &[IdOrClass] =
2389                                                       &[Class(AzString::from_const_str(
2390                                                           "node_input_connection_label",
2391                                                       ))];
2392                                                   IdOrClassVec::from_const_slice(
2393                                                       IDS_AND_CLASSES_16291496011772407931,
2394                                                   )
2395                                               })])),
2396                                           Dom::create_div()
2397                                               .with_callbacks(vec![
2398                                                   CoreCallbackData {
2399                                                       event: EventFilter::Hover(HoverEventFilter::LeftMouseUp),
2400                                                       refany: RefAny::new(NodeInputOutputLocalDataset {
2401                                                           io_id: Input(io_id),
2402                                                           backref: node_local_dataset.clone(),
2403                                                       }),
2404                                                       callback: CoreCallback { cb: nodegraph_input_output_connect as usize, ctx: OptionRefAny::None },
2405                                                   },
2406                                                   CoreCallbackData {
2407                                                       event: EventFilter::Hover(HoverEventFilter::MiddleMouseUp),
2408                                                       refany: RefAny::new(NodeInputOutputLocalDataset {
2409                                                           io_id: Input(io_id),
2410                                                           backref: node_local_dataset.clone(),
2411                                                       }),
2412                                                       callback: CoreCallback { cb: nodegraph_input_output_disconnect as usize, ctx: OptionRefAny::None },
2413                                                   },
2414                                               ].into())
2415                                               .with_css_props(CssPropertyWithConditionsVec::from_vec(vec![
2416                                                       // .node_input
2417                                                       CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
2418                                                           StyleBackgroundContentVecValue::Exact(vec![StyleBackgroundContent::Color(input_color)].into()),
2419                                                       )),
2420                                                       CssPropertyWithConditions::simple(CssProperty::Cursor(StyleCursorValue::Exact(
2421                                                           StyleCursor::Pointer,
2422                                                       ))),
2423                                                       CssPropertyWithConditions::simple(CssProperty::Height(LayoutHeightValue::Exact(
2424                                                           LayoutHeight::Px(PixelValue::const_px(15),),
2425                                                       ))),
2426                                                       CssPropertyWithConditions::simple(CssProperty::Width(LayoutWidthValue::Exact(
2427                                                           LayoutWidth::Px(PixelValue::const_px(15),),
2428                                                       ))),
2429                                                   ])
2430                                               )
2431                                               .with_ids_and_classes({
2432                                                   const IDS_AND_CLASSES_2128818677168244823:
2433                                                       &[IdOrClass] = &[Class(
2434                                                       AzString::from_const_str("node_input"),
2435                                                   )];
2436                                                   IdOrClassVec::from_const_slice(
2437                                                       IDS_AND_CLASSES_2128818677168244823,
2438                                                   )
2439                                               }),
2440                                       ]))
2441                               }).collect()
2442                           ))
2443                       ])),
2444                   Dom::create_div()
2445                       .with_css_props(CSS_MATCH_7432473243011547380)
2446                       .with_ids_and_classes({
2447                           const IDS_AND_CLASSES_746059979773622802: &[IdOrClass] =
2448                               &[Class(AzString::from_const_str("node_content_wrapper"))];
2449                           IdOrClassVec::from_const_slice(IDS_AND_CLASSES_746059979773622802)
2450                       })
2451                       .with_children({
2452
2453                           let mut fields = Vec::new();
2454
2455                           for (field_idx, field) in node.fields.iter().enumerate() {
2456
2457                               let field_local_dataset = RefAny::new(NodeFieldLocalDataset {
2458                                   field_idx,
2459                                   backref: node_local_dataset.clone(),
2460                               });
2461
2462                               let div = Dom::create_div()
2463                               .with_css_props(CSS_MATCH_2639191696846875011)
2464                               .with_ids_and_classes({
2465                                   const IDS_AND_CLASSES_4413230059125905311: &[IdOrClass] =
2466                                       &[Class(AzString::from_const_str(
2467                                           "node_configuration_field_container",
2468                                       ))];
2469                                   IdOrClassVec::from_const_slice(
2470                                       IDS_AND_CLASSES_4413230059125905311,
2471                                   )
2472                               })
2473                               .with_children(DomVec::from_vec(vec![
2474                                   Dom::create_text(field.key.clone())
2475                                   .with_css_props(CSS_MATCH_1198521124955124418)
2476                                   .with_ids_and_classes({
2477                                       const IDS_AND_CLASSES_12334207996395559585:
2478                                           &[IdOrClass] =
2479                                           &[Class(AzString::from_const_str(
2480                                               "node_configuration_field_label",
2481                                           ))];
2482                                       IdOrClassVec::from_const_slice(
2483                                           IDS_AND_CLASSES_12334207996395559585,
2484                                       )
2485                                   }),
2486
2487                                   match &field.value {
2488                                       NodeTypeFieldValue::TextInput(initial_text) => {
2489                                           TextInput::create()
2490                                           .with_text(initial_text.clone())
2491                                           .with_on_focus_lost(field_local_dataset, nodegraph_on_textinput_focus_lost as TextInputOnFocusLostCallbackType)
2492                                           .dom()
2493                                       },
2494                                       NodeTypeFieldValue::NumberInput(initial_value) => {
2495                                           NumberInput::create(*initial_value)
2496                                           .with_on_focus_lost(field_local_dataset, nodegraph_on_numberinput_focus_lost as NumberInputOnFocusLostCallbackType)
2497                                           .dom()
2498                                       },
2499                                       NodeTypeFieldValue::CheckBox(initial_checked) => {
2500                                           CheckBox::create(*initial_checked)
2501                                           .with_on_toggle(field_local_dataset, nodegraph_on_checkbox_value_changed as CheckBoxOnToggleCallbackType)
2502                                           .dom()
2503                                       },
2504                                       NodeTypeFieldValue::ColorInput(initial_color) => {
2505                                           ColorInput::create(*initial_color)
2506                                           .with_on_value_change(field_local_dataset, nodegraph_on_colorinput_value_changed as ColorInputOnValueChangeCallbackType)
2507                                           .dom()
2508                                       },
2509                                       NodeTypeFieldValue::FileInput(file_path) => {
2510                                           let cb: FileInputOnPathChangeCallbackType = nodegraph_on_fileinput_button_clicked;
2511                                           FileInput::create(file_path.clone())
2512                                           .with_on_path_change(field_local_dataset, cb)
2513                                           .dom()
2514                                       },
2515                                   }
2516                               ]));
2517
2518                               fields.push(div);
2519                           }
2520
2521                           DomVec::from_vec(fields)
2522                       }),
2523                   Dom::create_div()
2524                       .with_css_props(CSS_MATCH_14906563417280941890)
2525                       .with_ids_and_classes({
2526                           const IDS_AND_CLASSES_4737474624251936466: &[IdOrClass] =
2527                               &[Class(AzString::from_const_str("outputs"))];
2528                           IdOrClassVec::from_const_slice(IDS_AND_CLASSES_4737474624251936466)
2529                       })
2530                       .with_children(DomVec::from_vec(vec![Dom::create_div()
2531                           .with_css_props(CSS_MATCH_10339190304804100510)
2532                           .with_ids_and_classes({
2533                               const IDS_AND_CLASSES_12883576328110161157: &[IdOrClass] =
2534                                   &[Class(AzString::from_const_str("node_output_wrapper"))];
2535                               IdOrClassVec::from_const_slice(
2536                                   IDS_AND_CLASSES_12883576328110161157,
2537                               )
2538                           })
2539                           .with_children(DomVec::from_vec(
2540                               outputs
2541                               .into_iter()
2542                               .enumerate()
2543                               .map(|(io_id, (output_label, output_color))| {
2544                                   use self::InputOrOutput::*;
2545                                   Dom::create_div()
2546                                       .with_css_props(CSS_MATCH_12400244273289328300)
2547                                       .with_ids_and_classes({
2548                                           const IDS_AND_CLASSES_10917819668096233812:
2549                                               &[IdOrClass] = &[Class(AzString::from_const_str(
2550                                               "node_output_container",
2551                                           ))];
2552                                           IdOrClassVec::from_const_slice(
2553                                               IDS_AND_CLASSES_10917819668096233812,
2554                                           )
2555                                       })
2556                                       .with_children(DomVec::from_vec(vec![
2557                                           Dom::create_div()
2558                                               .with_callbacks(vec![
2559                                                   CoreCallbackData {
2560                                                       event: EventFilter::Hover(HoverEventFilter::LeftMouseUp),
2561                                                       refany: RefAny::new(NodeInputOutputLocalDataset {
2562                                                           io_id: Output(io_id),
2563                                                           backref: node_local_dataset.clone(),
2564                                                       }),
2565                                                       callback: CoreCallback { cb: nodegraph_input_output_connect as usize, ctx: OptionRefAny::None },
2566                                                   },
2567                                                   CoreCallbackData {
2568                                                       event: EventFilter::Hover(HoverEventFilter::MiddleMouseUp),
2569                                                       refany: RefAny::new(NodeInputOutputLocalDataset {
2570                                                           io_id: Output(io_id),
2571                                                           backref: node_local_dataset.clone(),
2572                                                       }),
2573                                                       callback: CoreCallback { cb: nodegraph_input_output_disconnect as usize, ctx: OptionRefAny::None },
2574                                                   },
2575                                               ].into())
2576                                               .with_css_props(
2577                                                   CssPropertyWithConditionsVec::from_vec(vec![
2578                                                       // .node_output
2579                                                       CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
2580                                                           StyleBackgroundContentVecValue::Exact(vec![
2581                                                               StyleBackgroundContent::Color(output_color)
2582                                                           ].into()),
2583                                                       )),
2584                                                       CssPropertyWithConditions::simple(CssProperty::Cursor(StyleCursorValue::Exact(
2585                                                           StyleCursor::Pointer,
2586                                                       ))),
2587                                                       CssPropertyWithConditions::simple(CssProperty::Height(LayoutHeightValue::Exact(
2588                                                           LayoutHeight::Px(PixelValue::const_px(15),),
2589                                                       ))),
2590                                                       CssPropertyWithConditions::simple(CssProperty::Width(LayoutWidthValue::Exact(
2591                                                           LayoutWidth::Px(PixelValue::const_px(15),),
2592                                                       ))),
2593                                                   ])
2594                                               )
2595                                               .with_ids_and_classes({
2596                                                   const IDS_AND_CLASSES_17632471664405317563:
2597                                                       &[IdOrClass] = &[Class(
2598                                                       AzString::from_const_str("node_output"),
2599                                                   )];
2600                                                   IdOrClassVec::from_const_slice(
2601                                                       IDS_AND_CLASSES_17632471664405317563,
2602                                                   )
2603                                               }),
2604                                           Dom::create_div()
2605                                               .with_css_props(
2606                                                   CSS_MATCH_12038890904436132038,
2607                                               )
2608                                               .with_ids_and_classes({
2609                                                   const IDS_AND_CLASSES_1667960214206134147:
2610                                                       &[IdOrClass] =
2611                                                       &[Class(AzString::from_const_str(
2612                                                           "node_output_connection_label_wrapper",
2613                                                       ))];
2614                                                   IdOrClassVec::from_const_slice(
2615                                                       IDS_AND_CLASSES_1667960214206134147,
2616                                                   )
2617                                               })
2618                                               .with_children(DomVec::from_vec(vec![Dom::create_text(
2619                                                   output_label.clone(),
2620                                               )
2621                                               .with_css_props(
2622                                                   CSS_MATCH_2008162367868363199,
2623                                               )
2624                                               .with_ids_and_classes({
2625                                                   const IDS_AND_CLASSES_2974914452796301884:
2626                                                       &[IdOrClass] =
2627                                                       &[Class(AzString::from_const_str(
2628                                                           "node_output_connection_label",
2629                                                       ))];
2630                                                   IdOrClassVec::from_const_slice(
2631                                                       IDS_AND_CLASSES_2974914452796301884,
2632                                                   )
2633                                               })])),
2634                                       ]))
2635                               }).collect()
2636                           ))])),
2637               ])),
2638        ]))
2639        .with_dataset(Some(node_local_dataset).into())
2640    ].into())
2641}
2642
2643fn render_connections(node_graph: &NodeGraph, root_marker_nodedata: RefAny) -> Dom {
2644    const THEME_RED: ColorU = ColorU {
2645        r: 255,
2646        g: 0,
2647        b: 0,
2648        a: 255,
2649    }; // #484c52
2650    const BACKGROUND_THEME_RED: &[StyleBackgroundContent] =
2651        &[StyleBackgroundContent::Color(THEME_RED)];
2652    const BACKGROUND_COLOR_RED: StyleBackgroundContentVec =
2653        StyleBackgroundContentVec::from_const_slice(BACKGROUND_THEME_RED);
2654
2655    static NODEGRAPH_CONNECTIONS_CONTAINER_CLASS: &[IdOrClass] = &[Class(
2656        AzString::from_const_str("nodegraph-connections-container"),
2657    )];
2658
2659    static NODEGRAPH_CONNECTIONS_CONTAINER_PROPS: &[CssPropertyWithConditions] = &[
2660        CssPropertyWithConditions::simple(CssProperty::position(LayoutPosition::Absolute)),
2661        CssPropertyWithConditions::simple(CssProperty::flex_grow(LayoutFlexGrow::const_new(1))),
2662    ];
2663
2664    Dom::create_div()
2665        .with_ids_and_classes(IdOrClassVec::from_const_slice(
2666            NODEGRAPH_CONNECTIONS_CONTAINER_CLASS,
2667        ))
2668        .with_css_props(CssPropertyWithConditionsVec::from_const_slice(
2669            NODEGRAPH_CONNECTIONS_CONTAINER_PROPS,
2670        ))
2671        .with_dataset(Some(root_marker_nodedata).into())
2672        .with_children({
2673            let mut children = Vec::new();
2674
2675            for NodeIdNodeMap { node_id, node } in node_graph.nodes.as_ref().iter() {
2676                let out_node_id = node_id;
2677                let node_type_info = match node_graph
2678                    .node_types
2679                    .iter()
2680                    .find(|i| i.node_type_id == node.node_type)
2681                {
2682                    Some(s) => &s.node_type_info,
2683                    None => continue,
2684                };
2685
2686                for OutputConnection {
2687                    output_index,
2688                    connects_to,
2689                } in node.connect_out.as_ref().iter()
2690                {
2691                    let output_type_id = match node_type_info.outputs.get(*output_index) {
2692                        Some(s) => s,
2693                        None => continue,
2694                    };
2695
2696                    let output_color = match node_graph
2697                        .input_output_types
2698                        .iter()
2699                        .find(|o| o.io_type_id == *output_type_id)
2700                    {
2701                        Some(s) => s.io_info.color.clone(),
2702                        None => continue,
2703                    };
2704
2705                    for InputNodeAndIndex {
2706                        node_id,
2707                        input_index,
2708                    } in connects_to.as_ref().iter()
2709                    {
2710                        let in_node_id = node_id;
2711
2712                        let mut cld = ConnectionLocalDataset {
2713                            out_node_id: *out_node_id,
2714                            out_idx: *output_index,
2715                            in_node_id: *in_node_id,
2716                            in_idx: *input_index,
2717                            swap_vert: false,
2718                            swap_horz: false,
2719                            color: output_color,
2720                        };
2721
2722                        let (rect, swap_vert, swap_horz) = match get_rect(&node_graph, cld) {
2723                            Some(s) => s,
2724                            None => continue,
2725                        };
2726
2727                        cld.swap_vert = swap_vert;
2728                        cld.swap_horz = swap_horz;
2729
2730                        let cld_refany = RefAny::new(cld);
2731                        let connection_div = Dom::create_image(ImageRef::callback(
2732                            draw_connection as usize,
2733                            cld_refany.clone(),
2734                        ))
2735                        .with_dataset(Some(cld_refany).into())
2736                        .with_css_props(
2737                            vec![
2738                                CssPropertyWithConditions::simple(CssProperty::Transform(
2739                                    StyleTransformVecValue::Exact(
2740                                        vec![
2741                                            StyleTransform::Translate(StyleTransformTranslate2D {
2742                                                x: PixelValue::px(
2743                                                    node_graph.offset.x + rect.origin.x,
2744                                                ),
2745                                                y: PixelValue::px(
2746                                                    node_graph.offset.y + rect.origin.y,
2747                                                ),
2748                                            }),
2749                                            StyleTransform::ScaleX(PercentageValue::new(
2750                                                node_graph.scale_factor * 100.0,
2751                                            )),
2752                                            StyleTransform::ScaleY(PercentageValue::new(
2753                                                node_graph.scale_factor * 100.0,
2754                                            )),
2755                                        ]
2756                                        .into(),
2757                                    ),
2758                                )),
2759                                CssPropertyWithConditions::simple(CssProperty::Width(
2760                                    LayoutWidthValue::Exact(LayoutWidth::Px(PixelValue::px(
2761                                        rect.size.width,
2762                                    ))),
2763                                )),
2764                                CssPropertyWithConditions::simple(CssProperty::Height(
2765                                    LayoutHeightValue::Exact(LayoutHeight::Px(PixelValue::px(
2766                                        rect.size.height,
2767                                    ))),
2768                                )),
2769                            ]
2770                            .into(),
2771                        );
2772
2773                        children.push(
2774                            Dom::create_div()
2775                                .with_inline_style(
2776                                    "flex-grow: 1; position: absolute; overflow: hidden;",
2777                                )
2778                                .with_children(vec![connection_div].into()),
2779                        );
2780                    }
2781                }
2782            }
2783
2784            children.into()
2785        })
2786}
2787
2788extern "C" fn draw_connection(mut refany: RefAny, _info: ()) -> ImageRef {
2789    // RenderImageCallbackInfo not available in memtest
2790    // let size = info.get_bounds().get_physical_size();
2791    let size = azul_core::geom::LogicalSize {
2792        width: 100.0,
2793        height: 100.0,
2794    };
2795    let invalid = ImageRef::null_image(
2796        size.width as usize,
2797        size.height as usize,
2798        RawImageFormat::R8,
2799        Vec::new(),
2800    );
2801
2802    // Cannot call draw_connection_inner without RenderImageCallbackInfo
2803    invalid
2804}
2805
2806fn draw_connection_inner(
2807    mut refany: RefAny,
2808    _info: &mut (),
2809    _texture_size: PhysicalSizeU32,
2810) -> Option<ImageRef> {
2811    use crate::xml::svg::tessellate_path_stroke;
2812
2813    let refany = refany.downcast_ref::<ConnectionLocalDataset>()?;
2814    let refany = &*refany;
2815
2816    // Cannot proceed without RenderImageCallbackInfo - all code below requires it
2817    return None;
2818
2819    /* Commented out - requires RenderImageCallbackInfo
2820    let gl_context = info.get_gl_context().into_option()?;
2821
2822    let mut texture = Texture::allocate_rgba8(
2823        gl_context.clone(),
2824        texture_size,
2825        coloru_from_str("#00000000"),
2826    );
2827
2828    texture.clear();
2829
2830    let mut stroke_style = SvgStrokeStyle::default();
2831    stroke_style.line_width = 4.0;
2832
2833    let tex_half = (texture_size.width as f32) / 2.0;
2834
2835    let tessellated_stroke = tessellate_path_stroke(
2836        &SvgPath {
2837            items: vec![
2838                // depending on in which quadrant the curve is drawn relative to the input node,
2839                // we need a different curve
2840                if refany.swap_vert {
2841                    if refany.swap_horz {
2842                        //          /- input
2843                        //  output-/
2844                        SvgPathElement::CubicCurve(SvgCubicCurve {
2845                            start: SvgPoint {
2846                                x: 0.0,
2847                                y: texture_size.height as f32 - (CONNECTION_DOT_HEIGHT / 2.0),
2848                            },
2849                            ctrl_1: SvgPoint {
2850                                x: tex_half,
2851                                y: texture_size.height as f32 - (CONNECTION_DOT_HEIGHT / 2.0),
2852                            },
2853                            ctrl_2: SvgPoint {
2854                                x: tex_half,
2855                                y: CONNECTION_DOT_HEIGHT / 2.0,
2856                            },
2857                            end: SvgPoint {
2858                                x: texture_size.width as f32,
2859                                y: CONNECTION_DOT_HEIGHT / 2.0,
2860                            },
2861                        })
2862                    } else {
2863                        //  input -\
2864                        //          \- output
2865                        SvgPathElement::CubicCurve(SvgCubicCurve {
2866                            start: SvgPoint {
2867                                x: 0.0,
2868                                y: CONNECTION_DOT_HEIGHT / 2.0,
2869                            },
2870                            ctrl_1: SvgPoint {
2871                                x: tex_half,
2872                                y: CONNECTION_DOT_HEIGHT / 2.0,
2873                            },
2874                            ctrl_2: SvgPoint {
2875                                x: tex_half,
2876                                y: texture_size.height as f32 - (CONNECTION_DOT_HEIGHT / 2.0),
2877                            },
2878                            end: SvgPoint {
2879                                x: texture_size.width as f32,
2880                                y: texture_size.height as f32 - (CONNECTION_DOT_HEIGHT / 2.0),
2881                            },
2882                        })
2883                    }
2884                } else {
2885                    if refany.swap_horz {
2886                        //  output-\
2887                        //          \- input
2888                        SvgPathElement::CubicCurve(SvgCubicCurve {
2889                            start: SvgPoint {
2890                                x: 0.0,
2891                                y: CONNECTION_DOT_HEIGHT / 2.0,
2892                            },
2893                            ctrl_1: SvgPoint {
2894                                x: tex_half,
2895                                y: CONNECTION_DOT_HEIGHT / 2.0,
2896                            },
2897                            ctrl_2: SvgPoint {
2898                                x: tex_half,
2899                                y: texture_size.height as f32 - (CONNECTION_DOT_HEIGHT / 2.0),
2900                            },
2901                            end: SvgPoint {
2902                                x: texture_size.width as f32,
2903                                y: texture_size.height as f32 - (CONNECTION_DOT_HEIGHT / 2.0),
2904                            },
2905                        })
2906                    } else {
2907                        //         /- output
2908                        // input -/
2909                        SvgPathElement::CubicCurve(SvgCubicCurve {
2910                            start: SvgPoint {
2911                                x: 0.0,
2912                                y: texture_size.height as f32 - (CONNECTION_DOT_HEIGHT / 2.0),
2913                            },
2914                            ctrl_1: SvgPoint {
2915                                x: tex_half,
2916                                y: texture_size.height as f32 - (CONNECTION_DOT_HEIGHT / 2.0),
2917                            },
2918                            ctrl_2: SvgPoint {
2919                                x: tex_half,
2920                                y: CONNECTION_DOT_HEIGHT / 2.0,
2921                            },
2922                            end: SvgPoint {
2923                                x: texture_size.width as f32,
2924                                y: CONNECTION_DOT_HEIGHT / 2.0,
2925                            },
2926                        })
2927                    }
2928                },
2929            ]
2930            .into(),
2931        },
2932        stroke_style,
2933    );
2934
2935    let tesselated_gpu_buffer = TessellatedGPUSvgNode::new(&tessellated_stroke, gl_context.clone());
2936
2937    tesselated_gpu_buffer.draw(&mut texture, texture_size, refany.color, Vec::new().into());
2938
2939    Some(ImageRef::new_gltexture(texture))
2940    */
2941}
2942
2943const NODE_WIDTH: f32 = 250.0;
2944const V_OFFSET: f32 = 71.0;
2945const DIST_BETWEEN_NODES: f32 = 10.0;
2946const CONNECTION_DOT_HEIGHT: f32 = 15.0;
2947
2948// calculates the rect on which the connection is drawn in the UI
2949fn get_rect(
2950    node_graph: &NodeGraph,
2951    connection: ConnectionLocalDataset,
2952) -> Option<(LogicalRect, bool, bool)> {
2953    let ConnectionLocalDataset {
2954        out_node_id,
2955        out_idx,
2956        in_node_id,
2957        in_idx,
2958        ..
2959    } = connection;
2960    let out_node = node_graph.nodes.iter().find(|i| i.node_id == out_node_id)?;
2961    let in_node = node_graph.nodes.iter().find(|i| i.node_id == in_node_id)?;
2962
2963    let x_out = out_node.node.position.x + NODE_WIDTH;
2964    let y_out = out_node.node.position.y
2965        + V_OFFSET
2966        + (out_idx as f32 * (DIST_BETWEEN_NODES + CONNECTION_DOT_HEIGHT));
2967
2968    let x_in = in_node.node.position.x;
2969    let y_in = in_node.node.position.y
2970        + V_OFFSET
2971        + (in_idx as f32 * (DIST_BETWEEN_NODES + CONNECTION_DOT_HEIGHT));
2972
2973    let should_swap_vertical = y_in > y_out;
2974    let should_swap_horizontal = x_in < x_out;
2975
2976    let width = (x_in - x_out).abs();
2977    let height = (y_in - y_out).abs() + CONNECTION_DOT_HEIGHT;
2978
2979    let x = x_in.min(x_out);
2980    let y = y_in.min(y_out);
2981
2982    Some((
2983        LogicalRect {
2984            size: LogicalSize { width, height },
2985            origin: LogicalPosition { x, y },
2986        },
2987        should_swap_vertical,
2988        should_swap_horizontal,
2989    ))
2990}
2991
2992extern "C" fn nodegraph_set_active_node(mut refany: RefAny, _info: CallbackInfo) -> Update {
2993    let data_clone = refany.clone();
2994    if let Some(mut refany) = refany.downcast_mut::<NodeLocalDataset>() {
2995        let node_id = refany.node_id.clone();
2996        if let Some(mut backref) = refany.backref.downcast_mut::<NodeGraphLocalDataset>() {
2997            backref.active_node_being_dragged = Some((node_id, data_clone));
2998        }
2999    }
3000    Update::DoNothing
3001}
3002
3003extern "C" fn nodegraph_unset_active_node(mut refany: RefAny, _info: CallbackInfo) -> Update {
3004    if let Some(mut refany) = refany.downcast_mut::<NodeGraphLocalDataset>() {
3005        refany.active_node_being_dragged = None;
3006    }
3007    Update::DoNothing
3008}
3009
3010// drag either the graph or the currently active nodes
3011extern "C" fn nodegraph_drag_graph_or_nodes(mut refany: RefAny, mut info: CallbackInfo) -> Update {
3012    let mut refany = match refany.downcast_mut::<NodeGraphLocalDataset>() {
3013        Some(s) => s,
3014        None => return Update::DoNothing,
3015    };
3016    let refany = &mut *refany;
3017
3018    let prev = match info.get_previous_mouse_state() {
3019        Some(s) => s,
3020        None => return Update::DoNothing,
3021    };
3022    let cur = info.get_current_mouse_state();
3023    if !(cur.left_down && prev.left_down) {
3024        // event is not a drag event
3025        return Update::DoNothing;
3026    }
3027
3028    let (current_mouse_pos, previous_mouse_pos) = match (cur.cursor_position, prev.cursor_position)
3029    {
3030        (InWindow(c), InWindow(p)) => (c, p),
3031        _ => return Update::DoNothing,
3032    };
3033
3034    let dx = (current_mouse_pos.x - previous_mouse_pos.x) * (1.0 / refany.node_graph.scale_factor);
3035    let dy = (current_mouse_pos.y - previous_mouse_pos.y) * (1.0 / refany.node_graph.scale_factor);
3036    let nodegraph_node = info.get_hit_node();
3037
3038    let should_update = match refany.active_node_being_dragged.clone() {
3039        // drag node
3040        Some((node_graph_node_id, data_marker)) => {
3041            let node_connection_marker = &mut refany.node_connection_marker;
3042
3043            let _nodegraph_node = info.get_hit_node();
3044            let result = match refany.callbacks.on_node_dragged.as_ref() {
3045                Some(OnNodeDragged { callback, refany }) => (callback.cb)(
3046                    refany.clone(),
3047                    info.clone(),
3048                    node_graph_node_id,
3049                    NodeDragAmount { x: dx, y: dy },
3050                ),
3051                None => Update::DoNothing,
3052            };
3053
3054            // update the visual transform of the node in the UI
3055            let node_position = match refany
3056                .node_graph
3057                .nodes
3058                .iter_mut()
3059                .find(|i| i.node_id == node_graph_node_id)
3060            {
3061                Some(s) => {
3062                    s.node.position.x += dx;
3063                    s.node.position.y += dy;
3064                    s.node.position
3065                }
3066                None => return Update::DoNothing,
3067            };
3068
3069            let visual_node_id = match info.get_node_id_of_root_dataset(data_marker) {
3070                Some(s) => s,
3071                None => return Update::DoNothing,
3072            };
3073
3074            let node_transform = StyleTransformTranslate2D {
3075                x: PixelValue::px(node_position.x + refany.node_graph.offset.x),
3076                y: PixelValue::px(node_position.y + refany.node_graph.offset.y),
3077            };
3078
3079            info.set_css_property(
3080                visual_node_id,
3081                CssProperty::transform(
3082                    if refany.node_graph.scale_factor != 1.0 {
3083                        vec![
3084                            StyleTransform::Translate(node_transform),
3085                            StyleTransform::ScaleX(PercentageValue::new(
3086                                refany.node_graph.scale_factor * 100.0,
3087                            )),
3088                            StyleTransform::ScaleY(PercentageValue::new(
3089                                refany.node_graph.scale_factor * 100.0,
3090                            )),
3091                        ]
3092                    } else {
3093                        vec![StyleTransform::Translate(node_transform)]
3094                    }
3095                    .into(),
3096                ),
3097            );
3098
3099            // get the NodeId of the node containing all the connection lines
3100            let connection_container_nodeid =
3101                match info.get_node_id_of_root_dataset(node_connection_marker.clone()) {
3102                    Some(s) => s,
3103                    None => return result,
3104                };
3105
3106            // animate all the connections
3107            let mut first_connection_child = info.get_first_child(connection_container_nodeid);
3108
3109            while let Some(connection_nodeid) = first_connection_child {
3110                first_connection_child = info.get_next_sibling(connection_nodeid);
3111
3112                let first_child = match info.get_first_child(connection_nodeid) {
3113                    Some(s) => s,
3114                    None => continue,
3115                };
3116
3117                let mut dataset = match info.get_dataset(first_child) {
3118                    Some(s) => s,
3119                    None => continue,
3120                };
3121
3122                let mut cld = match dataset.downcast_mut::<ConnectionLocalDataset>() {
3123                    Some(s) => s,
3124                    None => continue,
3125                };
3126
3127                if !(cld.out_node_id == node_graph_node_id || cld.in_node_id == node_graph_node_id)
3128                {
3129                    continue; // connection does not need to be modified
3130                }
3131
3132                let (new_rect, swap_vert, swap_horz) = match get_rect(&refany.node_graph, *cld) {
3133                    Some(s) => s,
3134                    None => continue,
3135                };
3136
3137                cld.swap_vert = swap_vert;
3138                cld.swap_horz = swap_horz;
3139
3140                let node_transform = StyleTransformTranslate2D {
3141                    x: PixelValue::px(refany.node_graph.offset.x + new_rect.origin.x),
3142                    y: PixelValue::px(refany.node_graph.offset.y + new_rect.origin.y),
3143                };
3144
3145                info.set_css_property(
3146                    first_child,
3147                    CssProperty::transform(
3148                        if refany.node_graph.scale_factor != 1.0 {
3149                            vec![
3150                                StyleTransform::Translate(node_transform),
3151                                StyleTransform::ScaleX(PercentageValue::new(
3152                                    refany.node_graph.scale_factor * 100.0,
3153                                )),
3154                                StyleTransform::ScaleY(PercentageValue::new(
3155                                    refany.node_graph.scale_factor * 100.0,
3156                                )),
3157                            ]
3158                        } else {
3159                            vec![StyleTransform::Translate(node_transform)]
3160                        }
3161                        .into(),
3162                    ),
3163                );
3164
3165                info.set_css_property(
3166                    first_child,
3167                    CssProperty::Width(LayoutWidthValue::Exact(LayoutWidth::Px(PixelValue::px(
3168                        new_rect.size.width,
3169                    )))),
3170                );
3171                info.set_css_property(
3172                    first_child,
3173                    CssProperty::Height(LayoutHeightValue::Exact(LayoutHeight::Px(
3174                        PixelValue::px(new_rect.size.height),
3175                    ))),
3176                );
3177            }
3178
3179            result
3180        }
3181        // drag graph
3182        None => {
3183            let result = match refany.callbacks.on_node_graph_dragged.as_ref() {
3184                Some(OnNodeGraphDragged { callback, refany }) => (callback.cb)(
3185                    refany.clone(),
3186                    info.clone(),
3187                    GraphDragAmount { x: dx, y: dy },
3188                ),
3189                None => Update::DoNothing,
3190            };
3191
3192            refany.node_graph.offset.x += dx;
3193            refany.node_graph.offset.y += dy;
3194
3195            // Update the visual node positions
3196            let node_container = match info.get_first_child(nodegraph_node) {
3197                Some(s) => s,
3198                None => return Update::DoNothing,
3199            };
3200
3201            let node_container = match info.get_next_sibling(node_container) {
3202                Some(s) => s,
3203                None => return Update::DoNothing,
3204            };
3205
3206            let mut node = match info.get_first_child(node_container) {
3207                Some(s) => s,
3208                None => return Update::DoNothing,
3209            };
3210
3211            loop {
3212                let node_first_child = match info.get_first_child(node) {
3213                    Some(s) => s,
3214                    None => return Update::DoNothing,
3215                };
3216
3217                let mut node_local_dataset = match info.get_dataset(node_first_child) {
3218                    None => return Update::DoNothing,
3219                    Some(s) => s,
3220                };
3221
3222                let node_graph_node_id = match node_local_dataset.downcast_ref::<NodeLocalDataset>()
3223                {
3224                    Some(s) => s,
3225                    None => continue,
3226                };
3227
3228                let node_graph_node_id = node_graph_node_id.node_id;
3229
3230                let node_position = match refany
3231                    .node_graph
3232                    .nodes
3233                    .iter()
3234                    .find(|i| i.node_id == node_graph_node_id)
3235                {
3236                    Some(s) => s.node.position,
3237                    None => continue,
3238                };
3239
3240                let node_transform = StyleTransformTranslate2D {
3241                    x: PixelValue::px(node_position.x + refany.node_graph.offset.x),
3242                    y: PixelValue::px(node_position.y + refany.node_graph.offset.y),
3243                };
3244
3245                info.set_css_property(
3246                    node_first_child,
3247                    CssProperty::transform(
3248                        if refany.node_graph.scale_factor != 1.0 {
3249                            vec![
3250                                StyleTransform::Translate(node_transform),
3251                                StyleTransform::ScaleX(PercentageValue::new(
3252                                    refany.node_graph.scale_factor * 100.0,
3253                                )),
3254                                StyleTransform::ScaleY(PercentageValue::new(
3255                                    refany.node_graph.scale_factor * 100.0,
3256                                )),
3257                            ]
3258                        } else {
3259                            vec![StyleTransform::Translate(node_transform)]
3260                        }
3261                        .into(),
3262                    ),
3263                );
3264
3265                node = match info.get_next_sibling(node) {
3266                    Some(s) => s,
3267                    None => break,
3268                };
3269            }
3270
3271            let node_connection_marker = &mut refany.node_connection_marker;
3272
3273            // Update the connection positions
3274            let connection_container_nodeid =
3275                match info.get_node_id_of_root_dataset(node_connection_marker.clone()) {
3276                    Some(s) => s,
3277                    None => return result,
3278                };
3279
3280            let mut first_connection_child = info.get_first_child(connection_container_nodeid);
3281
3282            while let Some(connection_nodeid) = first_connection_child {
3283                first_connection_child = info.get_next_sibling(connection_nodeid);
3284
3285                let first_child = match info.get_first_child(connection_nodeid) {
3286                    Some(s) => s,
3287                    None => continue,
3288                };
3289
3290                let mut dataset = match info.get_dataset(first_child) {
3291                    Some(s) => s,
3292                    None => continue,
3293                };
3294
3295                let cld = match dataset.downcast_ref::<ConnectionLocalDataset>() {
3296                    Some(s) => s,
3297                    None => continue,
3298                };
3299
3300                let (new_rect, _, _) = match get_rect(&refany.node_graph, *cld) {
3301                    Some(s) => s,
3302                    None => continue,
3303                };
3304
3305                info.set_css_property(
3306                    first_child,
3307                    CssProperty::transform(
3308                        vec![
3309                            StyleTransform::Translate(StyleTransformTranslate2D {
3310                                x: PixelValue::px(refany.node_graph.offset.x + new_rect.origin.x),
3311                                y: PixelValue::px(refany.node_graph.offset.y + new_rect.origin.y),
3312                            }),
3313                            StyleTransform::ScaleX(PercentageValue::new(
3314                                refany.node_graph.scale_factor * 100.0,
3315                            )),
3316                            StyleTransform::ScaleY(PercentageValue::new(
3317                                refany.node_graph.scale_factor * 100.0,
3318                            )),
3319                        ]
3320                        .into(),
3321                    ),
3322                );
3323            }
3324
3325            result
3326        }
3327    };
3328
3329    info.stop_propagation();
3330
3331    should_update
3332}
3333
3334extern "C" fn nodegraph_duplicate_node(mut refany: RefAny, _info: CallbackInfo) -> Update {
3335    let _data = match refany.downcast_mut::<NodeLocalDataset>() {
3336        Some(s) => s,
3337        None => return Update::DoNothing,
3338    };
3339
3340    Update::DoNothing // TODO
3341}
3342
3343extern "C" fn nodegraph_delete_node(mut refany: RefAny, mut info: CallbackInfo) -> Update {
3344    let mut refany = match refany.downcast_mut::<NodeLocalDataset>() {
3345        Some(s) => s,
3346        None => return Update::DoNothing,
3347    };
3348
3349    let node_id = refany.node_id.clone();
3350
3351    let mut backref = match refany.backref.downcast_mut::<NodeGraphLocalDataset>() {
3352        Some(s) => s,
3353        None => return Update::DoNothing,
3354    };
3355
3356    let result = match backref.callbacks.on_node_removed.as_ref() {
3357        Some(OnNodeRemoved { callback, refany }) => (callback.cb)(refany.clone(), info, node_id),
3358        None => Update::DoNothing,
3359    };
3360
3361    result
3362}
3363
3364extern "C" fn nodegraph_context_menu_click(mut refany: RefAny, mut info: CallbackInfo) -> Update {
3365    use azul_core::window::CursorPosition;
3366
3367    let mut refany = match refany.downcast_mut::<ContextMenuEntryLocalDataset>() {
3368        Some(s) => s,
3369        None => return Update::DoNothing,
3370    };
3371
3372    let new_node_type = refany.node_type.clone();
3373
3374    let node_graph_wrapper_id = match info.get_node_id_of_root_dataset(refany.backref.clone()) {
3375        Some(s) => s,
3376        None => return Update::DoNothing,
3377    };
3378
3379    let mut backref = match refany.backref.downcast_mut::<NodeGraphLocalDataset>() {
3380        Some(s) => s,
3381        None => return Update::DoNothing,
3382    };
3383
3384    let node_wrapper_offset = info
3385        .get_node_position(node_graph_wrapper_id)
3386        .map(|p| p)
3387        .map(|p| (p.x, p.y))
3388        .unwrap_or((0.0, 0.0));
3389
3390    let cursor_in_viewport = match info.get_current_mouse_state().cursor_position {
3391        CursorPosition::InWindow(i) => i,
3392        CursorPosition::OutOfWindow(i) => i,
3393        _ => LogicalPosition::zero(),
3394    };
3395
3396    let new_node_pos = NodePosition {
3397        x: (cursor_in_viewport.x - node_wrapper_offset.0) * (1.0 / backref.node_graph.scale_factor)
3398            - backref.node_graph.offset.x,
3399        y: (cursor_in_viewport.y - node_wrapper_offset.1) * (1.0 / backref.node_graph.scale_factor)
3400            - backref.node_graph.offset.y,
3401    };
3402
3403    let new_node_id = backref.node_graph.generate_unique_node_id();
3404
3405    let result = match backref.callbacks.on_node_added.as_ref() {
3406        Some(OnNodeAdded { callback, refany }) => (callback.cb)(
3407            refany.clone(),
3408            info,
3409            new_node_type,
3410            new_node_id,
3411            new_node_pos,
3412        ),
3413        None => Update::DoNothing,
3414    };
3415
3416    result
3417}
3418
3419extern "C" fn nodegraph_input_output_connect(mut refany: RefAny, mut info: CallbackInfo) -> Update {
3420    use self::InputOrOutput::*;
3421
3422    let mut refany = match refany.downcast_mut::<NodeInputOutputLocalDataset>() {
3423        Some(s) => s,
3424        None => return Update::DoNothing,
3425    };
3426
3427    let io_id = refany.io_id.clone();
3428
3429    let mut backref = match refany.backref.downcast_mut::<NodeLocalDataset>() {
3430        Some(s) => s,
3431        None => return Update::DoNothing,
3432    };
3433
3434    let node_id = backref.node_id.clone();
3435
3436    let mut backref = match backref.backref.downcast_mut::<NodeGraphLocalDataset>() {
3437        Some(s) => s,
3438        None => return Update::DoNothing,
3439    };
3440
3441    let (input_node, input_index, output_node, output_index) =
3442        match backref.last_input_or_output_clicked.clone() {
3443            None => {
3444                backref.last_input_or_output_clicked = Some((node_id, io_id));
3445                return Update::DoNothing;
3446            }
3447            Some((prev_node_id, prev_io_id)) => {
3448                match (prev_io_id, io_id) {
3449                    (Input(i), Output(o)) => (prev_node_id, i, node_id, o),
3450                    (Output(o), Input(i)) => (node_id, i, prev_node_id, o),
3451                    _ => {
3452                        // error: trying to connect input to input or output to output
3453                        backref.last_input_or_output_clicked = None;
3454                        return Update::DoNothing;
3455                    }
3456                }
3457            }
3458        };
3459
3460    // verify that the nodetype matches
3461    match backref.node_graph.connect_input_output(
3462        input_node,
3463        input_index,
3464        output_node,
3465        output_index,
3466    ) {
3467        Ok(_) => {}
3468        Err(e) => {
3469            eprintln!("{:?}", e);
3470            backref.last_input_or_output_clicked = None;
3471            return Update::DoNothing;
3472        }
3473    }
3474
3475    let result = match backref.callbacks.on_node_connected.as_ref() {
3476        Some(OnNodeConnected { callback, refany }) => {
3477            let r = (callback.cb)(
3478                refany.clone(),
3479                info,
3480                input_node,
3481                input_index,
3482                output_node,
3483                output_index,
3484            );
3485            backref.last_input_or_output_clicked = None;
3486            r
3487        }
3488        None => Update::DoNothing,
3489    };
3490
3491    result
3492}
3493
3494extern "C" fn nodegraph_input_output_disconnect(mut refany: RefAny, info: CallbackInfo) -> Update {
3495    use self::InputOrOutput::*;
3496
3497    let mut refany = match refany.downcast_mut::<NodeInputOutputLocalDataset>() {
3498        Some(s) => s,
3499        None => return Update::DoNothing,
3500    };
3501
3502    let io_id = refany.io_id.clone();
3503
3504    let mut backref = match refany.backref.downcast_mut::<NodeLocalDataset>() {
3505        Some(s) => s,
3506        None => return Update::DoNothing,
3507    };
3508
3509    let node_id = backref.node_id.clone();
3510
3511    let mut backref = match backref.backref.downcast_mut::<NodeGraphLocalDataset>() {
3512        Some(s) => s,
3513        None => return Update::DoNothing,
3514    };
3515
3516    let mut result = Update::DoNothing;
3517    match io_id {
3518        Input(i) => {
3519            result.max_self(
3520                match backref.callbacks.on_node_input_disconnected.as_ref() {
3521                    Some(OnNodeInputDisconnected { callback, refany }) => {
3522                        (callback.cb)(refany.clone(), info, node_id, i)
3523                    }
3524                    None => Update::DoNothing,
3525                },
3526            );
3527        }
3528        Output(o) => {
3529            result.max_self(
3530                match backref.callbacks.on_node_output_disconnected.as_ref() {
3531                    Some(OnNodeOutputDisconnected { callback, refany }) => {
3532                        (callback.cb)(refany.clone(), info, node_id, o)
3533                    }
3534                    None => Update::DoNothing,
3535                },
3536            );
3537        }
3538    };
3539
3540    result
3541}
3542
3543extern "C" fn nodegraph_on_textinput_focus_lost(
3544    mut refany: RefAny,
3545    info: CallbackInfo,
3546    textinputstate: TextInputState,
3547) -> Update {
3548    let mut refany = match refany.downcast_mut::<NodeFieldLocalDataset>() {
3549        Some(s) => s,
3550        None => return Update::DoNothing,
3551    };
3552
3553    let field_idx = refany.field_idx;
3554
3555    let mut node_local_dataset = match refany.backref.downcast_mut::<NodeLocalDataset>() {
3556        Some(s) => s,
3557        None => return Update::DoNothing,
3558    };
3559
3560    let node_id = node_local_dataset.node_id;
3561
3562    let mut node_graph = match node_local_dataset
3563        .backref
3564        .downcast_mut::<NodeGraphLocalDataset>()
3565    {
3566        Some(s) => s,
3567        None => return Update::DoNothing,
3568    };
3569
3570    let node_type = match node_graph
3571        .node_graph
3572        .nodes
3573        .iter()
3574        .find(|i| i.node_id == node_id)
3575    {
3576        Some(s) => s.node.node_type,
3577        None => return Update::DoNothing,
3578    };
3579
3580    let result = match node_graph.callbacks.on_node_field_edited.as_ref() {
3581        Some(OnNodeFieldEdited { refany, callback }) => (callback.cb)(
3582            refany.clone(),
3583            info,
3584            node_id,
3585            field_idx,
3586            node_type,
3587            NodeTypeFieldValue::TextInput(textinputstate.get_text().into()),
3588        ),
3589        None => Update::DoNothing,
3590    };
3591
3592    result
3593}
3594
3595extern "C" fn nodegraph_on_numberinput_focus_lost(
3596    mut refany: RefAny,
3597    info: CallbackInfo,
3598    numberinputstate: NumberInputState,
3599) -> Update {
3600    let mut refany = match refany.downcast_mut::<NodeFieldLocalDataset>() {
3601        Some(s) => s,
3602        None => return Update::DoNothing,
3603    };
3604
3605    let field_idx = refany.field_idx;
3606
3607    let mut node_local_dataset = match refany.backref.downcast_mut::<NodeLocalDataset>() {
3608        Some(s) => s,
3609        None => return Update::DoNothing,
3610    };
3611
3612    let node_id = node_local_dataset.node_id;
3613
3614    let mut node_graph = match node_local_dataset
3615        .backref
3616        .downcast_mut::<NodeGraphLocalDataset>()
3617    {
3618        Some(s) => s,
3619        None => return Update::DoNothing,
3620    };
3621
3622    let node_type = match node_graph
3623        .node_graph
3624        .nodes
3625        .iter()
3626        .find(|i| i.node_id == node_id)
3627    {
3628        Some(s) => s.node.node_type,
3629        None => return Update::DoNothing,
3630    };
3631
3632    let result = match node_graph.callbacks.on_node_field_edited.as_ref() {
3633        Some(OnNodeFieldEdited { refany, callback }) => (callback.cb)(
3634            refany.clone(),
3635            info,
3636            node_id,
3637            field_idx,
3638            node_type,
3639            NodeTypeFieldValue::NumberInput(numberinputstate.number),
3640        ),
3641        None => Update::DoNothing,
3642    };
3643
3644    result
3645}
3646
3647extern "C" fn nodegraph_on_checkbox_value_changed(
3648    mut refany: RefAny,
3649    info: CallbackInfo,
3650    checkboxinputstate: CheckBoxState,
3651) -> Update {
3652    let mut refany = match refany.downcast_mut::<NodeFieldLocalDataset>() {
3653        Some(s) => s,
3654        None => return Update::DoNothing,
3655    };
3656
3657    let field_idx = refany.field_idx;
3658
3659    let mut node_local_dataset = match refany.backref.downcast_mut::<NodeLocalDataset>() {
3660        Some(s) => s,
3661        None => return Update::DoNothing,
3662    };
3663
3664    let node_id = node_local_dataset.node_id;
3665
3666    let mut node_graph = match node_local_dataset
3667        .backref
3668        .downcast_mut::<NodeGraphLocalDataset>()
3669    {
3670        Some(s) => s,
3671        None => return Update::DoNothing,
3672    };
3673
3674    let node_type = match node_graph
3675        .node_graph
3676        .nodes
3677        .iter()
3678        .find(|i| i.node_id == node_id)
3679    {
3680        Some(s) => s.node.node_type,
3681        None => return Update::DoNothing,
3682    };
3683
3684    let result = match node_graph.callbacks.on_node_field_edited.as_ref() {
3685        Some(OnNodeFieldEdited { refany, callback }) => (callback.cb)(
3686            refany.clone(),
3687            info,
3688            node_id,
3689            field_idx,
3690            node_type,
3691            NodeTypeFieldValue::CheckBox(checkboxinputstate.checked),
3692        ),
3693        None => Update::DoNothing,
3694    };
3695
3696    result
3697}
3698
3699extern "C" fn nodegraph_on_colorinput_value_changed(
3700    mut refany: RefAny,
3701    info: CallbackInfo,
3702    colorinputstate: ColorInputState,
3703) -> Update {
3704    let mut refany = match refany.downcast_mut::<NodeFieldLocalDataset>() {
3705        Some(s) => s,
3706        None => return Update::DoNothing,
3707    };
3708
3709    let field_idx = refany.field_idx;
3710
3711    let mut node_local_dataset = match refany.backref.downcast_mut::<NodeLocalDataset>() {
3712        Some(s) => s,
3713        None => return Update::DoNothing,
3714    };
3715
3716    let node_id = node_local_dataset.node_id;
3717    let mut node_graph = match node_local_dataset
3718        .backref
3719        .downcast_mut::<NodeGraphLocalDataset>()
3720    {
3721        Some(s) => s,
3722        None => return Update::DoNothing,
3723    };
3724
3725    let node_type = match node_graph
3726        .node_graph
3727        .nodes
3728        .iter()
3729        .find(|i| i.node_id == node_id)
3730    {
3731        Some(s) => s.node.node_type,
3732        None => return Update::DoNothing,
3733    };
3734
3735    let result = match node_graph.callbacks.on_node_field_edited.as_ref() {
3736        Some(OnNodeFieldEdited { refany, callback }) => (callback.cb)(
3737            refany.clone(),
3738            info,
3739            node_id,
3740            field_idx,
3741            node_type,
3742            NodeTypeFieldValue::ColorInput(colorinputstate.color),
3743        ),
3744        None => Update::DoNothing,
3745    };
3746
3747    result
3748}
3749
3750extern "C" fn nodegraph_on_fileinput_button_clicked(
3751    mut refany: RefAny,
3752    info: CallbackInfo,
3753    file: FileInputState,
3754) -> Update {
3755    let mut refany = match refany.downcast_mut::<NodeFieldLocalDataset>() {
3756        Some(s) => s,
3757        None => return Update::DoNothing,
3758    };
3759
3760    let field_idx = refany.field_idx;
3761
3762    let mut node_local_dataset = match refany.backref.downcast_mut::<NodeLocalDataset>() {
3763        Some(s) => s,
3764        None => return Update::DoNothing,
3765    };
3766
3767    let node_id = node_local_dataset.node_id;
3768    let mut node_graph = match node_local_dataset
3769        .backref
3770        .downcast_mut::<NodeGraphLocalDataset>()
3771    {
3772        Some(s) => s,
3773        None => return Update::DoNothing,
3774    };
3775
3776    let node_type = match node_graph
3777        .node_graph
3778        .nodes
3779        .iter()
3780        .find(|i| i.node_id == node_id)
3781    {
3782        Some(s) => s.node.node_type,
3783        None => return Update::DoNothing,
3784    };
3785
3786    // If a new file was selected, invoke callback
3787    let result = match node_graph.callbacks.on_node_field_edited.as_ref() {
3788        Some(OnNodeFieldEdited { refany, callback }) => (callback.cb)(
3789            refany.clone(),
3790            info,
3791            node_id,
3792            field_idx,
3793            node_type,
3794            NodeTypeFieldValue::FileInput(file.path.clone()),
3795        ),
3796        None => return Update::DoNothing,
3797    };
3798
3799    result
3800}