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

            
14
use alloc::vec::Vec;
15
use core::fmt;
16

            
17
use 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
};
28
use azul_css::{
29
    dynamic_selector::{CssPropertyWithConditions, CssPropertyWithConditionsVec},
30
    props::{
31
        basic::*,
32
        layout::*,
33
        property::{CssProperty, *},
34
        style::*,
35
    },
36
    *,
37
};
38
use azul_css::css::BoxOrStatic;
39

            
40
use 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)]
55
pub 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

            
67
impl 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

            
83
impl 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)]
101
pub struct NodeTypeIdInfoMap {
102
    pub node_type_id: NodeTypeId,
103
    pub node_type_info: NodeTypeInfo,
104
}
105

            
106
impl_option!(NodeTypeIdInfoMap, OptionNodeTypeIdInfoMap, copy = false, [Debug, Clone]);
107
impl_vec!(NodeTypeIdInfoMap, NodeTypeIdInfoMapVec, NodeTypeIdInfoMapVecDestructor, NodeTypeIdInfoMapVecDestructorType, NodeTypeIdInfoMapVecSlice, OptionNodeTypeIdInfoMap);
108
impl_vec_clone!(
109
    NodeTypeIdInfoMap,
110
    NodeTypeIdInfoMapVec,
111
    NodeTypeIdInfoMapVecDestructor
112
);
113
impl_vec_mut!(NodeTypeIdInfoMap, NodeTypeIdInfoMapVec);
114
impl_vec_debug!(NodeTypeIdInfoMap, NodeTypeIdInfoMapVec);
115

            
116
/// Maps an [`InputOutputTypeId`] to its [`InputOutputInfo`] metadata.
117
#[derive(Debug, Clone)]
118
#[repr(C)]
119
pub struct InputOutputTypeIdInfoMap {
120
    pub io_type_id: InputOutputTypeId,
121
    pub io_info: InputOutputInfo,
122
}
123

            
124
impl_option!(InputOutputTypeIdInfoMap, OptionInputOutputTypeIdInfoMap, copy = false, [Debug, Clone]);
125
impl_vec!(InputOutputTypeIdInfoMap, InputOutputTypeIdInfoMapVec, InputOutputTypeIdInfoMapVecDestructor, InputOutputTypeIdInfoMapVecDestructorType, InputOutputTypeIdInfoMapVecSlice, OptionInputOutputTypeIdInfoMap);
126
impl_vec_clone!(
127
    InputOutputTypeIdInfoMap,
128
    InputOutputTypeIdInfoMapVec,
129
    InputOutputTypeIdInfoMapVecDestructor
130
);
131
impl_vec_mut!(InputOutputTypeIdInfoMap, InputOutputTypeIdInfoMapVec);
132
impl_vec_debug!(InputOutputTypeIdInfoMap, InputOutputTypeIdInfoMapVec);
133

            
134
/// Maps a [`NodeGraphNodeId`] to its [`Node`] data.
135
#[derive(Debug, Clone)]
136
#[repr(C)]
137
pub struct NodeIdNodeMap {
138
    pub node_id: NodeGraphNodeId,
139
    pub node: Node,
140
}
141

            
142
impl_option!(NodeIdNodeMap, OptionNodeIdNodeMap, copy = false, [Debug, Clone]);
143
impl_vec!(NodeIdNodeMap, NodeIdNodeMapVec, NodeIdNodeMapVecDestructor, NodeIdNodeMapVecDestructorType, NodeIdNodeMapVecSlice, OptionNodeIdNodeMap);
144
impl_vec_clone!(NodeIdNodeMap, NodeIdNodeMapVec, NodeIdNodeMapVecDestructor);
145
impl_vec_mut!(NodeIdNodeMap, NodeIdNodeMapVec);
146
impl_vec_debug!(NodeIdNodeMap, NodeIdNodeMapVec);
147

            
148
#[derive(Debug, Copy, Clone)]
149
#[repr(C)]
150
pub 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)]
158
pub 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

            
169
pub 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;
176
impl_widget_callback!(
177
    OnNodeAdded,
178
    OptionOnNodeAdded,
179
    OnNodeAddedCallback,
180
    OnNodeAddedCallbackType
181
);
182

            
183
pub type OnNodeRemovedCallbackType =
184
    extern "C" fn(refany: RefAny, info: CallbackInfo, node_id_to_remove: NodeGraphNodeId) -> Update;
185
impl_widget_callback!(
186
    OnNodeRemoved,
187
    OptionOnNodeRemoved,
188
    OnNodeRemovedCallback,
189
    OnNodeRemovedCallbackType
190
);
191

            
192
pub type OnNodeGraphDraggedCallbackType =
193
    extern "C" fn(refany: RefAny, info: CallbackInfo, drag_amount: GraphDragAmount) -> Update;
