egui_xyflow 0.2.0

An interactive node-graph editor widget for egui, inspired by xyflow (React Flow)
Documentation
//! Handle types for node connection points.
//!
//! [`NodeHandle`] is the user-facing type placed on nodes; [`Handle`] is the
//! resolved version with absolute coordinates used internally.

use std::sync::Arc;

use super::position::Position;

/// Whether a handle is a source (output) or target (input) for connections.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum HandleType {
    /// Output handle — connections start here.
    Source,
    /// Input handle — connections end here.
    Target,
}

/// A resolved handle with absolute position within a node.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Handle {
    pub id: Option<String>,
    pub node_id: Arc<str>,
    pub x: f32,
    pub y: f32,
    pub position: Position,
    pub handle_type: HandleType,
    pub width: f32,
    pub height: f32,
}

impl Handle {
    /// Return the center point of this handle relative to its node.
    pub fn center(&self) -> egui::Pos2 {
        egui::pos2(self.x + self.width / 2.0, self.y + self.height / 2.0)
    }
}

/// User-specified handle on a node (before measurement).
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NodeHandle {
    pub id: Option<String>,
    pub handle_type: HandleType,
    pub position: Position,
    #[cfg_attr(feature = "serde", serde(default))]
    pub x: f32,
    #[cfg_attr(feature = "serde", serde(default))]
    pub y: f32,
    pub width: Option<f32>,
    pub height: Option<f32>,
}

impl NodeHandle {
    /// Create a source (output) handle at the given position.
    pub fn source(position: Position) -> Self {
        Self {
            id: None,
            handle_type: HandleType::Source,
            position,
            x: 0.0,
            y: 0.0,
            width: None,
            height: None,
        }
    }

    /// Create a target (input) handle at the given position.
    pub fn target(position: Position) -> Self {
        Self {
            id: None,
            handle_type: HandleType::Target,
            position,
            x: 0.0,
            y: 0.0,
            width: None,
            height: None,
        }
    }

    /// Assign an identifier to this handle for multi-handle nodes.
    pub fn with_id(mut self, id: impl Into<String>) -> Self {
        self.id = Some(id.into());
        self
    }
}