dear-node-editor 0.14.0

Safe Rust bindings for imgui-node-editor for Dear ImGui
Documentation
use crate::sys;
use bitflags::bitflags;
use std::ffi::CStr;

macro_rules! id_type {
    ($name:ident) => {
        #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
        #[repr(transparent)]
        pub struct $name(pub usize);

        impl $name {
            #[inline]
            pub const fn new(value: usize) -> Self {
                Self(value)
            }

            #[inline]
            pub const fn raw(self) -> usize {
                self.0
            }

            #[inline]
            pub const fn is_null(self) -> bool {
                self.0 == 0
            }
        }

        impl From<usize> for $name {
            #[inline]
            fn from(value: usize) -> Self {
                Self(value)
            }
        }

        impl From<$name> for usize {
            #[inline]
            fn from(value: $name) -> Self {
                value.0
            }
        }
    };
}

id_type!(NodeId);
id_type!(PinId);
id_type!(LinkId);

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PinKind {
    Input,
    Output,
}

impl PinKind {
    pub(crate) fn raw(self) -> sys::DnePinKind {
        match self {
            Self::Input => sys::DNE_PIN_KIND_INPUT,
            Self::Output => sys::DNE_PIN_KIND_OUTPUT,
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FlowDirection {
    Forward,
    Backward,
}

impl FlowDirection {
    pub(crate) fn raw(self) -> sys::DneFlowDirection {
        match self {
            Self::Forward => sys::DNE_FLOW_FORWARD,
            Self::Backward => sys::DNE_FLOW_BACKWARD,
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CanvasSizeMode {
    FitVerticalView,
    FitHorizontalView,
    CenterOnly,
}

impl CanvasSizeMode {
    pub(crate) fn raw(self) -> sys::DneCanvasSizeMode {
        match self {
            Self::FitVerticalView => sys::DNE_CANVAS_SIZE_FIT_VERTICAL_VIEW,
            Self::FitHorizontalView => sys::DNE_CANVAS_SIZE_FIT_HORIZONTAL_VIEW,
            Self::CenterOnly => sys::DNE_CANVAS_SIZE_CENTER_ONLY,
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StyleColor {
    Background,
    Grid,
    NodeBackground,
    NodeBorder,
    HoveredNodeBorder,
    SelectedNodeBorder,
    NodeSelectionRect,
    NodeSelectionRectBorder,
    HoveredLinkBorder,
    SelectedLinkBorder,
    HighlightLinkBorder,
    LinkSelectionRect,
    LinkSelectionRectBorder,
    PinRect,
    PinRectBorder,
    Flow,
    FlowMarker,
    GroupBackground,
    GroupBorder,
}

impl StyleColor {
    pub const COUNT: usize = sys::DNE_STYLE_COLOR_COUNT as usize;

    pub const ALL: [Self; Self::COUNT] = [
        Self::Background,
        Self::Grid,
        Self::NodeBackground,
        Self::NodeBorder,
        Self::HoveredNodeBorder,
        Self::SelectedNodeBorder,
        Self::NodeSelectionRect,
        Self::NodeSelectionRectBorder,
        Self::HoveredLinkBorder,
        Self::SelectedLinkBorder,
        Self::HighlightLinkBorder,
        Self::LinkSelectionRect,
        Self::LinkSelectionRectBorder,
        Self::PinRect,
        Self::PinRectBorder,
        Self::Flow,
        Self::FlowMarker,
        Self::GroupBackground,
        Self::GroupBorder,
    ];

    pub(crate) const fn raw(self) -> sys::DneStyleColor {
        match self {
            Self::Background => sys::DNE_STYLE_COLOR_BG,
            Self::Grid => sys::DNE_STYLE_COLOR_GRID,
            Self::NodeBackground => sys::DNE_STYLE_COLOR_NODE_BG,
            Self::NodeBorder => sys::DNE_STYLE_COLOR_NODE_BORDER,
            Self::HoveredNodeBorder => sys::DNE_STYLE_COLOR_HOVERED_NODE_BORDER,
            Self::SelectedNodeBorder => sys::DNE_STYLE_COLOR_SELECTED_NODE_BORDER,
            Self::NodeSelectionRect => sys::DNE_STYLE_COLOR_NODE_SELECTION_RECT,
            Self::NodeSelectionRectBorder => sys::DNE_STYLE_COLOR_NODE_SELECTION_RECT_BORDER,
            Self::HoveredLinkBorder => sys::DNE_STYLE_COLOR_HOVERED_LINK_BORDER,
            Self::SelectedLinkBorder => sys::DNE_STYLE_COLOR_SELECTED_LINK_BORDER,
            Self::HighlightLinkBorder => sys::DNE_STYLE_COLOR_HIGHLIGHT_LINK_BORDER,
            Self::LinkSelectionRect => sys::DNE_STYLE_COLOR_LINK_SELECTION_RECT,
            Self::LinkSelectionRectBorder => sys::DNE_STYLE_COLOR_LINK_SELECTION_RECT_BORDER,
            Self::PinRect => sys::DNE_STYLE_COLOR_PIN_RECT,
            Self::PinRectBorder => sys::DNE_STYLE_COLOR_PIN_RECT_BORDER,
            Self::Flow => sys::DNE_STYLE_COLOR_FLOW,
            Self::FlowMarker => sys::DNE_STYLE_COLOR_FLOW_MARKER,
            Self::GroupBackground => sys::DNE_STYLE_COLOR_GROUP_BG,
            Self::GroupBorder => sys::DNE_STYLE_COLOR_GROUP_BORDER,
        }
    }

    /// Returns the upstream style color name.
    #[doc(alias = "GetStyleColorName")]
    pub fn name(self) -> &'static str {
        unsafe {
            let ptr = sys::dne_get_style_color_name(self.raw());
            if ptr.is_null() {
                return "Unknown";
            }
            CStr::from_ptr(ptr).to_str().unwrap_or("Unknown")
        }
    }

    pub const fn index(self) -> usize {
        self.raw() as usize
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StyleVar {
    NodePadding,
    NodeRounding,
    NodeBorderWidth,
    HoveredNodeBorderWidth,
    SelectedNodeBorderWidth,
    PinRounding,
    PinBorderWidth,
    LinkStrength,
    SourceDirection,
    TargetDirection,
    ScrollDuration,
    FlowMarkerDistance,
    FlowSpeed,
    FlowDuration,
    PivotAlignment,
    PivotSize,
    PivotScale,
    PinCorners,
    PinRadius,
    PinArrowSize,
    PinArrowWidth,
    GroupRounding,
    GroupBorderWidth,
    HighlightConnectedLinks,
    SnapLinkToPinDir,
    HoveredNodeBorderOffset,
    SelectedNodeBorderOffset,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StyleVarType {
    Float,
    Vec2,
    Vec4,
}

impl StyleVar {
    pub const fn value_type(self) -> StyleVarType {
        match self {
            Self::NodePadding => StyleVarType::Vec4,
            Self::SourceDirection
            | Self::TargetDirection
            | Self::PivotAlignment
            | Self::PivotSize
            | Self::PivotScale => StyleVarType::Vec2,
            Self::NodeRounding
            | Self::NodeBorderWidth
            | Self::HoveredNodeBorderWidth
            | Self::SelectedNodeBorderWidth
            | Self::PinRounding
            | Self::PinBorderWidth
            | Self::LinkStrength
            | Self::ScrollDuration
            | Self::FlowMarkerDistance
            | Self::FlowSpeed
            | Self::FlowDuration
            | Self::PinCorners
            | Self::PinRadius
            | Self::PinArrowSize
            | Self::PinArrowWidth
            | Self::GroupRounding
            | Self::GroupBorderWidth
            | Self::HighlightConnectedLinks
            | Self::SnapLinkToPinDir
            | Self::HoveredNodeBorderOffset
            | Self::SelectedNodeBorderOffset => StyleVarType::Float,
        }
    }

    pub(crate) fn raw(self) -> sys::DneStyleVar {
        match self {
            Self::NodePadding => sys::DNE_STYLE_VAR_NODE_PADDING,
            Self::NodeRounding => sys::DNE_STYLE_VAR_NODE_ROUNDING,
            Self::NodeBorderWidth => sys::DNE_STYLE_VAR_NODE_BORDER_WIDTH,
            Self::HoveredNodeBorderWidth => sys::DNE_STYLE_VAR_HOVERED_NODE_BORDER_WIDTH,
            Self::SelectedNodeBorderWidth => sys::DNE_STYLE_VAR_SELECTED_NODE_BORDER_WIDTH,
            Self::PinRounding => sys::DNE_STYLE_VAR_PIN_ROUNDING,
            Self::PinBorderWidth => sys::DNE_STYLE_VAR_PIN_BORDER_WIDTH,
            Self::LinkStrength => sys::DNE_STYLE_VAR_LINK_STRENGTH,
            Self::SourceDirection => sys::DNE_STYLE_VAR_SOURCE_DIRECTION,
            Self::TargetDirection => sys::DNE_STYLE_VAR_TARGET_DIRECTION,
            Self::ScrollDuration => sys::DNE_STYLE_VAR_SCROLL_DURATION,
            Self::FlowMarkerDistance => sys::DNE_STYLE_VAR_FLOW_MARKER_DISTANCE,
            Self::FlowSpeed => sys::DNE_STYLE_VAR_FLOW_SPEED,
            Self::FlowDuration => sys::DNE_STYLE_VAR_FLOW_DURATION,
            Self::PivotAlignment => sys::DNE_STYLE_VAR_PIVOT_ALIGNMENT,
            Self::PivotSize => sys::DNE_STYLE_VAR_PIVOT_SIZE,
            Self::PivotScale => sys::DNE_STYLE_VAR_PIVOT_SCALE,
            Self::PinCorners => sys::DNE_STYLE_VAR_PIN_CORNERS,
            Self::PinRadius => sys::DNE_STYLE_VAR_PIN_RADIUS,
            Self::PinArrowSize => sys::DNE_STYLE_VAR_PIN_ARROW_SIZE,
            Self::PinArrowWidth => sys::DNE_STYLE_VAR_PIN_ARROW_WIDTH,
            Self::GroupRounding => sys::DNE_STYLE_VAR_GROUP_ROUNDING,
            Self::GroupBorderWidth => sys::DNE_STYLE_VAR_GROUP_BORDER_WIDTH,
            Self::HighlightConnectedLinks => sys::DNE_STYLE_VAR_HIGHLIGHT_CONNECTED_LINKS,
            Self::SnapLinkToPinDir => sys::DNE_STYLE_VAR_SNAP_LINK_TO_PIN_DIR,
            Self::HoveredNodeBorderOffset => sys::DNE_STYLE_VAR_HOVERED_NODE_BORDER_OFFSET,
            Self::SelectedNodeBorderOffset => sys::DNE_STYLE_VAR_SELECTED_NODE_BORDER_OFFSET,
        }
    }
}

bitflags! {
    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
    pub struct SaveReasonFlags: u32 {
        const NAVIGATION = sys::DNE_SAVE_REASON_NAVIGATION as u32;
        const POSITION = sys::DNE_SAVE_REASON_POSITION as u32;
        const SIZE = sys::DNE_SAVE_REASON_SIZE as u32;
        const SELECTION = sys::DNE_SAVE_REASON_SELECTION as u32;
        const ADD_NODE = sys::DNE_SAVE_REASON_ADD_NODE as u32;
        const REMOVE_NODE = sys::DNE_SAVE_REASON_REMOVE_NODE as u32;
        const USER = sys::DNE_SAVE_REASON_USER as u32;
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn style_color_names_are_available() {
        assert_ne!(StyleColor::Background.name(), "Unknown");
        assert!(!StyleColor::Background.name().is_empty());
    }

    #[test]
    fn style_var_types_match_upstream_overloads() {
        assert_eq!(StyleVar::NodePadding.value_type(), StyleVarType::Vec4);
        assert_eq!(StyleVar::SourceDirection.value_type(), StyleVarType::Vec2);
        assert_eq!(StyleVar::TargetDirection.value_type(), StyleVarType::Vec2);
        assert_eq!(StyleVar::PivotAlignment.value_type(), StyleVarType::Vec2);
        assert_eq!(StyleVar::PivotSize.value_type(), StyleVarType::Vec2);
        assert_eq!(StyleVar::PivotScale.value_type(), StyleVarType::Vec2);
        assert_eq!(StyleVar::LinkStrength.value_type(), StyleVarType::Float);
        assert_eq!(
            StyleVar::HoveredNodeBorderOffset.value_type(),
            StyleVarType::Float
        );
    }
}