194
impl_widget_callback!(
195
    OnNodeGraphDragged,
196
    OptionOnNodeGraphDragged,
197
    OnNodeGraphDraggedCallback,
198
    OnNodeGraphDraggedCallbackType
199
);
200

            
201
pub type OnNodeDraggedCallbackType = extern "C" fn(
202
    refany: RefAny,
203
    info: CallbackInfo,
204
    node_dragged: NodeGraphNodeId,
205
    drag_amount: NodeDragAmount,
206
) -> Update;
207
impl_widget_callback!(
208
    OnNodeDragged,
209
    OptionOnNodeDragged,
210
    OnNodeDraggedCallback,
211
    OnNodeDraggedCallbackType
212
);
213

            
214
pub 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;
222
impl_widget_callback!(
223
    OnNodeConnected,
224
    OptionOnNodeConnected,
225
    OnNodeConnectedCallback,
226
    OnNodeConnectedCallbackType
227
);
228

            
229
pub type OnNodeInputDisconnectedCallbackType = extern "C" fn(
230
    refany: RefAny,
231
    info: CallbackInfo,
232
    input: NodeGraphNodeId,
233
    input_index: usize,
234
) -> Update;
235
impl_widget_callback!(
236
    OnNodeInputDisconnected,
237
    OptionOnNodeInputDisconnected,
238
    OnNodeInputDisconnectedCallback,
239
    OnNodeInputDisconnectedCallbackType
240
);
241

            
242
pub type OnNodeOutputDisconnectedCallbackType = extern "C" fn(
243
    refany: RefAny,
244
    info: CallbackInfo,
245
    output: NodeGraphNodeId,
246
    output_index: usize,
247
) -> Update;
248
impl_widget_callback!(
249
    OnNodeOutputDisconnected,
250
    OptionOnNodeOutputDisconnected,
251
    OnNodeOutputDisconnectedCallback,
252
    OnNodeOutputDisconnectedCallbackType
253
);
254

            
255
pub 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;
263
impl_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)]
273
pub struct InputOutputTypeId {
274
    pub inner: u64,
275
}
276

            
277
impl_option!(InputOutputTypeId, OptionInputOutputTypeId, copy = false, [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]);
278
impl_vec!(InputOutputTypeId, InputOutputTypeIdVec, InputOutputTypeIdVecDestructor, InputOutputTypeIdVecDestructorType, InputOutputTypeIdVecSlice, OptionInputOutputTypeId);
279
impl_vec_clone!(
280
    InputOutputTypeId,
281
    InputOutputTypeIdVec,
282
    InputOutputTypeIdVecDestructor
283
);
284
impl_vec_mut!(InputOutputTypeId, InputOutputTypeIdVec);
285
impl_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)]
290
pub 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)]
297
pub 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)]
304
pub 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)]
315
pub struct NodeTypeField {
316
    pub key: AzString,
317
    pub value: NodeTypeFieldValue,
318
}
319

            
320
impl_option!(NodeTypeField, OptionNodeTypeField, copy = false, [Debug, Clone]);
321
impl_vec!(NodeTypeField, NodeTypeFieldVec, NodeTypeFieldVecDestructor, NodeTypeFieldVecDestructorType, NodeTypeFieldVecSlice, OptionNodeTypeField);
322
impl_vec_clone!(NodeTypeField, NodeTypeFieldVec, NodeTypeFieldVecDestructor);
323
impl_vec_debug!(NodeTypeField, NodeTypeFieldVec);
324
impl_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)]
329
pub 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)]
340
pub struct InputConnection {
341
    pub input_index: usize,
342
    pub connects_to: OutputNodeAndIndexVec,
343
}
344

            
345
impl_option!(InputConnection, OptionInputConnection, copy = false, [Debug, Clone]);
346
impl_vec!(InputConnection, InputConnectionVec, InputConnectionVecDestructor, InputConnectionVecDestructorType, InputConnectionVecSlice, OptionInputConnection);
347
impl_vec_clone!(
348
    InputConnection,
349
    InputConnectionVec,
350
    InputConnectionVecDestructor
351
);
352
impl_vec_debug!(InputConnection, InputConnectionVec);
353
impl_vec_mut!(InputConnection, InputConnectionVec);
354

            
355
/// Reference to a specific output port on a node.
356
#[derive(Debug, Clone)]
357
#[repr(C)]
358
pub struct OutputNodeAndIndex {
359
    pub node_id: NodeGraphNodeId,
360
    pub output_index: usize,
361
}
362

            
363
impl_option!(OutputNodeAndIndex, OptionOutputNodeAndIndex, copy = false, [Debug, Clone]);
364
impl_vec!(OutputNodeAndIndex, OutputNodeAndIndexVec, OutputNodeAndIndexVecDestructor, OutputNodeAndIndexVecDestructorType, OutputNodeAndIndexVecSlice, OptionOutputNodeAndIndex);
365
impl_vec_clone!(
366
    OutputNodeAndIndex,
367
    OutputNodeAndIndexVec,
368
    OutputNodeAndIndexVecDestructor
369
);
370
impl_vec_debug!(OutputNodeAndIndex, OutputNodeAndIndexVec);
371
impl_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)]
376
pub struct OutputConnection {
377
    pub output_index: usize,
378
    pub connects_to: InputNodeAndIndexVec,
379
}
380

            
381
impl_option!(OutputConnection, OptionOutputConnection, copy = false, [Debug, Clone]);
382
impl_vec!(OutputConnection, OutputConnectionVec, OutputConnectionVecDestructor, OutputConnectionVecDestructorType, OutputConnectionVecSlice, OptionOutputConnection);
383
impl_vec_clone!(
384
    OutputConnection,
385
    OutputConnectionVec,
386
    OutputConnectionVecDestructor
387
);
388
impl_vec_debug!(OutputConnection, OutputConnectionVec);
389
impl_vec_mut!(OutputConnection, OutputConnectionVec);
390

            
391
/// Reference to a specific input port on a node.
392
#[derive(Debug, Clone, PartialEq)]
393
#[repr(C)]
394
pub struct InputNodeAndIndex {
395
    pub node_id: NodeGraphNodeId,
396
    pub input_index: usize,
397
}
398

            
399
impl_option!(InputNodeAndIndex, OptionInputNodeAndIndex, copy = false, [Debug, Clone]);
400
impl_vec!(InputNodeAndIndex, InputNodeAndIndexVec, InputNodeAndIndexVecDestructor, InputNodeAndIndexVecDestructorType, InputNodeAndIndexVecSlice, OptionInputNodeAndIndex);
401
impl_vec_clone!(
402
    InputNodeAndIndex,
403
    InputNodeAndIndexVec,
404
    InputNodeAndIndexVecDestructor
405
);
406
impl_vec_debug!(InputNodeAndIndex, InputNodeAndIndexVec);
407
impl_vec_mut!(InputNodeAndIndex, InputNodeAndIndexVec);
408

            
409
/// Metadata describing a node type and its I/O port configuration.
410
#[derive(Debug, Clone)]
411
#[repr(C)]
412
pub 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)]
426
pub 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)]
437
pub 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)]
446
pub 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

            
458
impl 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)]
473
pub 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)]
481
pub struct NodeDragAmount {
482
    pub x: f32,
483
    pub y: f32,
484
}
485

            
486
impl 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
999
struct NodeGraphLocalDataset {
    node_graph: NodeGraph,
    last_input_or_output_clicked: Option<(NodeGraphNodeId, InputOrOutput)>,
    // Ref<NodeLocalDataSet> - used as a marker for getting the visual node ID
    active_node_being_dragged: Option<(NodeGraphNodeId, RefAny)>,
    node_connection_marker: RefAny, // Ref<NodeConnectionMarkerDataset>
    callbacks: NodeGraphCallbacks,
}
struct ContextMenuEntryLocalDataset {
    node_type: NodeTypeId,
    backref: RefAny, // RefAny<NodeGraphLocalDataset>
}
struct NodeConnectionMarkerDataset {}
struct NodeLocalDataset {
    node_id: NodeGraphNodeId,
    backref: RefAny, // RefAny<NodeGraphLocalDataset>
}
#[derive(Debug, Copy, Clone)]
enum InputOrOutput {
    Input(usize),
    Output(usize),
}
struct NodeInputOutputLocalDataset {
    io_id: InputOrOutput,
    backref: RefAny, // RefAny<NodeLocalDataset>
}
struct NodeFieldLocalDataset {
    field_idx: usize,
    backref: RefAny, // RefAny<NodeLocalDataset>
}
#[derive(Copy, Clone)]
struct ConnectionLocalDataset {
    out_node_id: NodeGraphNodeId,
    out_idx: usize,
    in_node_id: NodeGraphNodeId,
    in_idx: usize,
    swap_vert: bool,
    swap_horz: bool,
    color: ColorU,
}
fn render_node(
    node: &Node,
    graph_offset: (f32, f32),
    node_info: &NodeTypeInfo,
    mut node_local_dataset: NodeLocalDataset,
    scale_factor: f32,
) -> Dom {
    use azul_core::dom::{
        CssPropertyWithConditions, CssPropertyWithConditionsVec, Dom, DomVec, IdOrClass,
        IdOrClass::Class, IdOrClassVec,
    };
    use azul_css::*;
    const STRING_9416190750059025162: AzString = AzString::from_const_str("Material Icons");
    const STRING_16146701490593874959: AzString = AzString::from_const_str("system:ui");
    const STYLE_BACKGROUND_CONTENT_524016094839686509_ITEMS: &[StyleBackgroundContent] =
        &[StyleBackgroundContent::Color(ColorU {
            r: 34,
            g: 34,
            b: 34,
            a: 255,
        })];
    const STYLE_BACKGROUND_CONTENT_10430246856047584562_ITEMS: &[StyleBackgroundContent] =
        &[StyleBackgroundContent::LinearGradient(LinearGradient {
            direction: Direction::FromTo(DirectionCorners {
                dir_from: DirectionCorner::Left,
                dir_to: DirectionCorner::Right,
            }),
            extend_mode: ExtendMode::Clamp,
            stops: NormalizedLinearColorStopVec::from_const_slice(
                LINEAR_COLOR_STOP_4373556077110009258_ITEMS,
            ),
        })];
    const STYLE_BACKGROUND_CONTENT_11535310356736632656_ITEMS: &[StyleBackgroundContent] =
        &[StyleBackgroundContent::RadialGradient(RadialGradient {
            shape: Shape::Ellipse,
            extend_mode: ExtendMode::Clamp,
            position: StyleBackgroundPosition {
                horizontal: BackgroundPositionHorizontal::Left,
                vertical: BackgroundPositionVertical::Top,
            },
            size: RadialGradientSize::FarthestCorner,
            stops: NormalizedLinearColorStopVec::from_const_slice(
                LINEAR_COLOR_STOP_15596411095679453272_ITEMS,
            ),
        })];
    const STYLE_BACKGROUND_CONTENT_11936041127084538304_ITEMS: &[StyleBackgroundContent] =
        &[StyleBackgroundContent::LinearGradient(LinearGradient {
            direction: Direction::FromTo(DirectionCorners {
                dir_from: DirectionCorner::Right,
                dir_to: DirectionCorner::Left,
            }),
            extend_mode: ExtendMode::Clamp,
            stops: NormalizedLinearColorStopVec::from_const_slice(
                LINEAR_COLOR_STOP_4373556077110009258_ITEMS,
            ),
        })];
    const STYLE_BACKGROUND_CONTENT_15813232491335471489_ITEMS: &[StyleBackgroundContent] =
        &[StyleBackgroundContent::Color(ColorU {
            r: 0,
            g: 0,
            b: 0,
            a: 85,
        })];
    const STYLE_BACKGROUND_CONTENT_17648039690071193942_ITEMS: &[StyleBackgroundContent] =
        &[StyleBackgroundContent::LinearGradient(LinearGradient {
            direction: Direction::FromTo(DirectionCorners {
                dir_from: DirectionCorner::Top,
                dir_to: DirectionCorner::Bottom,
            }),
            extend_mode: ExtendMode::Clamp,
            stops: NormalizedLinearColorStopVec::from_const_slice(
                LINEAR_COLOR_STOP_7397113864565941600_ITEMS,
            ),
        })];
    const STYLE_TRANSFORM_347117342922946953_ITEMS: &[StyleTransform] =
        &[StyleTransform::Translate(StyleTransformTranslate2D {
            x: PixelValue::const_px(200),
            y: PixelValue::const_px(100),
        })];
    const STYLE_TRANSFORM_14683950870521466298_ITEMS: &[StyleTransform] =
        &[StyleTransform::Translate(StyleTransformTranslate2D {
            x: PixelValue::const_px(240),
            y: PixelValue::const_px(-10),
        })];
    const STYLE_FONT_FAMILY_8122988506401935406_ITEMS: &[StyleFontFamily] =
        &[StyleFontFamily::System(STRING_16146701490593874959)];
    const STYLE_FONT_FAMILY_11383897783350685780_ITEMS: &[StyleFontFamily] =
        &[StyleFontFamily::System(STRING_9416190750059025162)];
    const LINEAR_COLOR_STOP_4373556077110009258_ITEMS: &[NormalizedLinearColorStop] = &[
        NormalizedLinearColorStop {
            offset: PercentageValue::const_new(20),
            color: ColorOrSystem::color(ColorU {
                r: 0,
                g: 0,
                b: 0,
                a: 204,
            }),
        },
        NormalizedLinearColorStop {
            offset: PercentageValue::const_new(100),
            color: ColorOrSystem::color(ColorU {
                r: 0,
                g: 0,
                b: 0,
                a: 0,
            }),
        },
    ];
    const LINEAR_COLOR_STOP_7397113864565941600_ITEMS: &[NormalizedLinearColorStop] = &[
        NormalizedLinearColorStop {
            offset: PercentageValue::const_new(0),
            color: ColorOrSystem::color(ColorU {
                r: 229,
                g: 57,
                b: 53,
                a: 255,
            }),
        },
        NormalizedLinearColorStop {
            offset: PercentageValue::const_new(100),
            color: ColorOrSystem::color(ColorU {
                r: 227,
                g: 93,
                b: 91,
                a: 255,
            }),
        },
    ];
    const LINEAR_COLOR_STOP_15596411095679453272_ITEMS: &[NormalizedLinearColorStop] = &[
        NormalizedLinearColorStop {
            offset: PercentageValue::const_new(0),
            color: ColorOrSystem::color(ColorU {
                r: 47,
                g: 49,
                b: 54,
                a: 255,
            }),
        },
        NormalizedLinearColorStop {
            offset: PercentageValue::const_new(50),
            color: ColorOrSystem::color(ColorU {
                r: 47,
                g: 49,
                b: 54,
                a: 255,
            }),
        },
        NormalizedLinearColorStop {
            offset: PercentageValue::const_new(100),
            color: ColorOrSystem::color(ColorU {
                r: 32,
                g: 34,
                b: 37,
                a: 255,
            }),
        },
    ];
    const CSS_MATCH_10339190304804100510_PROPERTIES: &[CssPropertyWithConditions] = &[
        // .node_output_wrapper
        CssPropertyWithConditions::simple(CssProperty::Display(LayoutDisplayValue::Exact(
            LayoutDisplay::Flex,
        ))),
        CssPropertyWithConditions::simple(CssProperty::FlexDirection(
            LayoutFlexDirectionValue::Exact(LayoutFlexDirection::Column),
        )),
        CssPropertyWithConditions::simple(CssProperty::Left(LayoutLeftValue::Exact(LayoutLeft {
            inner: PixelValue::const_px(0),
        }))),
        CssPropertyWithConditions::simple(CssProperty::OverflowX(LayoutOverflowValue::Exact(
            LayoutOverflow::Visible,
        ))),
        CssPropertyWithConditions::simple(CssProperty::OverflowY(LayoutOverflowValue::Exact(
            LayoutOverflow::Visible,
        ))),
        CssPropertyWithConditions::simple(CssProperty::Position(LayoutPositionValue::Exact(
            LayoutPosition::Absolute,
        ))),
    ];
    const CSS_MATCH_10339190304804100510: CssPropertyWithConditionsVec =
        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_10339190304804100510_PROPERTIES);
    const CSS_MATCH_11452431279102104133_PROPERTIES: &[CssPropertyWithConditions] = &[
        // .node_input_connection_label
        CssPropertyWithConditions::simple(CssProperty::FontFamily(StyleFontFamilyVecValue::Exact(
            StyleFontFamilyVec::from_const_slice(STYLE_FONT_FAMILY_8122988506401935406_ITEMS),
        ))),
        CssPropertyWithConditions::simple(CssProperty::FontSize(StyleFontSizeValue::Exact(
            StyleFontSize {
                inner: PixelValue::const_px(12),
            },
        ))),
        CssPropertyWithConditions::simple(CssProperty::Height(LayoutHeightValue::Exact(
            LayoutHeight::Px(PixelValue::const_px(15)),
        ))),
        CssPropertyWithConditions::simple(CssProperty::TextAlign(StyleTextAlignValue::Exact(
            StyleTextAlign::Right,
        ))),
        CssPropertyWithConditions::simple(CssProperty::Width(LayoutWidthValue::Exact(
            LayoutWidth::Px(PixelValue::const_px(100)),
        ))),
    ];
    const CSS_MATCH_11452431279102104133: CssPropertyWithConditionsVec =
        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_11452431279102104133_PROPERTIES);
    const CSS_MATCH_1173826950760010563_PROPERTIES: &[CssPropertyWithConditions] = &[
        // .node_configuration_field_value:focus
        CssPropertyWithConditions::simple(CssProperty::BorderTopColor(
            StyleBorderTopColorValue::Exact(StyleBorderTopColor {
                inner: ColorU {
                    r: 0,
                    g: 131,
                    b: 176,
                    a: 119,
                },
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderRightColor(
            StyleBorderRightColorValue::Exact(StyleBorderRightColor {
                inner: ColorU {
                    r: 0,
                    g: 131,
                    b: 176,
                    a: 119,
                },
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderLeftColor(
            StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
                inner: ColorU {
                    r: 0,
                    g: 131,
                    b: 176,
                    a: 119,
                },
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderBottomColor(
            StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
                inner: ColorU {
                    r: 0,
                    g: 131,
                    b: 176,
                    a: 119,
                },
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderTopStyle(
            StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
                inner: BorderStyle::Solid,
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderRightStyle(
            StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
                inner: BorderStyle::Solid,
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderLeftStyle(
            StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
                inner: BorderStyle::Solid,
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderBottomStyle(
            StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
                inner: BorderStyle::Solid,
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderTopWidth(
            LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
                inner: PixelValue::const_px(1),
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderRightWidth(
            LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
                inner: PixelValue::const_px(1),
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderLeftWidth(
            LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
                inner: PixelValue::const_px(1),
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderBottomWidth(
            LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
                inner: PixelValue::const_px(1),
            }),
        )),
        // .node_configuration_field_value
        CssPropertyWithConditions::simple(CssProperty::AlignItems(LayoutAlignItemsValue::Exact(
            LayoutAlignItems::Center,
        ))),
        CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
            StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
                STYLE_BACKGROUND_CONTENT_524016094839686509_ITEMS,
            )),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderTopColor(
            StyleBorderTopColorValue::Exact(StyleBorderTopColor {
                inner: ColorU {
                    r: 54,
                    g: 57,
                    b: 63,
                    a: 255,
                },
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderRightColor(
            StyleBorderRightColorValue::Exact(StyleBorderRightColor {
                inner: ColorU {
                    r: 54,
                    g: 57,
                    b: 63,
                    a: 255,
                },
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderLeftColor(
            StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
                inner: ColorU {
                    r: 54,
                    g: 57,
                    b: 63,
                    a: 255,
                },
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderBottomColor(
            StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
                inner: ColorU {
                    r: 54,
                    g: 57,
                    b: 63,
                    a: 255,
                },
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderTopStyle(
            StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
                inner: BorderStyle::Solid,
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderRightStyle(
            StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
                inner: BorderStyle::Solid,
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderLeftStyle(
            StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
                inner: BorderStyle::Solid,
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderBottomStyle(
            StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
                inner: BorderStyle::Solid,
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderTopWidth(
            LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
                inner: PixelValue::const_px(1),
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderRightWidth(
            LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
                inner: PixelValue::const_px(1),
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderLeftWidth(
            LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
                inner: PixelValue::const_px(1),
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderBottomWidth(
            LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
                inner: PixelValue::const_px(1),
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::FlexGrow(LayoutFlexGrowValue::Exact(
            LayoutFlexGrow {
                inner: FloatValue::const_new(1),
            },
        ))),
        CssPropertyWithConditions::simple(CssProperty::TextAlign(StyleTextAlignValue::Exact(
            StyleTextAlign::Left,
        ))),
    ];
    const CSS_MATCH_1173826950760010563: CssPropertyWithConditionsVec =
        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_1173826950760010563_PROPERTIES);
    const CSS_MATCH_1198521124955124418_PROPERTIES: &[CssPropertyWithConditions] = &[
        // .node_configuration_field_label
        CssPropertyWithConditions::simple(CssProperty::AlignItems(LayoutAlignItemsValue::Exact(
            LayoutAlignItems::Center,
        ))),
        CssPropertyWithConditions::simple(CssProperty::FlexGrow(LayoutFlexGrowValue::Exact(
            LayoutFlexGrow {
                inner: FloatValue::const_new(1),
            },
        ))),
        CssPropertyWithConditions::simple(CssProperty::MaxWidth(LayoutMaxWidthValue::Exact(
            LayoutMaxWidth {
                inner: PixelValue::const_px(120),
            },
        ))),
        CssPropertyWithConditions::simple(CssProperty::PaddingLeft(LayoutPaddingLeftValue::Exact(
            LayoutPaddingLeft {
                inner: PixelValue::const_px(10),
            },
        ))),
        CssPropertyWithConditions::simple(CssProperty::TextAlign(StyleTextAlignValue::Exact(
            StyleTextAlign::Left,
        ))),
    ];
    const CSS_MATCH_1198521124955124418: CssPropertyWithConditionsVec =
        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_1198521124955124418_PROPERTIES);
    const CSS_MATCH_12038890904436132038_PROPERTIES: &[CssPropertyWithConditions] = &[
        // .node_output_connection_label_wrapper
        CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
            StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
                STYLE_BACKGROUND_CONTENT_10430246856047584562_ITEMS,
            )),
        )),
        CssPropertyWithConditions::simple(CssProperty::PaddingLeft(LayoutPaddingLeftValue::Exact(
            LayoutPaddingLeft {
                inner: PixelValue::const_px(5),
            },
        ))),
    ];
    const CSS_MATCH_12038890904436132038: CssPropertyWithConditionsVec =
        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_12038890904436132038_PROPERTIES);
    const CSS_MATCH_12400244273289328300_PROPERTIES: &[CssPropertyWithConditions] = &[
        // .node_output_container
        CssPropertyWithConditions::simple(CssProperty::Display(LayoutDisplayValue::Exact(
            LayoutDisplay::Flex,
        ))),
        CssPropertyWithConditions::simple(CssProperty::FlexDirection(
            LayoutFlexDirectionValue::Exact(LayoutFlexDirection::Row),
        )),
        CssPropertyWithConditions::simple(CssProperty::MarginTop(LayoutMarginTopValue::Exact(
            LayoutMarginTop {
                inner: PixelValue::const_px(10),
            },
        ))),
    ];
    const CSS_MATCH_12400244273289328300: CssPropertyWithConditionsVec =
        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_12400244273289328300_PROPERTIES);
    const CSS_MATCH_14906563417280941890_PROPERTIES: &[CssPropertyWithConditions] = &[
        // .outputs
        CssPropertyWithConditions::simple(CssProperty::FlexGrow(LayoutFlexGrowValue::Exact(
            LayoutFlexGrow {
                inner: FloatValue::const_new(0),
            },
        ))),
        CssPropertyWithConditions::simple(CssProperty::OverflowX(LayoutOverflowValue::Exact(
            LayoutOverflow::Visible,
        ))),
        CssPropertyWithConditions::simple(CssProperty::OverflowY(LayoutOverflowValue::Exact(
            LayoutOverflow::Visible,
        ))),
        CssPropertyWithConditions::simple(CssProperty::Position(LayoutPositionValue::Exact(
            LayoutPosition::Relative,
        ))),
        CssPropertyWithConditions::simple(CssProperty::Width(LayoutWidthValue::Exact(
            LayoutWidth::Px(PixelValue::const_px(0)),
        ))),
    ];
    const CSS_MATCH_14906563417280941890: CssPropertyWithConditionsVec =
        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_14906563417280941890_PROPERTIES);
    const CSS_MATCH_16946967739775705757_PROPERTIES: &[CssPropertyWithConditions] = &[
        // .inputs
        CssPropertyWithConditions::simple(CssProperty::FlexGrow(LayoutFlexGrowValue::Exact(
            LayoutFlexGrow {
                inner: FloatValue::const_new(0),
            },
        ))),
        CssPropertyWithConditions::simple(CssProperty::OverflowX(LayoutOverflowValue::Exact(
            LayoutOverflow::Visible,
        ))),
        CssPropertyWithConditions::simple(CssProperty::OverflowY(LayoutOverflowValue::Exact(
            LayoutOverflow::Visible,
        ))),
        CssPropertyWithConditions::simple(CssProperty::Position(LayoutPositionValue::Exact(
            LayoutPosition::Relative,
        ))),
        CssPropertyWithConditions::simple(CssProperty::Width(LayoutWidthValue::Exact(
            LayoutWidth::Px(PixelValue::const_px(0)),
        ))),
    ];
    const CSS_MATCH_16946967739775705757: CssPropertyWithConditionsVec =
        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_16946967739775705757_PROPERTIES);
    const CSS_MATCH_1739273067404038547_PROPERTIES: &[CssPropertyWithConditions] = &[
        // .node_label
        CssPropertyWithConditions::simple(CssProperty::FontSize(StyleFontSizeValue::Exact(
            StyleFontSize {
                inner: PixelValue::const_px(18),
            },
        ))),
        CssPropertyWithConditions::simple(CssProperty::Height(LayoutHeightValue::Exact(
            LayoutHeight::Px(PixelValue::const_px(50)),
        ))),
        CssPropertyWithConditions::simple(CssProperty::PaddingLeft(LayoutPaddingLeftValue::Exact(
            LayoutPaddingLeft {
                inner: PixelValue::const_px(5),
            },
        ))),
        CssPropertyWithConditions::simple(CssProperty::PaddingTop(LayoutPaddingTopValue::Exact(
            LayoutPaddingTop {
                inner: PixelValue::const_px(10),
            },
        ))),
    ];
    const CSS_MATCH_1739273067404038547: CssPropertyWithConditionsVec =
        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_1739273067404038547_PROPERTIES);
    const CSS_MATCH_2008162367868363199_PROPERTIES: &[CssPropertyWithConditions] = &[
        // .node_output_connection_label
        CssPropertyWithConditions::simple(CssProperty::FontFamily(StyleFontFamilyVecValue::Exact(
            StyleFontFamilyVec::from_const_slice(STYLE_FONT_FAMILY_8122988506401935406_ITEMS),
        ))),
        CssPropertyWithConditions::simple(CssProperty::FontSize(StyleFontSizeValue::Exact(
            StyleFontSize {
                inner: PixelValue::const_px(12),
            },
        ))),
        CssPropertyWithConditions::simple(CssProperty::Height(LayoutHeightValue::Exact(
            LayoutHeight::Px(PixelValue::const_px(15)),
        ))),
        CssPropertyWithConditions::simple(CssProperty::TextAlign(StyleTextAlignValue::Exact(
            StyleTextAlign::Left,
        ))),
        CssPropertyWithConditions::simple(CssProperty::Width(LayoutWidthValue::Exact(
            LayoutWidth::Px(PixelValue::const_px(100)),
        ))),
    ];
    const CSS_MATCH_2008162367868363199: CssPropertyWithConditionsVec =
        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_2008162367868363199_PROPERTIES);
    const CSS_MATCH_2639191696846875011_PROPERTIES: &[CssPropertyWithConditions] = &[
        // .node_configuration_field_container
        CssPropertyWithConditions::simple(CssProperty::FlexDirection(
            LayoutFlexDirectionValue::Exact(LayoutFlexDirection::Column),
        )),
        CssPropertyWithConditions::simple(CssProperty::PaddingTop(LayoutPaddingTopValue::Exact(
            LayoutPaddingTop {
                inner: PixelValue::const_px(3),
            },
        ))),
        CssPropertyWithConditions::simple(CssProperty::PaddingBottom(
            LayoutPaddingBottomValue::Exact(LayoutPaddingBottom {
                inner: PixelValue::const_px(3),
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::PaddingLeft(LayoutPaddingLeftValue::Exact(
            LayoutPaddingLeft {
                inner: PixelValue::const_px(5),
            },
        ))),
        CssPropertyWithConditions::simple(CssProperty::PaddingRight(
            LayoutPaddingRightValue::Exact(LayoutPaddingRight {
                inner: PixelValue::const_px(5),
            }),
        )),
    ];
    const CSS_MATCH_2639191696846875011: CssPropertyWithConditionsVec =
        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_2639191696846875011_PROPERTIES);
    const CSS_MATCH_3354247437065914166_PROPERTIES: &[CssPropertyWithConditions] = &[
        // .node_body
        CssPropertyWithConditions::simple(CssProperty::FlexDirection(
            LayoutFlexDirectionValue::Exact(LayoutFlexDirection::Row),
        )),
        CssPropertyWithConditions::simple(CssProperty::Position(LayoutPositionValue::Exact(
            LayoutPosition::Relative,
        ))),
    ];
    const CSS_MATCH_3354247437065914166: CssPropertyWithConditionsVec =
        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_3354247437065914166_PROPERTIES);
    const CSS_MATCH_4700400755767504372_PROPERTIES: &[CssPropertyWithConditions] = &[
        // .node_input_connection_label_wrapper
        CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
            StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
                STYLE_BACKGROUND_CONTENT_11936041127084538304_ITEMS,
            )),
        )),
        CssPropertyWithConditions::simple(CssProperty::PaddingRight(
            LayoutPaddingRightValue::Exact(LayoutPaddingRight {
                inner: PixelValue::const_px(5),
            }),
        )),
    ];
    const CSS_MATCH_4700400755767504372: CssPropertyWithConditionsVec =
        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_4700400755767504372_PROPERTIES);
    const CSS_MATCH_705881630351954657_PROPERTIES: &[CssPropertyWithConditions] = &[
        // .node_input_wrapper
        CssPropertyWithConditions::simple(CssProperty::Display(LayoutDisplayValue::Exact(
            LayoutDisplay::Flex,
        ))),
        CssPropertyWithConditions::simple(CssProperty::FlexDirection(
            LayoutFlexDirectionValue::Exact(LayoutFlexDirection::Column),
        )),
        CssPropertyWithConditions::simple(CssProperty::OverflowX(LayoutOverflowValue::Exact(
            LayoutOverflow::Visible,
        ))),
        CssPropertyWithConditions::simple(CssProperty::OverflowY(LayoutOverflowValue::Exact(
            LayoutOverflow::Visible,
        ))),
        CssPropertyWithConditions::simple(CssProperty::Position(LayoutPositionValue::Exact(
            LayoutPosition::Absolute,
        ))),
        CssPropertyWithConditions::simple(CssProperty::Right(LayoutRightValue::Exact(
            LayoutRight {
                inner: PixelValue::const_px(0),
            },
        ))),
    ];
    const CSS_MATCH_705881630351954657: CssPropertyWithConditionsVec =
        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_705881630351954657_PROPERTIES);
    const CSS_MATCH_7395766480280098891_PROPERTIES: &[CssPropertyWithConditions] = &[
        // .node_close_button
        CssPropertyWithConditions::simple(CssProperty::AlignItems(LayoutAlignItemsValue::Exact(
            LayoutAlignItems::Center,
        ))),
        CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
            StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
                STYLE_BACKGROUND_CONTENT_17648039690071193942_ITEMS,
            )),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderTopColor(
            StyleBorderTopColorValue::Exact(StyleBorderTopColor {
                inner: ColorU {
                    r: 255,
                    g: 255,
                    b: 255,
                    a: 153,
                },
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderRightColor(
            StyleBorderRightColorValue::Exact(StyleBorderRightColor {
                inner: ColorU {
                    r: 255,
                    g: 255,
                    b: 255,
                    a: 153,
                },
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderLeftColor(
            StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
                inner: ColorU {
                    r: 255,
                    g: 255,
                    b: 255,
                    a: 153,
                },
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderBottomColor(
            StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
                inner: ColorU {
                    r: 255,
                    g: 255,
                    b: 255,
                    a: 153,
                },
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderTopStyle(
            StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
                inner: BorderStyle::Solid,
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderRightStyle(
            StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
                inner: BorderStyle::Solid,
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderLeftStyle(
            StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
                inner: BorderStyle::Solid,
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderBottomStyle(
            StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
                inner: BorderStyle::Solid,
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderTopWidth(
            LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
                inner: PixelValue::const_px(1),
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderRightWidth(
            LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
                inner: PixelValue::const_px(1),
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderLeftWidth(
            LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
                inner: PixelValue::const_px(1),
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BorderBottomWidth(
            LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
                inner: PixelValue::const_px(1),
            }),
        )),
        CssPropertyWithConditions::simple(CssProperty::BoxShadowLeft(StyleBoxShadowValue::Exact(BoxOrStatic::Static(&
            StyleBoxShadow {
                offset_x: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                offset_y: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                color: ColorU {
                    r: 229,
                    g: 57,
                    b: 53,
                    a: 255,
                },
                blur_radius: PixelValueNoPercent {
                    inner: PixelValue::const_px(2),
                },
                spread_radius: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                clip_mode: BoxShadowClipMode::Outset,
            },
        )))),
        CssPropertyWithConditions::simple(CssProperty::BoxShadowRight(StyleBoxShadowValue::Exact(BoxOrStatic::Static(&
            StyleBoxShadow {
                offset_x: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                offset_y: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                color: ColorU {
                    r: 229,
                    g: 57,
                    b: 53,
                    a: 255,
                },
                blur_radius: PixelValueNoPercent {
                    inner: PixelValue::const_px(2),
                },
                spread_radius: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                clip_mode: BoxShadowClipMode::Outset,
            },
        )))),
        CssPropertyWithConditions::simple(CssProperty::BoxShadowTop(StyleBoxShadowValue::Exact(BoxOrStatic::Static(&
            StyleBoxShadow {
                offset_x: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                offset_y: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                color: ColorU {
                    r: 229,
                    g: 57,
                    b: 53,
                    a: 255,
                },
                blur_radius: PixelValueNoPercent {
                    inner: PixelValue::const_px(2),
                },
                spread_radius: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                clip_mode: BoxShadowClipMode::Outset,
            },
        )))),
        CssPropertyWithConditions::simple(CssProperty::BoxShadowBottom(
            StyleBoxShadowValue::Exact(BoxOrStatic::Static(&StyleBoxShadow {
                offset_x: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                offset_y: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                color: ColorU {
                    r: 229,
                    g: 57,
                    b: 53,
                    a: 255,
                },
                blur_radius: PixelValueNoPercent {
                    inner: PixelValue::const_px(2),
                },
                spread_radius: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                clip_mode: BoxShadowClipMode::Outset,
            })),
        )),
        CssPropertyWithConditions::simple(CssProperty::Cursor(StyleCursorValue::Exact(
            StyleCursor::Pointer,
        ))),
        CssPropertyWithConditions::simple(CssProperty::FontFamily(StyleFontFamilyVecValue::Exact(
            StyleFontFamilyVec::from_const_slice(STYLE_FONT_FAMILY_11383897783350685780_ITEMS),
        ))),
        CssPropertyWithConditions::simple(CssProperty::Height(LayoutHeightValue::Exact(
            LayoutHeight::Px(PixelValue::const_px(20)),
        ))),
        CssPropertyWithConditions::simple(CssProperty::Position(LayoutPositionValue::Exact(
            LayoutPosition::Absolute,
        ))),
        CssPropertyWithConditions::simple(CssProperty::TextAlign(StyleTextAlignValue::Exact(
            StyleTextAlign::Center,
        ))),
        CssPropertyWithConditions::simple(CssProperty::Transform(StyleTransformVecValue::Exact(
            StyleTransformVec::from_const_slice(STYLE_TRANSFORM_14683950870521466298_ITEMS),
        ))),
        CssPropertyWithConditions::simple(CssProperty::Width(LayoutWidthValue::Exact(
            LayoutWidth::Px(PixelValue::const_px(20)),
        ))),
    ];
    const CSS_MATCH_7395766480280098891: CssPropertyWithConditionsVec =
        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_7395766480280098891_PROPERTIES);
    const CSS_MATCH_7432473243011547380_PROPERTIES: &[CssPropertyWithConditions] = &[
        // .node_content_wrapper
        CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
            StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
                STYLE_BACKGROUND_CONTENT_15813232491335471489_ITEMS,
            )),
        )),
        CssPropertyWithConditions::simple(CssProperty::BoxShadowLeft(StyleBoxShadowValue::Exact(BoxOrStatic::Static(&
            StyleBoxShadow {
                offset_x: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                offset_y: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                color: ColorU {
                    r: 0,
                    g: 0,
                    b: 0,
                    a: 255,
                },
                blur_radius: PixelValueNoPercent {
                    inner: PixelValue::const_px(4),
                },
                spread_radius: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                clip_mode: BoxShadowClipMode::Inset,
            },
        )))),
        CssPropertyWithConditions::simple(CssProperty::BoxShadowRight(StyleBoxShadowValue::Exact(BoxOrStatic::Static(&
            StyleBoxShadow {
                offset_x: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                offset_y: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                color: ColorU {
                    r: 0,
                    g: 0,
                    b: 0,
                    a: 255,
                },
                blur_radius: PixelValueNoPercent {
                    inner: PixelValue::const_px(4),
                },
                spread_radius: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                clip_mode: BoxShadowClipMode::Inset,
            },
        )))),
        CssPropertyWithConditions::simple(CssProperty::BoxShadowTop(StyleBoxShadowValue::Exact(BoxOrStatic::Static(&
            StyleBoxShadow {
                offset_x: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                offset_y: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                color: ColorU {
                    r: 0,
                    g: 0,
                    b: 0,
                    a: 255,
                },
                blur_radius: PixelValueNoPercent {
                    inner: PixelValue::const_px(4),
                },
                spread_radius: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                clip_mode: BoxShadowClipMode::Inset,
            },
        )))),
        CssPropertyWithConditions::simple(CssProperty::BoxShadowBottom(
            StyleBoxShadowValue::Exact(BoxOrStatic::Static(&StyleBoxShadow {
                offset_x: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                offset_y: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                color: ColorU {
                    r: 0,
                    g: 0,
                    b: 0,
                    a: 255,
                },
                blur_radius: PixelValueNoPercent {
                    inner: PixelValue::const_px(4),
                },
                spread_radius: PixelValueNoPercent {
                    inner: PixelValue::const_px(0),
                },
                clip_mode: BoxShadowClipMode::Inset,
            })),
        )),
        CssPropertyWithConditions::simple(CssProperty::FlexGrow(LayoutFlexGrowValue::Exact(
            LayoutFlexGrow {
                inner: FloatValue::const_new(1),
            },
        ))),
    ];
    const CSS_MATCH_7432473243011547380: CssPropertyWithConditionsVec =
        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_7432473243011547380_PROPERTIES);
    const CSS_MATCH_9863994880298313101_PROPERTIES: &[CssPropertyWithConditions] = &[
        // .node_input_container
        CssPropertyWithConditions::simple(CssProperty::Display(LayoutDisplayValue::Exact(
            LayoutDisplay::Flex,
        ))),
        CssPropertyWithConditions::simple(CssProperty::FlexDirection(
            LayoutFlexDirectionValue::Exact(LayoutFlexDirection::Row),
        )),
        CssPropertyWithConditions::simple(CssProperty::MarginTop(LayoutMarginTopValue::Exact(
            LayoutMarginTop {
                inner: PixelValue::const_px(10),
            },
        ))),
    ];
    const CSS_MATCH_9863994880298313101: CssPropertyWithConditionsVec =
        CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_9863994880298313101_PROPERTIES);
    // NODE RENDER FUNCTION BEGIN
    let node_transform = StyleTransformTranslate2D {
        x: PixelValue::px(graph_offset.0 + node.position.x),
        y: PixelValue::px(graph_offset.1 + node.position.y),
    };
    // get names and colors for inputs / outputs
    let inputs = node_info
        .inputs
        .iter()
        .filter_map(|io_id| {
            let node_graph_ref = node_local_dataset
                .backref
                .downcast_ref::<NodeGraphLocalDataset>()?;
            let io_info = node_graph_ref
                .node_graph
                .input_output_types
                .iter()
                .find(|i| i.io_type_id == *io_id)?;
            Some((
                io_info.io_info.data_type.clone(),
                io_info.io_info.color.clone(),
            ))
        })
        .collect::<Vec<_>>();
    let outputs = node_info
        .outputs
        .iter()
        .filter_map(|io_id| {
            let node_graph_ref = node_local_dataset
                .backref
                .downcast_ref::<NodeGraphLocalDataset>()?;
            let io_info = node_graph_ref
                .node_graph
                .input_output_types
                .iter()
                .find(|i| i.io_type_id == *io_id)?;
            Some((
                io_info.io_info.data_type.clone(),
                io_info.io_info.color.clone(),
            ))
        })
        .collect::<Vec<_>>();
    let node_local_dataset = RefAny::new(node_local_dataset);
    Dom::create_div()
    .with_css_props(vec![
        CssPropertyWithConditions::simple(CssProperty::Position(LayoutPositionValue::Exact(
            LayoutPosition::Absolute,
        ))),
    ].into())
    .with_children(vec![
        Dom::create_div()
        .with_callbacks(vec![
           CoreCallbackData {
               event: EventFilter::Hover(HoverEventFilter::LeftMouseDown),
               refany: node_local_dataset.clone(),
               callback: CoreCallback { cb: nodegraph_set_active_node as usize, ctx: OptionRefAny::None },
           },
        ].into())
        .with_css_props(vec![
           // .node_graph_node
           CssPropertyWithConditions::simple(CssProperty::OverflowX(
               LayoutOverflowValue::Exact(LayoutOverflow::Visible)
           )),
           CssPropertyWithConditions::simple(CssProperty::Position(LayoutPositionValue::Exact(
               LayoutPosition::Relative,
           ))),
           CssPropertyWithConditions::simple(CssProperty::OverflowY(
               LayoutOverflowValue::Exact(LayoutOverflow::Visible)
           )),
           CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
               StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
                   STYLE_BACKGROUND_CONTENT_11535310356736632656_ITEMS,
               )),
           )),
           CssPropertyWithConditions::simple(CssProperty::BorderTopColor(
               StyleBorderTopColorValue::Exact(StyleBorderTopColor {
                   inner: ColorU {
                       r: 0,
                       g: 180,
                       b: 219,
                       a: 255,
                   },
               }),
           )),
           CssPropertyWithConditions::simple(CssProperty::BorderRightColor(
               StyleBorderRightColorValue::Exact(StyleBorderRightColor {
                   inner: ColorU {
                       r: 0,
                       g: 180,
                       b: 219,
                       a: 255,
                   },
               }),
           )),
           CssPropertyWithConditions::simple(CssProperty::BorderLeftColor(
               StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
                   inner: ColorU {
                       r: 0,
                       g: 180,
                       b: 219,
                       a: 255,
                   },
               }),
           )),
           CssPropertyWithConditions::simple(CssProperty::BorderBottomColor(
               StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
                   inner: ColorU {
                       r: 0,
                       g: 180,
                       b: 219,
                       a: 255,
                   },
               }),
           )),
           CssPropertyWithConditions::simple(CssProperty::BorderTopStyle(
               StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
                   inner: BorderStyle::Solid,
               }),
           )),
           CssPropertyWithConditions::simple(CssProperty::BorderRightStyle(
               StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
                   inner: BorderStyle::Solid,
               }),
           )),
           CssPropertyWithConditions::simple(CssProperty::BorderLeftStyle(
               StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
                   inner: BorderStyle::Solid,
               }),
           )),
           CssPropertyWithConditions::simple(CssProperty::BorderBottomStyle(
               StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
                   inner: BorderStyle::Solid,
               }),
           )),
           CssPropertyWithConditions::simple(CssProperty::BorderTopWidth(
               LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
                   inner: PixelValue::const_px(1),
               }),
           )),
           CssPropertyWithConditions::simple(CssProperty::BorderRightWidth(
               LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
                   inner: PixelValue::const_px(1),
               }),
           )),
           CssPropertyWithConditions::simple(CssProperty::BorderLeftWidth(
               LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
                   inner: PixelValue::const_px(1),
               }),
           )),
           CssPropertyWithConditions::simple(CssProperty::BorderBottomWidth(
               LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
                   inner: PixelValue::const_px(1),
               }),
           )),
           CssPropertyWithConditions::simple(CssProperty::BoxShadowLeft(StyleBoxShadowValue::Exact(BoxOrStatic::heap(
               StyleBoxShadow {
                   offset_x: PixelValueNoPercent { inner: PixelValue::const_px(0) }, offset_y: PixelValueNoPercent { inner: PixelValue::const_px(0) },
                   color: ColorU {
                       r: 0,
                       g: 131,
                       b: 176,
                       a: 119,
                   },
                   blur_radius: PixelValueNoPercent {
                       inner: PixelValue::const_px(3),
                   },
                   spread_radius: PixelValueNoPercent {
                       inner: PixelValue::const_px(0),
                   },
                   clip_mode: BoxShadowClipMode::Outset,
               },
           )))),
           CssPropertyWithConditions::simple(CssProperty::BoxShadowRight(StyleBoxShadowValue::Exact(BoxOrStatic::heap(
               StyleBoxShadow {
                   offset_x: PixelValueNoPercent { inner: PixelValue::const_px(0) }, offset_y: PixelValueNoPercent { inner: PixelValue::const_px(0) },
                   color: ColorU {
                       r: 0,
                       g: 131,
                       b: 176,
                       a: 119,
                   },
                   blur_radius: PixelValueNoPercent {
                       inner: PixelValue::const_px(3),
                   },
                   spread_radius: PixelValueNoPercent {
                       inner: PixelValue::const_px(0),
                   },
                   clip_mode: BoxShadowClipMode::Outset,
               },
           )))),
           CssPropertyWithConditions::simple(CssProperty::BoxShadowTop(StyleBoxShadowValue::Exact(BoxOrStatic::heap(
               StyleBoxShadow {
                   offset_x: PixelValueNoPercent { inner: PixelValue::const_px(0) }, offset_y: PixelValueNoPercent { inner: PixelValue::const_px(0) },
                   color: ColorU {
                       r: 0,
                       g: 131,
                       b: 176,
                       a: 119,
                   },
                   blur_radius: PixelValueNoPercent {
                       inner: PixelValue::const_px(3),
                   },
                   spread_radius: PixelValueNoPercent {
                       inner: PixelValue::const_px(0),
                   },
                   clip_mode: BoxShadowClipMode::Outset,
               },
           )))),
           CssPropertyWithConditions::simple(CssProperty::BoxShadowBottom(
               StyleBoxShadowValue::Exact(BoxOrStatic::heap(StyleBoxShadow {
                   offset_x: PixelValueNoPercent { inner: PixelValue::const_px(0) }, offset_y: PixelValueNoPercent { inner: PixelValue::const_px(0) },
                   color: ColorU {
                       r: 0,
                       g: 131,
                       b: 176,
                       a: 119,
                   },
                   blur_radius: PixelValueNoPercent {
                       inner: PixelValue::const_px(3),
                   },
                   spread_radius: PixelValueNoPercent {
                       inner: PixelValue::const_px(0),
                   },
                   clip_mode: BoxShadowClipMode::Outset,
               })),
           )),
           CssPropertyWithConditions::simple(CssProperty::TextColor(StyleTextColorValue::Exact(
               StyleTextColor {
                   inner: ColorU {
                       r: 255,
                       g: 255,
                       b: 255,
                       a: 255,
                   },
               },
           ))),
           CssPropertyWithConditions::simple(CssProperty::Display(LayoutDisplayValue::Exact(
               LayoutDisplay::Block
           ))),
           CssPropertyWithConditions::simple(CssProperty::FontFamily(StyleFontFamilyVecValue::Exact(
               StyleFontFamilyVec::from_const_slice(STYLE_FONT_FAMILY_8122988506401935406_ITEMS),
           ))),
           CssPropertyWithConditions::simple(CssProperty::PaddingTop(LayoutPaddingTopValue::Exact(
               LayoutPaddingTop {
                   inner: PixelValue::const_px(10),
               },
           ))),
           CssPropertyWithConditions::simple(CssProperty::PaddingBottom(
               LayoutPaddingBottomValue::Exact(LayoutPaddingBottom {
                   inner: PixelValue::const_px(10),
               }),
           )),
           CssPropertyWithConditions::simple(CssProperty::PaddingLeft(LayoutPaddingLeftValue::Exact(
               LayoutPaddingLeft {
                   inner: PixelValue::const_px(10),
               },
           ))),
           CssPropertyWithConditions::simple(CssProperty::PaddingRight(
               LayoutPaddingRightValue::Exact(LayoutPaddingRight {
                   inner: PixelValue::const_px(10),
               }),
           )),
           CssPropertyWithConditions::simple(CssProperty::Transform(StyleTransformVecValue::Exact(
               if scale_factor != 1.0 {
                    vec![
                         StyleTransform::Translate(node_transform),
                         StyleTransform::ScaleX(PercentageValue::new(scale_factor * 100.0)),
                         StyleTransform::ScaleY(PercentageValue::new(scale_factor * 100.0)),
                    ]
               } else {
                    vec![
                         StyleTransform::Translate(node_transform)
                    ]
               }.into()
           ))),
           CssPropertyWithConditions::simple(CssProperty::Width(LayoutWidthValue::Exact(
               LayoutWidth::Px(PixelValue::const_px(250),),
           ))),
        ].into())
        .with_ids_and_classes({
           const IDS_AND_CLASSES_4480169002427296613: &[IdOrClass] =
               &[Class(AzString::from_const_str("node_graph_node"))];
           IdOrClassVec::from_const_slice(IDS_AND_CLASSES_4480169002427296613)
        })
        .with_children(DomVec::from_vec(vec![
           Dom::create_text(AzString::from_const_str("X"))
               .with_css_props(CSS_MATCH_7395766480280098891)
               .with_callbacks(vec![
                   CoreCallbackData {
                       event: EventFilter::Hover(HoverEventFilter::MouseUp),
                       refany: node_local_dataset.clone(),
                       callback: CoreCallback { cb: nodegraph_delete_node as usize, ctx: OptionRefAny::None },
                   },
               ].into())
               .with_ids_and_classes({
                   const IDS_AND_CLASSES_7122017923389407516: &[IdOrClass] =
                       &[Class(AzString::from_const_str("node_close_button"))];
                   IdOrClassVec::from_const_slice(IDS_AND_CLASSES_7122017923389407516)
               }),
           Dom::create_text(node_info.node_type_name.clone())
               .with_css_props(CSS_MATCH_1739273067404038547)
               .with_ids_and_classes({
                   const IDS_AND_CLASSES_15777790571346582635: &[IdOrClass] =
                       &[Class(AzString::from_const_str("node_label"))];
                   IdOrClassVec::from_const_slice(IDS_AND_CLASSES_15777790571346582635)
               }),
           Dom::create_div()
               .with_css_props(CSS_MATCH_3354247437065914166)
               .with_ids_and_classes({
                   const IDS_AND_CLASSES_5590500152394859708: &[IdOrClass] =
                       &[Class(AzString::from_const_str("node_body"))];
                   IdOrClassVec::from_const_slice(IDS_AND_CLASSES_5590500152394859708)
               })
               .with_children(DomVec::from_vec(vec![
                   Dom::create_div()
                       .with_css_props(CSS_MATCH_16946967739775705757)
                       .with_ids_and_classes({
                           const IDS_AND_CLASSES_3626404106673061698: &[IdOrClass] =
                               &[Class(AzString::from_const_str("inputs"))];
                           IdOrClassVec::from_const_slice(IDS_AND_CLASSES_3626404106673061698)
                       })
                       .with_children(DomVec::from_vec(vec![Dom::create_div()
                           .with_css_props(CSS_MATCH_705881630351954657)
                           .with_ids_and_classes({
                               const IDS_AND_CLASSES_12825690349660780627: &[IdOrClass] =
                                   &[Class(AzString::from_const_str("node_input_wrapper"))];
                               IdOrClassVec::from_const_slice(
                                   IDS_AND_CLASSES_12825690349660780627,
                               )
                           })
                           .with_children(DomVec::from_vec(
                               inputs
                               .into_iter()
                               .enumerate()
                               .map(|(io_id, (input_label, input_color))| {
                                   use self::InputOrOutput::*;
                                   Dom::create_div()
                                       .with_css_props(CSS_MATCH_9863994880298313101)
                                       .with_ids_and_classes({
                                           const IDS_AND_CLASSES_5020681879750641508:
                                               &[IdOrClass] = &[Class(AzString::from_const_str(
                                               "node_input_container",
                                           ))];
                                           IdOrClassVec::from_const_slice(
                                               IDS_AND_CLASSES_5020681879750641508,
                                           )
                                       })
                                       .with_children(DomVec::from_vec(vec![
                                           Dom::create_div()
                                               .with_css_props(
                                                   CSS_MATCH_4700400755767504372,
                                               )
                                               .with_ids_and_classes({
                                                   const IDS_AND_CLASSES_9154857442066749879:
                                                       &[IdOrClass] =
                                                       &[Class(AzString::from_const_str(
                                                           "node_input_connection_label_wrapper",
                                                       ))];
                                                   IdOrClassVec::from_const_slice(
                                                       IDS_AND_CLASSES_9154857442066749879,
                                                   )
                                               })
                                               .with_children(DomVec::from_vec(vec![Dom::create_text(
                                                   input_label.clone(),
                                               )
                                               .with_css_props(
                                                   CSS_MATCH_11452431279102104133,
                                               )
                                               .with_ids_and_classes({
                                                   const IDS_AND_CLASSES_16291496011772407931:
                                                       &[IdOrClass] =
                                                       &[Class(AzString::from_const_str(
                                                           "node_input_connection_label",
                                                       ))];
                                                   IdOrClassVec::from_const_slice(
                                                       IDS_AND_CLASSES_16291496011772407931,
                                                   )
                                               })])),
                                           Dom::create_div()
                                               .with_callbacks(vec![
                                                   CoreCallbackData {
                                                       event: EventFilter::Hover(HoverEventFilter::LeftMouseUp),
                                                       refany: RefAny::new(NodeInputOutputLocalDataset {
                                                           io_id: Input(io_id),
                                                           backref: node_local_dataset.clone(),
                                                       }),
                                                       callback: CoreCallback { cb: nodegraph_input_output_connect as usize, ctx: OptionRefAny::None },
                                                   },
                                                   CoreCallbackData {
                                                       event: EventFilter::Hover(HoverEventFilter::MiddleMouseUp),
                                                       refany: RefAny::new(NodeInputOutputLocalDataset {
                                                           io_id: Input(io_id),
                                                           backref: node_local_dataset.clone(),
                                                       }),
                                                       callback: CoreCallback { cb: nodegraph_input_output_disconnect as usize, ctx: OptionRefAny::None },
                                                   },
                                               ].into())
                                               .with_css_props(CssPropertyWithConditionsVec::from_vec(vec![
                                                       // .node_input
                                                       CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
                                                           StyleBackgroundContentVecValue::Exact(vec![StyleBackgroundContent::Color(input_color)].into()),
                                                       )),
                                                       CssPropertyWithConditions::simple(CssProperty::Cursor(StyleCursorValue::Exact(
                                                           StyleCursor::Pointer,
                                                       ))),
                                                       CssPropertyWithConditions::simple(CssProperty::Height(LayoutHeightValue::Exact(
                                                           LayoutHeight::Px(PixelValue::const_px(15),),
                                                       ))),
                                                       CssPropertyWithConditions::simple(CssProperty::Width(LayoutWidthValue::Exact(
                                                           LayoutWidth::Px(PixelValue::const_px(15),),
                                                       ))),
                                                   ])
                                               )
                                               .with_ids_and_classes({
                                                   const IDS_AND_CLASSES_2128818677168244823:
                                                       &[IdOrClass] = &[Class(
                                                       AzString::from_const_str("node_input"),
                                                   )];
                                                   IdOrClassVec::from_const_slice(
                                                       IDS_AND_CLASSES_2128818677168244823,
                                                   )
                                               }),
                                       ]))
                               }).collect()
                           ))
                       ])),
                   Dom::create_div()
                       .with_css_props(CSS_MATCH_7432473243011547380)
                       .with_ids_and_classes({
                           const IDS_AND_CLASSES_746059979773622802: &[IdOrClass] =
                               &[Class(AzString::from_const_str("node_content_wrapper"))];
                           IdOrClassVec::from_const_slice(IDS_AND_CLASSES_746059979773622802)
                       })
                       .with_children({
                           let mut fields = Vec::new();
                           for (field_idx, field) in node.fields.iter().enumerate() {
                               let field_local_dataset = RefAny::new(NodeFieldLocalDataset {
                                   field_idx,
                                   backref: node_local_dataset.clone(),
                               });
                               let div = Dom::create_div()
                               .with_css_props(CSS_MATCH_2639191696846875011)
                               .with_ids_and_classes({
                                   const IDS_AND_CLASSES_4413230059125905311: &[IdOrClass] =
                                       &[Class(AzString::from_const_str(
                                           "node_configuration_field_container",
                                       ))];
                                   IdOrClassVec::from_const_slice(
                                       IDS_AND_CLASSES_4413230059125905311,
                                   )
                               })
                               .with_children(DomVec::from_vec(vec![
                                   Dom::create_text(field.key.clone())
                                   .with_css_props(CSS_MATCH_1198521124955124418)
                                   .with_ids_and_classes({
                                       const IDS_AND_CLASSES_12334207996395559585:
                                           &[IdOrClass] =
                                           &[Class(AzString::from_const_str(
                                               "node_configuration_field_label",
                                           ))];
                                       IdOrClassVec::from_const_slice(
                                           IDS_AND_CLASSES_12334207996395559585,
                                       )
                                   }),
                                   match &field.value {
                                       NodeTypeFieldValue::TextInput(initial_text) => {
                                           TextInput::create()
                                           .with_text(initial_text.clone())
                                           .with_on_focus_lost(field_local_dataset, nodegraph_on_textinput_focus_lost as TextInputOnFocusLostCallbackType)
                                           .dom()
                                       },
                                       NodeTypeFieldValue::NumberInput(initial_value) => {
                                           NumberInput::create(*initial_value)
                                           .with_on_focus_lost(field_local_dataset, nodegraph_on_numberinput_focus_lost as NumberInputOnFocusLostCallbackType)
                                           .dom()
                                       },
                                       NodeTypeFieldValue::CheckBox(initial_checked) => {
                                           CheckBox::create(*initial_checked)
                                           .with_on_toggle(field_local_dataset, nodegraph_on_checkbox_value_changed as CheckBoxOnToggleCallbackType)
                                           .dom()
                                       },
                                       NodeTypeFieldValue::ColorInput(initial_color) => {
                                           ColorInput::create(*initial_color)
                                           .with_on_value_change(field_local_dataset, nodegraph_on_colorinput_value_changed as ColorInputOnValueChangeCallbackType)
                                           .dom()
                                       },
                                       NodeTypeFieldValue::FileInput(file_path) => {
                                           let cb: FileInputOnPathChangeCallbackType = nodegraph_on_fileinput_button_clicked;
                                           FileInput::create(file_path.clone())
                                           .with_on_path_change(field_local_dataset, cb)
                                           .dom()
                                       },
                                   }
                               ]));
                               fields.push(div);
                           }
                           DomVec::from_vec(fields)
                       }),
                   Dom::create_div()
                       .with_css_props(CSS_MATCH_14906563417280941890)
                       .with_ids_and_classes({
                           const IDS_AND_CLASSES_4737474624251936466: &[IdOrClass] =
                               &[Class(AzString::from_const_str("outputs"))];
                           IdOrClassVec::from_const_slice(IDS_AND_CLASSES_4737474624251936466)
                       })
                       .with_children(DomVec::from_vec(vec![Dom::create_div()
                           .with_css_props(CSS_MATCH_10339190304804100510)
                           .with_ids_and_classes({
                               const IDS_AND_CLASSES_12883576328110161157: &[IdOrClass] =
                                   &[Class(AzString::from_const_str("node_output_wrapper"))];
                               IdOrClassVec::from_const_slice(
                                   IDS_AND_CLASSES_12883576328110161157,
                               )
                           })
                           .with_children(DomVec::from_vec(
                               outputs
                               .into_iter()
                               .enumerate()
                               .map(|(io_id, (output_label, output_color))| {
                                   use self::InputOrOutput::*;
                                   Dom::create_div()
                                       .with_css_props(CSS_MATCH_12400244273289328300)
                                       .with_ids_and_classes({
                                           const IDS_AND_CLASSES_10917819668096233812:
                                               &[IdOrClass] = &[Class(AzString::from_const_str(
                                               "node_output_container",
                                           ))];
                                           IdOrClassVec::from_const_slice(
                                               IDS_AND_CLASSES_10917819668096233812,
                                           )
                                       })
                                       .with_children(DomVec::from_vec(vec![
                                           Dom::create_div()
                                               .with_callbacks(vec![
                                                   CoreCallbackData {
                                                       event: EventFilter::Hover(HoverEventFilter::LeftMouseUp),
                                                       refany: RefAny::new(NodeInputOutputLocalDataset {
                                                           io_id: Output(io_id),
                                                           backref: node_local_dataset.clone(),
                                                       }),
                                                       callback: CoreCallback { cb: nodegraph_input_output_connect as usize, ctx: OptionRefAny::None },
                                                   },
                                                   CoreCallbackData {
                                                       event: EventFilter::Hover(HoverEventFilter::MiddleMouseUp),
                                                       refany: RefAny::new(NodeInputOutputLocalDataset {
                                                           io_id: Output(io_id),
                                                           backref: node_local_dataset.clone(),
                                                       }),
                                                       callback: CoreCallback { cb: nodegraph_input_output_disconnect as usize, ctx: OptionRefAny::None },
                                                   },
                                               ].into())
                                               .with_css_props(
                                                   CssPropertyWithConditionsVec::from_vec(vec![
                                                       // .node_output
                                                       CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
                                                           StyleBackgroundContentVecValue::Exact(vec![
                                                               StyleBackgroundContent::Color(output_color)
                                                           ].into()),
                                                       )),
                                                       CssPropertyWithConditions::simple(CssProperty::Cursor(StyleCursorValue::Exact(
                                                           StyleCursor::Pointer,
                                                       ))),
                                                       CssPropertyWithConditions::simple(CssProperty::Height(LayoutHeightValue::Exact(
                                                           LayoutHeight::Px(PixelValue::const_px(15),),
                                                       ))),
                                                       CssPropertyWithConditions::simple(CssProperty::Width(LayoutWidthValue::Exact(
                                                           LayoutWidth::Px(PixelValue::const_px(15),),
                                                       ))),
                                                   ])
                                               )
                                               .with_ids_and_classes({
                                                   const IDS_AND_CLASSES_17632471664405317563:
                                                       &[IdOrClass] = &[Class(
                                                       AzString::from_const_str("node_output"),
                                                   )];
                                                   IdOrClassVec::from_const_slice(
                                                       IDS_AND_CLASSES_17632471664405317563,
                                                   )
                                               }),
                                           Dom::create_div()
                                               .with_css_props(
                                                   CSS_MATCH_12038890904436132038,
                                               )
                                               .with_ids_and_classes({
                                                   const IDS_AND_CLASSES_1667960214206134147:
                                                       &[IdOrClass] =
                                                       &[Class(AzString::from_const_str(
                                                           "node_output_connection_label_wrapper",
                                                       ))];
                                                   IdOrClassVec::from_const_slice(
                                                       IDS_AND_CLASSES_1667960214206134147,
                                                   )
                                               })
                                               .with_children(DomVec::from_vec(vec![Dom::create_text(
                                                   output_label.clone(),
                                               )
                                               .with_css_props(
                                                   CSS_MATCH_2008162367868363199,
                                               )
                                               .with_ids_and_classes({
                                                   const IDS_AND_CLASSES_2974914452796301884:
                                                       &[IdOrClass] =
                                                       &[Class(AzString::from_const_str(
                                                           "node_output_connection_label",
                                                       ))];
                                                   IdOrClassVec::from_const_slice(
                                                       IDS_AND_CLASSES_2974914452796301884,
                                                   )
                                               })])),
                                       ]))
                               }).collect()
                           ))])),
               ])),
        ]))
        .with_dataset(Some(node_local_dataset).into())
    ].into())
}
fn render_connections(node_graph: &NodeGraph, root_marker_nodedata: RefAny) -> Dom {
    static NODEGRAPH_CONNECTIONS_CONTAINER_CLASS: &[IdOrClass] = &[Class(
        AzString::from_const_str("nodegraph-connections-container"),
    )];
    static NODEGRAPH_CONNECTIONS_CONTAINER_PROPS: &[CssPropertyWithConditions] = &[
        CssPropertyWithConditions::simple(CssProperty::position(LayoutPosition::Absolute)),
        CssPropertyWithConditions::simple(CssProperty::flex_grow(LayoutFlexGrow::const_new(1))),
    ];
    Dom::create_div()
        .with_ids_and_classes(IdOrClassVec::from_const_slice(
            NODEGRAPH_CONNECTIONS_CONTAINER_CLASS,
        ))
        .with_css_props(CssPropertyWithConditionsVec::from_const_slice(
            NODEGRAPH_CONNECTIONS_CONTAINER_PROPS,
        ))
        .with_dataset(Some(root_marker_nodedata).into())
        .with_children({
            let mut children = Vec::new();
            for NodeIdNodeMap { node_id, node } in node_graph.nodes.as_ref().iter() {
                let out_node_id = node_id;
                let node_type_info = match node_graph
                    .node_types
                    .iter()
                    .find(|i| i.node_type_id == node.node_type)
                {
                    Some(s) => &s.node_type_info,
                    None => continue,
                };
                for OutputConnection {
                    output_index,
                    connects_to,
                } in node.connect_out.as_ref().iter()
                {
                    let output_type_id = match node_type_info.outputs.get(*output_index) {
                        Some(s) => s,
                        None => continue,
                    };
                    let output_color = match node_graph
                        .input_output_types
                        .iter()
                        .find(|o| o.io_type_id == *output_type_id)
                    {
                        Some(s) => s.io_info.color.clone(),
                        None => continue,
                    };
                    for InputNodeAndIndex {
                        node_id,
                        input_index,
                    } in connects_to.as_ref().iter()
                    {
                        let in_node_id = node_id;
                        let mut cld = ConnectionLocalDataset {
                            out_node_id: *out_node_id,
                            out_idx: *output_index,
                            in_node_id: *in_node_id,
                            in_idx: *input_index,
                            swap_vert: false,
                            swap_horz: false,
                            color: output_color,
                        };
                        let (rect, swap_vert, swap_horz) = match get_rect(&node_graph, cld) {
                            Some(s) => s,
                            None => continue,
                        };
                        cld.swap_vert = swap_vert;
                        cld.swap_horz = swap_horz;
                        let cld_refany = RefAny::new(cld);
                        let connection_div = Dom::create_image(ImageRef::callback(
                            draw_connection as usize,
                            cld_refany.clone(),
                        ))
                        .with_dataset(Some(cld_refany).into())
                        .with_css_props(
                            vec![
                                CssPropertyWithConditions::simple(CssProperty::Transform(
                                    StyleTransformVecValue::Exact(
                                        vec![
                                            StyleTransform::Translate(StyleTransformTranslate2D {
                                                x: PixelValue::px(
                                                    node_graph.offset.x + rect.origin.x,
                                                ),
                                                y: PixelValue::px(
                                                    node_graph.offset.y + rect.origin.y,
                                                ),
                                            }),
                                            StyleTransform::ScaleX(PercentageValue::new(
                                                node_graph.scale_factor * 100.0,
                                            )),
                                            StyleTransform::ScaleY(PercentageValue::new(
                                                node_graph.scale_factor * 100.0,
                                            )),
                                        ]
                                        .into(),
                                    ),
                                )),
                                CssPropertyWithConditions::simple(CssProperty::Width(
                                    LayoutWidthValue::Exact(LayoutWidth::Px(PixelValue::px(
                                        rect.size.width,
                                    ))),
                                )),
                                CssPropertyWithConditions::simple(CssProperty::Height(
                                    LayoutHeightValue::Exact(LayoutHeight::Px(PixelValue::px(
                                        rect.size.height,
                                    ))),
                                )),
                            ]
                            .into(),
                        );
                        children.push(
                            Dom::create_div()
                                .with_css(
                                    "flex-grow: 1; position: absolute; overflow: hidden;",
                                )
                                .with_children(vec![connection_div].into()),
                        );
                    }
                }
            }
            children.into()
        })
}
extern "C" fn draw_connection(mut refany: RefAny, _info: ()) -> ImageRef {
    // RenderImageCallbackInfo not available in memtest
    // let size = info.get_bounds().get_physical_size();
    let size = azul_core::geom::LogicalSize {
        width: 100.0,
        height: 100.0,
    };
    let invalid = ImageRef::null_image(
        size.width as usize,
        size.height as usize,
        RawImageFormat::R8,
        Vec::new(),
    );
    // Cannot call draw_connection_inner without RenderImageCallbackInfo
    invalid
}
const NODE_WIDTH: f32 = 250.0;
const V_OFFSET: f32 = 71.0;
const DIST_BETWEEN_NODES: f32 = 10.0;
const CONNECTION_DOT_HEIGHT: f32 = 15.0;
// calculates the rect on which the connection is drawn in the UI
fn get_rect(
    node_graph: &NodeGraph,
    connection: ConnectionLocalDataset,
) -> Option<(LogicalRect, bool, bool)> {
    let ConnectionLocalDataset {
        out_node_id,
        out_idx,
        in_node_id,
        in_idx,
        ..
    } = connection;
    let out_node = node_graph.nodes.iter().find(|i| i.node_id == out_node_id)?;
    let in_node = node_graph.nodes.iter().find(|i| i.node_id == in_node_id)?;
    let x_out = out_node.node.position.x + NODE_WIDTH;
    let y_out = out_node.node.position.y
        + V_OFFSET
        + (out_idx as f32 * (DIST_BETWEEN_NODES + CONNECTION_DOT_HEIGHT));
    let x_in = in_node.node.position.x;
    let y_in = in_node.node.position.y
        + V_OFFSET
        + (in_idx as f32 * (DIST_BETWEEN_NODES + CONNECTION_DOT_HEIGHT));
    let should_swap_vertical = y_in > y_out;
    let should_swap_horizontal = x_in < x_out;
    let width = (x_in - x_out).abs();
    let height = (y_in - y_out).abs() + CONNECTION_DOT_HEIGHT;
    let x = x_in.min(x_out);
    let y = y_in.min(y_out);
    Some((
        LogicalRect {
            size: LogicalSize { width, height },
            origin: LogicalPosition { x, y },
        },
        should_swap_vertical,
        should_swap_horizontal,
    ))
}
extern "C" fn nodegraph_set_active_node(mut refany: RefAny, _info: CallbackInfo) -> Update {
    let data_clone = refany.clone();
    if let Some(mut refany) = refany.downcast_mut::<NodeLocalDataset>() {
        let node_id = refany.node_id.clone();
        if let Some(mut backref) = refany.backref.downcast_mut::<NodeGraphLocalDataset>() {
            backref.active_node_being_dragged = Some((node_id, data_clone));
        }
    }
    Update::DoNothing
}
extern "C" fn nodegraph_unset_active_node(mut refany: RefAny, _info: CallbackInfo) -> Update {
    if let Some(mut refany) = refany.downcast_mut::<NodeGraphLocalDataset>() {
        refany.active_node_being_dragged = None;
    }
    Update::DoNothing
}
// drag either the graph or the currently active nodes
extern "C" fn nodegraph_drag_graph_or_nodes(mut refany: RefAny, mut info: CallbackInfo) -> Update {
    let mut refany = match refany.downcast_mut::<NodeGraphLocalDataset>() {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let refany = &mut *refany;
    let prev = match info.get_previous_mouse_state() {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let cur = info.get_current_mouse_state();
    if !(cur.left_down && prev.left_down) {
        // event is not a drag event
        return Update::DoNothing;
    }
    let (current_mouse_pos, previous_mouse_pos) = match (cur.cursor_position, prev.cursor_position)
    {
        (InWindow(c), InWindow(p)) => (c, p),
        _ => return Update::DoNothing,
    };
    let dx = (current_mouse_pos.x - previous_mouse_pos.x) * (1.0 / refany.node_graph.scale_factor);
    let dy = (current_mouse_pos.y - previous_mouse_pos.y) * (1.0 / refany.node_graph.scale_factor);
    let nodegraph_node = info.get_hit_node();
    let should_update = match refany.active_node_being_dragged.clone() {
        // drag node
        Some((node_graph_node_id, data_marker)) => {
            let node_connection_marker = &mut refany.node_connection_marker;
            let _nodegraph_node = info.get_hit_node();
            let result = match refany.callbacks.on_node_dragged.as_ref() {
                Some(OnNodeDragged { callback, refany }) => (callback.cb)(
                    refany.clone(),
                    info.clone(),
                    node_graph_node_id,
                    NodeDragAmount { x: dx, y: dy },
                ),
                None => Update::DoNothing,
            };
            // update the visual transform of the node in the UI
            let node_position = match refany
                .node_graph
                .nodes
                .iter_mut()
                .find(|i| i.node_id == node_graph_node_id)
            {
                Some(s) => {
                    s.node.position.x += dx;
                    s.node.position.y += dy;
                    s.node.position
                }
                None => return Update::DoNothing,
            };
            let visual_node_id = match info.get_node_id_of_root_dataset(data_marker) {
                Some(s) => s,
                None => return Update::DoNothing,
            };
            let node_transform = StyleTransformTranslate2D {
                x: PixelValue::px(node_position.x + refany.node_graph.offset.x),
                y: PixelValue::px(node_position.y + refany.node_graph.offset.y),
            };
            info.set_css_property(
                visual_node_id,
                CssProperty::transform(
                    if refany.node_graph.scale_factor != 1.0 {
                        vec![
                            StyleTransform::Translate(node_transform),
                            StyleTransform::ScaleX(PercentageValue::new(
                                refany.node_graph.scale_factor * 100.0,
                            )),
                            StyleTransform::ScaleY(PercentageValue::new(
                                refany.node_graph.scale_factor * 100.0,
                            )),
                        ]
                    } else {
                        vec![StyleTransform::Translate(node_transform)]
                    }
                    .into(),
                ),
            );
            // get the NodeId of the node containing all the connection lines
            let connection_container_nodeid =
                match info.get_node_id_of_root_dataset(node_connection_marker.clone()) {
                    Some(s) => s,
                    None => return result,
                };
            // animate all the connections
            let mut first_connection_child = info.get_first_child(connection_container_nodeid);
            while let Some(connection_nodeid) = first_connection_child {
                first_connection_child = info.get_next_sibling(connection_nodeid);
                let first_child = match info.get_first_child(connection_nodeid) {
                    Some(s) => s,
                    None => continue,
                };
                let mut dataset = match info.get_dataset(first_child) {
                    Some(s) => s,
                    None => continue,
                };
                let mut cld = match dataset.downcast_mut::<ConnectionLocalDataset>() {
                    Some(s) => s,
                    None => continue,
                };
                if !(cld.out_node_id == node_graph_node_id || cld.in_node_id == node_graph_node_id)
                {
                    continue; // connection does not need to be modified
                }
                let (new_rect, swap_vert, swap_horz) = match get_rect(&refany.node_graph, *cld) {
                    Some(s) => s,
                    None => continue,
                };
                cld.swap_vert = swap_vert;
                cld.swap_horz = swap_horz;
                let node_transform = StyleTransformTranslate2D {
                    x: PixelValue::px(refany.node_graph.offset.x + new_rect.origin.x),
                    y: PixelValue::px(refany.node_graph.offset.y + new_rect.origin.y),
                };
                info.set_css_property(
                    first_child,
                    CssProperty::transform(
                        if refany.node_graph.scale_factor != 1.0 {
                            vec![
                                StyleTransform::Translate(node_transform),
                                StyleTransform::ScaleX(PercentageValue::new(
                                    refany.node_graph.scale_factor * 100.0,
                                )),
                                StyleTransform::ScaleY(PercentageValue::new(
                                    refany.node_graph.scale_factor * 100.0,
                                )),
                            ]
                        } else {
                            vec![StyleTransform::Translate(node_transform)]
                        }
                        .into(),
                    ),
                );
                info.set_css_property(
                    first_child,
                    CssProperty::Width(LayoutWidthValue::Exact(LayoutWidth::Px(PixelValue::px(
                        new_rect.size.width,
                    )))),
                );
                info.set_css_property(
                    first_child,
                    CssProperty::Height(LayoutHeightValue::Exact(LayoutHeight::Px(
                        PixelValue::px(new_rect.size.height),
                    ))),
                );
            }
            result
        }
        // drag graph
        None => {
            let result = match refany.callbacks.on_node_graph_dragged.as_ref() {
                Some(OnNodeGraphDragged { callback, refany }) => (callback.cb)(
                    refany.clone(),
                    info.clone(),
                    GraphDragAmount { x: dx, y: dy },
                ),
                None => Update::DoNothing,
            };
            refany.node_graph.offset.x += dx;
            refany.node_graph.offset.y += dy;
            // Update the visual node positions
            let node_container = match info.get_first_child(nodegraph_node) {
                Some(s) => s,
                None => return Update::DoNothing,
            };
            let node_container = match info.get_next_sibling(node_container) {
                Some(s) => s,
                None => return Update::DoNothing,
            };
            let mut node = match info.get_first_child(node_container) {
                Some(s) => s,
                None => return Update::DoNothing,
            };
            loop {
                let node_first_child = match info.get_first_child(node) {
                    Some(s) => s,
                    None => return Update::DoNothing,
                };
                let mut node_local_dataset = match info.get_dataset(node_first_child) {
                    None => return Update::DoNothing,
                    Some(s) => s,
                };
                let node_graph_node_id = match node_local_dataset.downcast_ref::<NodeLocalDataset>()
                {
                    Some(s) => s,
                    None => continue,
                };
                let node_graph_node_id = node_graph_node_id.node_id;
                let node_position = match refany
                    .node_graph
                    .nodes
                    .iter()
                    .find(|i| i.node_id == node_graph_node_id)
                {
                    Some(s) => s.node.position,
                    None => continue,
                };
                let node_transform = StyleTransformTranslate2D {
                    x: PixelValue::px(node_position.x + refany.node_graph.offset.x),
                    y: PixelValue::px(node_position.y + refany.node_graph.offset.y),
                };
                info.set_css_property(
                    node_first_child,
                    CssProperty::transform(
                        if refany.node_graph.scale_factor != 1.0 {
                            vec![
                                StyleTransform::Translate(node_transform),
                                StyleTransform::ScaleX(PercentageValue::new(
                                    refany.node_graph.scale_factor * 100.0,
                                )),
                                StyleTransform::ScaleY(PercentageValue::new(
                                    refany.node_graph.scale_factor * 100.0,
                                )),
                            ]
                        } else {
                            vec![StyleTransform::Translate(node_transform)]
                        }
                        .into(),
                    ),
                );
                node = match info.get_next_sibling(node) {
                    Some(s) => s,
                    None => break,
                };
            }
            let node_connection_marker = &mut refany.node_connection_marker;
            // Update the connection positions
            let connection_container_nodeid =
                match info.get_node_id_of_root_dataset(node_connection_marker.clone()) {
                    Some(s) => s,
                    None => return result,
                };
            let mut first_connection_child = info.get_first_child(connection_container_nodeid);
            while let Some(connection_nodeid) = first_connection_child {
                first_connection_child = info.get_next_sibling(connection_nodeid);
                let first_child = match info.get_first_child(connection_nodeid) {
                    Some(s) => s,
                    None => continue,
                };
                let mut dataset = match info.get_dataset(first_child) {
                    Some(s) => s,
                    None => continue,
                };
                let cld = match dataset.downcast_ref::<ConnectionLocalDataset>() {
                    Some(s) => s,
                    None => continue,
                };
                let (new_rect, _, _) = match get_rect(&refany.node_graph, *cld) {
                    Some(s) => s,
                    None => continue,
                };
                info.set_css_property(
                    first_child,
                    CssProperty::transform(
                        vec![
                            StyleTransform::Translate(StyleTransformTranslate2D {
                                x: PixelValue::px(refany.node_graph.offset.x + new_rect.origin.x),
                                y: PixelValue::px(refany.node_graph.offset.y + new_rect.origin.y),
                            }),
                            StyleTransform::ScaleX(PercentageValue::new(
                                refany.node_graph.scale_factor * 100.0,
                            )),
                            StyleTransform::ScaleY(PercentageValue::new(
                                refany.node_graph.scale_factor * 100.0,
                            )),
                        ]
                        .into(),
                    ),
                );
            }
            result
        }
    };
    info.stop_propagation();
    should_update
}
extern "C" fn nodegraph_duplicate_node(mut refany: RefAny, _info: CallbackInfo) -> Update {
    let _data = match refany.downcast_mut::<NodeLocalDataset>() {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    Update::DoNothing // TODO
}
extern "C" fn nodegraph_delete_node(mut refany: RefAny, mut info: CallbackInfo) -> Update {
    let mut refany = match refany.downcast_mut::<NodeLocalDataset>() {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let node_id = refany.node_id.clone();
    let mut backref = match refany.backref.downcast_mut::<NodeGraphLocalDataset>() {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let result = match backref.callbacks.on_node_removed.as_ref() {
        Some(OnNodeRemoved { callback, refany }) => (callback.cb)(refany.clone(), info, node_id),
        None => Update::DoNothing,
    };
    result
}
extern "C" fn nodegraph_context_menu_click(mut refany: RefAny, mut info: CallbackInfo) -> Update {
    use azul_core::window::CursorPosition;
    let mut refany = match refany.downcast_mut::<ContextMenuEntryLocalDataset>() {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let new_node_type = refany.node_type.clone();
    let node_graph_wrapper_id = match info.get_node_id_of_root_dataset(refany.backref.clone()) {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let mut backref = match refany.backref.downcast_mut::<NodeGraphLocalDataset>() {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let node_wrapper_offset = info
        .get_node_position(node_graph_wrapper_id)
        .map(|p| p)
        .map(|p| (p.x, p.y))
        .unwrap_or((0.0, 0.0));
    let cursor_in_viewport = match info.get_current_mouse_state().cursor_position {
        CursorPosition::InWindow(i) => i,
        CursorPosition::OutOfWindow(i) => i,
        _ => LogicalPosition::zero(),
    };
    let new_node_pos = NodeGraphNodePosition {
        x: (cursor_in_viewport.x - node_wrapper_offset.0) * (1.0 / backref.node_graph.scale_factor)
            - backref.node_graph.offset.x,
        y: (cursor_in_viewport.y - node_wrapper_offset.1) * (1.0 / backref.node_graph.scale_factor)
            - backref.node_graph.offset.y,
    };
    let new_node_id = backref.node_graph.generate_unique_node_id();
    let result = match backref.callbacks.on_node_added.as_ref() {
        Some(OnNodeAdded { callback, refany }) => (callback.cb)(
            refany.clone(),
            info,
            new_node_type,
            new_node_id,
            new_node_pos,
        ),
        None => Update::DoNothing,
    };
    result
}
extern "C" fn nodegraph_input_output_connect(mut refany: RefAny, mut info: CallbackInfo) -> Update {
    use self::InputOrOutput::*;
    let mut refany = match refany.downcast_mut::<NodeInputOutputLocalDataset>() {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let io_id = refany.io_id.clone();
    let mut backref = match refany.backref.downcast_mut::<NodeLocalDataset>() {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let node_id = backref.node_id.clone();
    let mut backref = match backref.backref.downcast_mut::<NodeGraphLocalDataset>() {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let (input_node, input_index, output_node, output_index) =
        match backref.last_input_or_output_clicked.clone() {
            None => {
                backref.last_input_or_output_clicked = Some((node_id, io_id));
                return Update::DoNothing;
            }
            Some((prev_node_id, prev_io_id)) => {
                match (prev_io_id, io_id) {
                    (Input(i), Output(o)) => (prev_node_id, i, node_id, o),
                    (Output(o), Input(i)) => (node_id, i, prev_node_id, o),
                    _ => {
                        // error: trying to connect input to input or output to output
                        backref.last_input_or_output_clicked = None;
                        return Update::DoNothing;
                    }
                }
            }
        };
    // verify that the nodetype matches
    match backref.node_graph.connect_input_output(
        input_node,
        input_index,
        output_node,
        output_index,
    ) {
        Ok(_) => {}
        Err(e) => {
            eprintln!("{:?}", e);
            backref.last_input_or_output_clicked = None;
            return Update::DoNothing;
        }
    }
    let result = match backref.callbacks.on_node_connected.as_ref() {
        Some(OnNodeConnected { callback, refany }) => {
            let r = (callback.cb)(
                refany.clone(),
                info,
                input_node,
                input_index,
                output_node,
                output_index,
            );
            backref.last_input_or_output_clicked = None;
            r
        }
        None => Update::DoNothing,
    };
    result
}
extern "C" fn nodegraph_input_output_disconnect(mut refany: RefAny, info: CallbackInfo) -> Update {
    use self::InputOrOutput::*;
    let mut refany = match refany.downcast_mut::<NodeInputOutputLocalDataset>() {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let io_id = refany.io_id.clone();
    let mut backref = match refany.backref.downcast_mut::<NodeLocalDataset>() {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let node_id = backref.node_id.clone();
    let mut backref = match backref.backref.downcast_mut::<NodeGraphLocalDataset>() {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let mut result = Update::DoNothing;
    match io_id {
        Input(i) => {
            result.max_self(
                match backref.callbacks.on_node_input_disconnected.as_ref() {
                    Some(OnNodeInputDisconnected { callback, refany }) => {
                        (callback.cb)(refany.clone(), info, node_id, i)
                    }
                    None => Update::DoNothing,
                },
            );
        }
        Output(o) => {
            result.max_self(
                match backref.callbacks.on_node_output_disconnected.as_ref() {
                    Some(OnNodeOutputDisconnected { callback, refany }) => {
                        (callback.cb)(refany.clone(), info, node_id, o)
                    }
                    None => Update::DoNothing,
                },
            );
        }
    };
    result
}
extern "C" fn nodegraph_on_textinput_focus_lost(
    mut refany: RefAny,
    info: CallbackInfo,
    textinputstate: TextInputState,
) -> Update {
    let mut refany = match refany.downcast_mut::<NodeFieldLocalDataset>() {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let field_idx = refany.field_idx;
    let mut node_local_dataset = match refany.backref.downcast_mut::<NodeLocalDataset>() {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let node_id = node_local_dataset.node_id;
    let mut node_graph = match node_local_dataset
        .backref
        .downcast_mut::<NodeGraphLocalDataset>()
    {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let node_type = match node_graph
        .node_graph
        .nodes
        .iter()
        .find(|i| i.node_id == node_id)
    {
        Some(s) => s.node.node_type,
        None => return Update::DoNothing,
    };
    let result = match node_graph.callbacks.on_node_field_edited.as_ref() {
        Some(OnNodeFieldEdited { refany, callback }) => (callback.cb)(
            refany.clone(),
            info,
            node_id,
            field_idx,
            node_type,
            NodeTypeFieldValue::TextInput(textinputstate.get_text().into()),
        ),
        None => Update::DoNothing,
    };
    result
}
extern "C" fn nodegraph_on_numberinput_focus_lost(
    mut refany: RefAny,
    info: CallbackInfo,
    numberinputstate: NumberInputState,
) -> Update {
    let mut refany = match refany.downcast_mut::<NodeFieldLocalDataset>() {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let field_idx = refany.field_idx;
    let mut node_local_dataset = match refany.backref.downcast_mut::<NodeLocalDataset>() {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let node_id = node_local_dataset.node_id;
    let mut node_graph = match node_local_dataset
        .backref
        .downcast_mut::<NodeGraphLocalDataset>()
    {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let node_type = match node_graph
        .node_graph
        .nodes
        .iter()
        .find(|i| i.node_id == node_id)
    {
        Some(s) => s.node.node_type,
        None => return Update::DoNothing,
    };
    let result = match node_graph.callbacks.on_node_field_edited.as_ref() {
        Some(OnNodeFieldEdited { refany, callback }) => (callback.cb)(
            refany.clone(),
            info,
            node_id,
            field_idx,
            node_type,
            NodeTypeFieldValue::NumberInput(numberinputstate.number),
        ),
        None => Update::DoNothing,
    };
    result
}
extern "C" fn nodegraph_on_checkbox_value_changed(
    mut refany: RefAny,
    info: CallbackInfo,
    checkboxinputstate: CheckBoxState,
) -> Update {
    let mut refany = match refany.downcast_mut::<NodeFieldLocalDataset>() {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let field_idx = refany.field_idx;
    let mut node_local_dataset = match refany.backref.downcast_mut::<NodeLocalDataset>() {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let node_id = node_local_dataset.node_id;
    let mut node_graph = match node_local_dataset
        .backref
        .downcast_mut::<NodeGraphLocalDataset>()
    {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let node_type = match node_graph
        .node_graph
        .nodes
        .iter()
        .find(|i| i.node_id == node_id)
    {
        Some(s) => s.node.node_type,
        None => return Update::DoNothing,
    };
    let result = match node_graph.callbacks.on_node_field_edited.as_ref() {
        Some(OnNodeFieldEdited { refany, callback }) => (callback.cb)(
            refany.clone(),
            info,
            node_id,
            field_idx,
            node_type,
            NodeTypeFieldValue::CheckBox(checkboxinputstate.checked),
        ),
        None => Update::DoNothing,
    };
    result
}
extern "C" fn nodegraph_on_colorinput_value_changed(
    mut refany: RefAny,
    info: CallbackInfo,
    colorinputstate: ColorInputState,
) -> Update {
    let mut refany = match refany.downcast_mut::<NodeFieldLocalDataset>() {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let field_idx = refany.field_idx;
    let mut node_local_dataset = match refany.backref.downcast_mut::<NodeLocalDataset>() {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let node_id = node_local_dataset.node_id;
    let mut node_graph = match node_local_dataset
        .backref
        .downcast_mut::<NodeGraphLocalDataset>()
    {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let node_type = match node_graph
        .node_graph
        .nodes
        .iter()
        .find(|i| i.node_id == node_id)
    {
        Some(s) => s.node.node_type,
        None => return Update::DoNothing,
    };
    let result = match node_graph.callbacks.on_node_field_edited.as_ref() {
        Some(OnNodeFieldEdited { refany, callback }) => (callback.cb)(
            refany.clone(),
            info,
            node_id,
            field_idx,
            node_type,
            NodeTypeFieldValue::ColorInput(colorinputstate.color),
        ),
        None => Update::DoNothing,
    };
    result
}
extern "C" fn nodegraph_on_fileinput_button_clicked(
    mut refany: RefAny,
    info: CallbackInfo,
    file: FileInputState,
) -> Update {
    let mut refany = match refany.downcast_mut::<NodeFieldLocalDataset>() {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let field_idx = refany.field_idx;
    let mut node_local_dataset = match refany.backref.downcast_mut::<NodeLocalDataset>() {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let node_id = node_local_dataset.node_id;
    let mut node_graph = match node_local_dataset
        .backref
        .downcast_mut::<NodeGraphLocalDataset>()
    {
        Some(s) => s,
        None => return Update::DoNothing,
    };
    let node_type = match node_graph
        .node_graph
        .nodes
        .iter()
        .find(|i| i.node_id == node_id)
    {
        Some(s) => s.node.node_type,
        None => return Update::DoNothing,
    };
    // If a new file was selected, invoke callback
    let result = match node_graph.callbacks.on_node_field_edited.as_ref() {
        Some(OnNodeFieldEdited { refany, callback }) => (callback.cb)(
            refany.clone(),
            info,
            node_id,
            field_idx,
            node_type,
            NodeTypeFieldValue::FileInput(file.path.clone()),
        ),
        None => return Update::DoNothing,
    };
    result
}