use std::collections::{HashMap, HashSet};
use std::fmt::Debug;
use std::hash::Hash;
use std::time::Duration;
use iced::{Length, Point, Size, Vector};
use crate::ids::{EdgeId, NodeId, PinId};
use crate::node_pin::{PinEnd, PinInfo};
use crate::style::{EdgeStatus, EdgeStyle, GraphStyle, NodeStatus, NodeStyle, PinStatus, PinStyle};
pub(crate) type NodeStyleFn<'a, Theme> = Box<dyn Fn(&Theme, NodeStatus) -> NodeStyle + 'a>;
pub(crate) type EdgeStyleFn<'a, P, UI, Theme> =
Box<dyn Fn(&Theme, EdgeStatus, PinInfo<'_, P, UI>, PinInfo<'_, P, UI>) -> EdgeStyle + 'a>;
pub(crate) type PinStyleFn<'a, P, UI, Theme> = Box<
dyn Fn(&Theme, &PinInfo<'_, P, UI>, Option<&PinInfo<'_, P, UI>>, PinStatus) -> PinStyle + 'a,
>;
pub(crate) type DragEdgeStyleFn<'a, P, UI, Theme> =
Box<dyn Fn(&Theme, PinInfo<'_, P, UI>) -> EdgeStyle + 'a>;
pub struct Node<'a, N, P, UI, Message, Theme, Renderer> {
id: N,
position: Point,
element: iced::Element<'a, Message, Theme, Renderer>,
style_fn: Option<NodeStyleFn<'a, Theme>>,
pin_style_fn: Option<PinStyleFn<'a, P, UI, Theme>>,
}
pub fn node<'a, N, P, UI, Message, Theme, Renderer>(
id: N,
position: Point,
element: impl Into<iced::Element<'a, Message, Theme, Renderer>>,
) -> Node<'a, N, P, UI, Message, Theme, Renderer> {
Node {
id,
position,
element: element.into(),
style_fn: None,
pin_style_fn: None,
}
}
impl<'a, N, P, UI, Message, Theme, Renderer> Node<'a, N, P, UI, Message, Theme, Renderer> {
pub fn style(mut self, f: impl Fn(&Theme, NodeStatus) -> NodeStyle + 'a) -> Self {
self.style_fn = Some(Box::new(f));
self
}
pub fn pin_style(
mut self,
f: impl Fn(&Theme, &PinInfo<'_, P, UI>, Option<&PinInfo<'_, P, UI>>, PinStatus) -> PinStyle + 'a,
) -> Self {
self.pin_style_fn = Some(Box::new(f));
self
}
}
pub struct Edge<'a, N, P, E, UI, Theme> {
id: E,
from: PinRef<N, P>,
to: PinRef<N, P>,
style_fn: Option<EdgeStyleFn<'a, P, UI, Theme>>,
}
pub fn edge<'a, N, P, E, UI, Theme>(
from: PinRef<N, P>,
to: PinRef<N, P>,
id: E,
) -> Edge<'a, N, P, E, UI, Theme> {
Edge {
id,
from,
to,
style_fn: None,
}
}
#[macro_export]
macro_rules! edge {
($from:expr, $to:expr $(,)?) => {
$crate::edge($from, $to, ())
};
($from:expr, $to:expr, $id:expr $(,)?) => {
$crate::edge($from, $to, $id)
};
}
impl<'a, N, P, E, UI, Theme> Edge<'a, N, P, E, UI, Theme> {
pub fn style(
mut self,
f: impl Fn(&Theme, EdgeStatus, PinInfo<'_, P, UI>, PinInfo<'_, P, UI>) -> EdgeStyle + 'a,
) -> Self {
self.style_fn = Some(Box::new(f));
self
}
}
pub mod camera;
pub(crate) mod euclid;
pub(crate) mod input;
pub(crate) mod state;
pub(crate) mod widget;
#[derive(Debug, Clone, Copy)]
pub(crate) struct RenderContext {
pub camera_zoom: f32,
pub camera_position: euclid::WorldPoint,
pub viewport_origin: euclid::ScreenVector,
pub time: f32,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Counts {
pub total: usize,
pub in_view: usize,
pub culled: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OpTiming {
pub label: &'static str,
pub duration: Duration,
}
#[derive(Debug, Clone, PartialEq)]
pub struct GraphInfo {
pub nodes: Counts,
pub pins: Counts,
pub edges: Counts,
pub timings: Vec<OpTiming>,
pub sdf_entries: u32,
pub sdf_tiles: u32,
}
#[derive(Debug, Clone)]
pub enum DragInfo<N = usize, P = usize> {
Node { node_id: N },
Group { node_ids: Vec<N> },
Edge { from_node: N, from_pin: P },
BoxSelect { start_x: f32, start_y: f32 },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PinRef<N, P> {
pub node_id: N,
pub pin_id: P,
}
impl<N: Clone, P: Clone> PinRef<N, P> {
pub fn new(node_id: N, pin_id: P) -> Self {
Self { node_id, pin_id }
}
}
#[allow(missing_debug_implementations)]
pub struct NodeGraph<
'a,
N = usize,
P = usize,
UI = (),
Message = (),
Theme = iced::Theme,
Renderer = iced::Renderer,
E = (),
> where
N: NodeId,
P: PinId,
E: EdgeId,
{
pub(super) size: Size<Length>,
pub(super) nodes: Vec<(
N,
Point,
iced::Element<'a, Message, Theme, Renderer>,
Option<NodeStyleFn<'a, Theme>>,
Option<PinStyleFn<'a, P, UI, Theme>>,
)>,
node_lookup: HashMap<N, usize>,
pub(super) edges: Vec<(
E,
PinRef<N, P>,
PinRef<N, P>,
Option<EdgeStyleFn<'a, P, UI, Theme>>,
)>,
graph_style: Option<Box<dyn Fn(&Theme) -> GraphStyle + 'a>>,
on_connect: Option<Box<dyn Fn(PinRef<N, P>, PinRef<N, P>) -> Message + 'a>>,
on_disconnect: Option<Box<dyn Fn(PinRef<N, P>, PinRef<N, P>) -> Message + 'a>>,
on_move: Option<Box<dyn Fn(Vector, Vec<N>) -> Message + 'a>>,
on_select: Option<Box<dyn Fn(Vec<N>) -> Message + 'a>>,
on_clone: Option<Box<dyn Fn(Vec<N>) -> Message + 'a>>,
on_delete: Option<Box<dyn Fn(Vec<N>) -> Message + 'a>>,
external_selection: Option<HashSet<usize>>,
on_drag_start: Option<Box<dyn Fn(DragInfo<N, P>) -> Message + 'a>>,
on_drag_update: Option<Box<dyn Fn(Point) -> Message + 'a>>,
on_drag_end: Option<Box<dyn Fn() -> Message + 'a>>,
on_pan: Option<Box<dyn Fn(Point, f32) -> Message + 'a>>,
on_info: Option<Box<dyn Fn(GraphInfo) -> Message + 'a>>,
pub(super) box_select_style_fn: Option<Box<dyn Fn(&Theme) -> (iced::Color, iced::Color) + 'a>>,
pub(super) cutting_tool_style_fn: Option<Box<dyn Fn(&Theme) -> iced::Color + 'a>>,
pub(super) dragging_edge_style_fn: Option<DragEdgeStyleFn<'a, P, UI, Theme>>,
pub(super) view: Option<(Point, f32)>,
pub(super) can_connect:
Option<Box<dyn Fn(PinEnd<'_, N, P, UI>, PinEnd<'_, N, P, UI>) -> bool + 'a>>,
pub(super) keymap: input::Keymap,
}
impl<N, P, E, UI, Message, Theme, Renderer> Default
for NodeGraph<'_, N, P, UI, Message, Theme, Renderer, E>
where
N: NodeId,
P: PinId,
E: EdgeId,
Renderer: iced_wgpu::core::renderer::Renderer,
{
fn default() -> Self {
Self {
size: Size::new(Length::Fill, Length::Fill),
nodes: Vec::new(),
node_lookup: HashMap::new(),
edges: Vec::new(),
graph_style: None,
on_connect: None,
on_disconnect: None,
on_move: None,
on_select: None,
on_clone: None,
on_delete: None,
external_selection: None,
on_drag_start: None,
on_drag_update: None,
on_drag_end: None,
on_pan: None,
on_info: None,
box_select_style_fn: None,
cutting_tool_style_fn: None,
dragging_edge_style_fn: None,
view: None,
can_connect: None,
keymap: input::Keymap::default(),
}
}
}
impl<'a, N, P, E, UI, Message, Theme, Renderer> NodeGraph<'a, N, P, UI, Message, Theme, Renderer, E>
where
N: NodeId + 'static,
P: PinId + 'static,
E: EdgeId + 'static,
Renderer: iced_wgpu::core::renderer::Renderer,
{
pub fn view(mut self, position: Point, zoom: f32) -> Self {
self.view = Some((position, zoom));
self
}
pub fn push_node(&mut self, node: Node<'a, N, P, UI, Message, Theme, Renderer>) {
match self.node_lookup.entry(node.id.clone()) {
std::collections::hash_map::Entry::Occupied(_) => {
debug_assert!(
false,
"duplicate node id {:?}: node ids must be unique; \
the duplicate push is ignored (first wins)",
node.id,
);
}
std::collections::hash_map::Entry::Vacant(entry) => {
entry.insert(self.nodes.len());
self.nodes.push((
node.id,
node.position,
node.element,
node.style_fn,
node.pin_style_fn,
));
}
}
}
pub fn push_edge(&mut self, edge: Edge<'a, N, P, E, UI, Theme>) {
self.edges
.push((edge.id, edge.from, edge.to, edge.style_fn));
}
pub(super) fn node_id_at(&self, index: usize) -> Option<&N> {
self.nodes.get(index).map(|(id, ..)| id)
}
pub(super) fn index_to_node_id(&self, index: usize) -> Option<N> {
self.node_id_at(index).cloned()
}
pub(super) fn node_index(&self, id: &N) -> Option<usize> {
self.node_lookup.get(id).copied()
}
pub fn graph_style(mut self, f: impl Fn(&Theme) -> GraphStyle + 'a) -> Self {
self.graph_style = Some(Box::new(f));
self
}
pub fn box_select_style(
mut self,
f: impl Fn(&Theme) -> (iced::Color, iced::Color) + 'a,
) -> Self {
self.box_select_style_fn = Some(Box::new(f));
self
}
pub fn dragging_edge_style(
mut self,
f: impl Fn(&Theme, PinInfo<'_, P, UI>) -> EdgeStyle + 'a,
) -> Self {
self.dragging_edge_style_fn = Some(Box::new(f));
self
}
pub fn cutting_tool_style(mut self, f: impl Fn(&Theme) -> iced::Color + 'a) -> Self {
self.cutting_tool_style_fn = Some(Box::new(f));
self
}
pub fn can_connect(
mut self,
f: impl Fn(PinEnd<'_, N, P, UI>, PinEnd<'_, N, P, UI>) -> bool + 'a,
) -> Self {
self.can_connect = Some(Box::new(f));
self
}
pub fn keymap(mut self, keymap: input::Keymap) -> Self {
self.keymap = keymap;
self
}
pub fn on_connect(mut self, f: impl Fn(PinRef<N, P>, PinRef<N, P>) -> Message + 'a) -> Self {
self.on_connect = Some(Box::new(f));
self
}
pub fn on_disconnect(mut self, f: impl Fn(PinRef<N, P>, PinRef<N, P>) -> Message + 'a) -> Self {
self.on_disconnect = Some(Box::new(f));
self
}
pub fn on_move(mut self, f: impl Fn(Vector, Vec<N>) -> Message + 'a) -> Self {
self.on_move = Some(Box::new(f));
self
}
pub fn on_select(mut self, f: impl Fn(Vec<N>) -> Message + 'a) -> Self {
self.on_select = Some(Box::new(f));
self
}
pub fn on_clone(mut self, f: impl Fn(Vec<N>) -> Message + 'a) -> Self {
self.on_clone = Some(Box::new(f));
self
}
pub fn on_delete(mut self, f: impl Fn(Vec<N>) -> Message + 'a) -> Self {
self.on_delete = Some(Box::new(f));
self
}
pub fn on_drag_start(mut self, f: impl Fn(DragInfo<N, P>) -> Message + 'a) -> Self {
self.on_drag_start = Some(Box::new(f));
self
}
pub fn on_drag_update(mut self, f: impl Fn(Point) -> Message + 'a) -> Self {
self.on_drag_update = Some(Box::new(f));
self
}
pub fn on_drag_end(mut self, f: impl Fn() -> Message + 'a) -> Self {
self.on_drag_end = Some(Box::new(f));
self
}
pub fn on_pan(mut self, f: impl Fn(Point, f32) -> Message + 'a) -> Self {
self.on_pan = Some(Box::new(f));
self
}
pub fn on_info(mut self, f: impl Fn(GraphInfo) -> Message + 'a) -> Self {
self.on_info = Some(Box::new(f));
self
}
pub fn selection<'b>(mut self, selection: impl IntoIterator<Item = &'b N>) -> Self
where
N: 'b,
{
let indices: HashSet<usize> = selection
.into_iter()
.filter_map(|id| self.node_index(id))
.collect();
self.external_selection = Some(indices);
self
}
pub fn width(mut self, width: impl Into<Length>) -> Self {
self.size.width = width.into();
self
}
pub fn height(mut self, height: impl Into<Length>) -> Self {
self.size.height = height.into();
self
}
pub(super) fn elements_iter(
&self,
) -> impl Iterator<Item = (Point, &iced::Element<'a, Message, Theme, Renderer>)> {
self.nodes.iter().map(|(_, p, e, _, _)| (*p, e))
}
pub(super) fn elements_iter_mut(
&mut self,
) -> impl Iterator<Item = (Point, &mut iced::Element<'a, Message, Theme, Renderer>)> {
self.nodes.iter_mut().map(|(_, p, e, _, _)| (*p, e))
}
pub(super) fn on_connect_handler(
&self,
) -> Option<&Box<dyn Fn(PinRef<N, P>, PinRef<N, P>) -> Message + 'a>> {
self.on_connect.as_ref()
}
pub(super) fn on_disconnect_handler(
&self,
) -> Option<&Box<dyn Fn(PinRef<N, P>, PinRef<N, P>) -> Message + 'a>> {
self.on_disconnect.as_ref()
}
pub(super) fn on_move_handler(&self) -> Option<&Box<dyn Fn(Vector, Vec<N>) -> Message + 'a>> {
self.on_move.as_ref()
}
pub(super) fn on_select_handler(&self) -> Option<&Box<dyn Fn(Vec<N>) -> Message + 'a>> {
self.on_select.as_ref()
}
pub(super) fn on_clone_handler(&self) -> Option<&Box<dyn Fn(Vec<N>) -> Message + 'a>> {
self.on_clone.as_ref()
}
pub(super) fn on_delete_handler(&self) -> Option<&Box<dyn Fn(Vec<N>) -> Message + 'a>> {
self.on_delete.as_ref()
}
pub(super) fn on_drag_start_handler(
&self,
) -> Option<&Box<dyn Fn(DragInfo<N, P>) -> Message + 'a>> {
self.on_drag_start.as_ref()
}
pub(super) fn on_drag_update_handler(&self) -> Option<&Box<dyn Fn(Point) -> Message + 'a>> {
self.on_drag_update.as_ref()
}
pub(super) fn on_drag_end_handler(&self) -> Option<&Box<dyn Fn() -> Message + 'a>> {
self.on_drag_end.as_ref()
}
pub(super) fn get_external_selection(&self) -> Option<&HashSet<usize>> {
self.external_selection.as_ref()
}
pub(super) fn on_pan_handler(&self) -> Option<&Box<dyn Fn(Point, f32) -> Message + 'a>> {
self.on_pan.as_ref()
}
pub(super) fn on_info_handler(&self) -> Option<&Box<dyn Fn(GraphInfo) -> Message + 'a>> {
self.on_info.as_ref()
}
pub(super) fn view_value(&self) -> Option<(Point, f32)> {
self.view
}
pub(super) fn translate_node_ids(&self, indices: &[usize]) -> Vec<N> {
indices
.iter()
.filter_map(|&idx| self.index_to_node_id(idx))
.collect()
}
}