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