Skip to main content

azul_layout/widgets/
node_graph.rs

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