use std::collections::{HashMap, HashSet};
use std::time::Duration;
use iced_nodegraph_sdf::SdfStats;
use iced_widget::core::widget::Id as WidgetId;
use iced_widget::core::{Element, Length, Point, Size, Vector};
use web_time::Instant;
use self::focus::{FocusOptions, FocusTarget};
use crate::ids::{Ids, Indexed};
use crate::node_pin::{PinEnd, PinInfo};
use crate::style::{
AnchorStatus, AnchorStyle, AnchorStyleFn, Catalog, CuttingToolStyle, CuttingToolStyleFn,
DragEdgeStyleFn, EdgeStatus, EdgeStyle, EdgeStyleFn, GraphStyle, GraphStyleFn, MinimapStyle,
MinimapStyleFn, NodeStatus, NodeStyle, NodeStyleFn, ParticleStyle, ParticleStyleFn, PinStatus,
PinStyle, PinStyleFn, SelectionBoxStyle, SelectionBoxStyleFn,
};
pub(crate) const PIN_CLICK_THRESHOLD: f32 = 8.0;
pub(crate) const EDGE_CUT_THRESHOLD: f32 = 10.0;
pub(crate) const DEFAULT_ORBIT_OFFSET: f32 = 11.0;
pub(crate) const DEFAULT_ORBIT_SPACING: f32 = 6.0;
pub(crate) const DEFAULT_CORE_SIZE: f32 = 6.0;
pub(crate) const ANCHOR_GRAB_THRESHOLD: f32 = 7.0;
pub(crate) const EDGE_GRAB_THRESHOLD: f32 = EDGE_CUT_THRESHOLD;
pub(crate) const EDGE_END_GRAB_LENGTH: f32 = 24.0;
pub(crate) const RESIZE_GRIP_SIDE: f32 = 12.0;
pub(crate) const MIN_NODE_SIZE: Size = Size::new(32.0, 24.0);
pub struct Node<
'a,
I: Ids = Indexed,
Message = (),
Theme = iced_widget::core::Theme,
Renderer = iced_widget::renderer::Renderer,
> where
Theme: Catalog,
{
pub(super) id: I::NodeId,
pub(super) position: Point,
pub(super) element: Element<'a, Message, Theme, Renderer>,
pub(super) selected: bool,
pub(super) resizable: bool,
pub(super) frame: bool,
pub(super) class: Theme::NodeClass<'a>,
pub(super) pin_class: Theme::PinClass<'a, I>,
}
pub fn node<'a, I: Ids, Message, Theme: Catalog, Renderer>(
id: I::NodeId,
position: Point,
element: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> Node<'a, I, Message, Theme, Renderer> {
Node {
id,
position,
element: element.into(),
selected: false,
resizable: false,
frame: false,
class: Theme::default_node(),
pin_class: Theme::default_pin(),
}
}
impl<'a, I: Ids, Message, Theme: Catalog, Renderer> Node<'a, I, Message, Theme, Renderer> {
pub fn style(mut self, f: impl Fn(&Theme, NodeStatus) -> NodeStyle + 'a) -> Self
where
Theme::NodeClass<'a>: From<NodeStyleFn<'a, Theme>>,
{
self.class = (Box::new(f) as NodeStyleFn<'a, Theme>).into();
self
}
pub fn class(mut self, class: impl Into<Theme::NodeClass<'a>>) -> Self {
self.class = class.into();
self
}
pub fn selected(mut self, selected: bool) -> Self {
self.selected = selected;
self
}
pub fn resizable(mut self, resizable: bool) -> Self {
self.resizable = resizable;
self
}
pub fn frame(mut self) -> Self {
self.frame = true;
self
}
pub fn pin_style(
mut self,
f: impl Fn(&Theme, &PinInfo<'_, I>, Option<&PinInfo<'_, I>>, PinStatus) -> PinStyle + 'a,
) -> Self
where
Theme::PinClass<'a, I>: From<PinStyleFn<'a, Theme, I>>,
{
self.pin_class = (Box::new(f) as PinStyleFn<'a, Theme, I>).into();
self
}
pub fn pin_class(mut self, class: impl Into<Theme::PinClass<'a, I>>) -> Self {
self.pin_class = class.into();
self
}
}
pub struct Edge<'a, I: Ids = Indexed, Theme = iced_widget::core::Theme>
where
Theme: Catalog,
{
pub(super) id: I::EdgeId,
pub(super) from: PinRef<I>,
pub(super) to: PinRef<I>,
pub(super) route: Vec<I::AnchorId>,
pub(super) class: Theme::EdgeClass<'a, I>,
pub(super) particles: Vec<Particle<'a, Theme>>,
}
pub fn edge<'a, I: Ids, Theme: Catalog>(
id: I::EdgeId,
from: PinRef<I>,
to: PinRef<I>,
) -> Edge<'a, I, Theme> {
Edge {
id,
from,
to,
route: Vec::new(),
class: Theme::default_edge(),
particles: Vec::new(),
}
}
impl<'a, I: Ids, Theme: Catalog> Edge<'a, I, Theme> {
pub fn style(
mut self,
f: impl Fn(&Theme, EdgeStatus, PinInfo<'_, I>, PinInfo<'_, I>) -> EdgeStyle + 'a,
) -> Self
where
Theme::EdgeClass<'a, I>: From<EdgeStyleFn<'a, Theme, I>>,
{
self.class = (Box::new(f) as EdgeStyleFn<'a, Theme, I>).into();
self
}
pub fn class(mut self, class: impl Into<Theme::EdgeClass<'a, I>>) -> Self {
self.class = class.into();
self
}
pub fn route(mut self, anchors: impl IntoIterator<Item = I::AnchorId>) -> Self {
self.route = anchors.into_iter().collect();
self
}
pub fn particles(mut self, particles: impl IntoIterator<Item = Particle<'a, Theme>>) -> Self {
self.particles.extend(particles);
self
}
}
pub struct Particle<'a, Theme = iced_widget::core::Theme>
where
Theme: Catalog,
{
pub(super) born: Instant,
pub(super) speed: f32,
pub(super) class: Theme::ParticleClass<'a>,
}
pub fn particle<'a, Theme: Catalog>(born: Instant, speed: f32) -> Particle<'a, Theme> {
Particle {
born,
speed,
class: Theme::default_particle(),
}
}
impl<'a, Theme: Catalog> Particle<'a, Theme> {
pub fn style(mut self, f: impl Fn(&Theme) -> ParticleStyle + 'a) -> Self
where
Theme::ParticleClass<'a>: From<ParticleStyleFn<'a, Theme>>,
{
self.class = (Box::new(f) as ParticleStyleFn<'a, Theme>).into();
self
}
pub fn class(mut self, class: impl Into<Theme::ParticleClass<'a>>) -> Self {
self.class = class.into();
self
}
}
#[allow(missing_debug_implementations)]
pub struct Anchor<'a, I: Ids = Indexed, Theme = iced_widget::core::Theme>
where
Theme: Catalog,
{
pub(super) id: I::AnchorId,
pub(super) position: Point,
pub(super) class: Theme::AnchorClass<'a>,
}
pub fn anchor<'a, I: Ids, Theme: Catalog>(
id: I::AnchorId,
position: Point,
) -> Anchor<'a, I, Theme> {
Anchor {
id,
position,
class: Theme::default_anchor(),
}
}
impl<'a, I: Ids, Theme: Catalog> Anchor<'a, I, Theme> {
pub fn style(mut self, f: impl Fn(&Theme, AnchorStatus) -> AnchorStyle + 'a) -> Self
where
Theme::AnchorClass<'a>: From<AnchorStyleFn<'a, Theme>>,
{
self.class = (Box::new(f) as AnchorStyleFn<'a, Theme>).into();
self
}
pub fn class(mut self, class: impl Into<Theme::AnchorClass<'a>>) -> Self {
self.class = class.into();
self
}
}
pub fn node_graph<'a, Message, Theme, Renderer>() -> NodeGraph<'a, Indexed, Message, Theme, Renderer>
where
Theme: Catalog,
Renderer: iced_widget::core::renderer::Renderer,
{
NodeGraph::new()
}
pub(crate) mod cable;
pub(crate) mod camera;
pub(crate) mod edge_path;
pub(crate) mod euclid;
pub(crate) mod focus;
pub(crate) mod input;
pub(crate) mod orbits;
pub(crate) mod state;
pub(crate) mod widget;
#[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 anchors: Counts,
pub timings: Vec<OpTiming>,
pub sdf: SdfStats,
}
#[derive(Debug, Clone, PartialEq)]
pub enum DragInfo<I: Ids = Indexed> {
Node { node_id: I::NodeId },
Group { node_ids: Vec<I::NodeId> },
Edge {
from_node: I::NodeId,
from_pin: I::PinId,
},
SelectionBox { start_x: f32, start_y: f32 },
Anchor { anchor_id: I::AnchorId },
Route { edge_id: I::EdgeId },
Resize { node_id: I::NodeId },
EdgeCut,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PinRef<I: Ids = Indexed> {
pub node_id: I::NodeId,
pub pin_id: I::PinId,
}
impl<I: Ids> Copy for PinRef<I>
where
I::NodeId: Copy,
I::PinId: Copy,
{
}
impl<I: Ids> PinRef<I> {
pub fn new(node_id: I::NodeId, pin_id: I::PinId) -> Self {
Self { node_id, pin_id }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Corner {
TopLeft,
TopRight,
BottomLeft,
#[default]
BottomRight,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Minimap {
pub size: Size,
pub corner: Corner,
pub margin: f32,
}
impl Default for Minimap {
fn default() -> Self {
Self {
size: Size::new(200.0, 150.0),
corner: Corner::BottomRight,
margin: 12.0,
}
}
}
#[allow(missing_debug_implementations)]
pub struct NodeGraph<
'a,
I: Ids = Indexed,
Message = (),
Theme = iced_widget::core::Theme,
Renderer = iced_widget::renderer::Renderer,
> where
Theme: Catalog,
{
pub(super) size: Size<Length>,
pub(super) id: Option<WidgetId>,
pub(super) nodes: Vec<Node<'a, I, Message, Theme, Renderer>>,
pub(super) node_lookup: HashMap<I::NodeId, usize>,
pub(super) anchors: Vec<Anchor<'a, I, Theme>>,
pub(super) anchor_lookup: HashMap<I::AnchorId, usize>,
pub(super) edges: Vec<Edge<'a, I, Theme>>,
pub(super) graph_class: Theme::GraphClass<'a>,
pub(super) on_connect: Option<Box<dyn Fn(PinRef<I>, PinRef<I>) -> Message + 'a>>,
pub(super) on_disconnect: Option<Box<dyn Fn(PinRef<I>, PinRef<I>) -> Message + 'a>>,
pub(super) on_connect_refused: Option<Box<dyn Fn(PinRef<I>, PinRef<I>) -> Message + 'a>>,
pub(super) on_move: Option<Box<dyn Fn(Vector, Vec<I::NodeId>) -> Message + 'a>>,
pub(super) on_resize: Option<Box<dyn Fn(I::NodeId, Size) -> Message + 'a>>,
pub(super) on_anchor_move: Option<Box<dyn Fn(I::AnchorId, Point) -> Message + 'a>>,
pub(super) on_anchor_create: Option<Box<dyn Fn(I::EdgeId, Point) -> Message + 'a>>,
pub(super) on_route_attach: Option<Box<dyn Fn(I::EdgeId, I::AnchorId) -> Message + 'a>>,
pub(super) on_route_detach: Option<Box<dyn Fn(I::EdgeId, I::AnchorId) -> Message + 'a>>,
pub(super) on_anchor_delete: Option<Box<dyn Fn(I::AnchorId) -> Message + 'a>>,
pub(super) on_select: Option<Box<dyn Fn(Vec<I::NodeId>) -> Message + 'a>>,
pub(super) on_clone: Option<Box<dyn Fn(Vec<I::NodeId>) -> Message + 'a>>,
pub(super) on_delete: Option<Box<dyn Fn(Vec<I::NodeId>) -> Message + 'a>>,
pub(super) on_edge_delete: Option<Box<dyn Fn(Vec<I::EdgeId>) -> Message + 'a>>,
pub(super) on_drag_start: Option<Box<dyn Fn(DragInfo<I>) -> Message + 'a>>,
pub(super) on_drag_update: Option<Box<dyn Fn(Point) -> Message + 'a>>,
pub(super) on_drag_end: Option<Box<dyn Fn() -> Message + 'a>>,
pub(super) on_camera: Option<Box<dyn Fn(Point, f32) -> Message + 'a>>,
pub(super) on_info: Option<Box<dyn Fn(GraphInfo) -> Message + 'a>>,
pub(super) drag_edge_class: Theme::DragEdgeClass<'a, I>,
pub(super) drag_anchor_class: Theme::AnchorClass<'a>,
pub(super) selection_box_class: Theme::SelectionBoxClass<'a>,
pub(super) cutting_tool_class: Theme::CuttingToolClass<'a>,
pub(super) minimap: Option<Minimap>,
pub(super) minimap_class: Theme::MinimapClass<'a>,
pub(super) camera: Option<(Point, f32)>,
pub(super) can_connect: Option<Box<dyn Fn(PinEnd<'_, I>, PinEnd<'_, I>) -> bool + 'a>>,
pub(super) keymap: input::Keymap,
pub(super) snap_grid: Option<f32>,
}
impl<I: Ids, Message, Theme: Catalog, Renderer> Default
for NodeGraph<'_, I, Message, Theme, Renderer>
{
fn default() -> Self {
Self {
size: Size::new(Length::Fill, Length::Fill),
id: None,
nodes: Vec::new(),
node_lookup: HashMap::new(),
anchors: Vec::new(),
anchor_lookup: HashMap::new(),
edges: Vec::new(),
graph_class: Theme::default_graph(),
on_connect: None,
on_disconnect: None,
on_connect_refused: None,
on_move: None,
on_resize: None,
on_anchor_move: None,
on_anchor_create: None,
on_route_attach: None,
on_route_detach: None,
on_anchor_delete: None,
on_select: None,
on_clone: None,
on_delete: None,
on_edge_delete: None,
on_drag_start: None,
on_drag_update: None,
on_drag_end: None,
on_camera: None,
on_info: None,
drag_edge_class: Theme::default_drag_edge(),
drag_anchor_class: Theme::default_anchor(),
selection_box_class: Theme::default_selection_box(),
cutting_tool_class: Theme::default_cutting_tool(),
minimap: None,
minimap_class: Theme::default_minimap(),
camera: None,
can_connect: None,
keymap: input::Keymap::default(),
snap_grid: None,
}
}
}
impl<'a, I: Ids, Message, Theme: Catalog, Renderer> NodeGraph<'a, I, Message, Theme, Renderer> {
pub fn new() -> Self {
Self::default()
}
pub fn id(mut self, id: impl Into<WidgetId>) -> Self {
self.id = Some(id.into());
self
}
pub fn camera(mut self, position: Point, zoom: f32) -> Self {
self.camera = Some((position, zoom));
self
}
pub fn snap_grid(mut self, spacing: f32) -> Self {
self.snap_grid = Some(spacing);
self
}
pub fn push_node(mut self, node: Node<'a, I, Message, Theme, Renderer>) -> Self {
if self.node_lookup.contains_key(&node.id) {
debug_assert!(
false,
"duplicate node id {:?}: the duplicate push is ignored (first wins)",
node.id,
);
return self;
}
self.node_lookup.insert(node.id.clone(), self.nodes.len());
self.nodes.push(node);
self
}
pub fn nodes(
self,
nodes: impl IntoIterator<Item = Node<'a, I, Message, Theme, Renderer>>,
) -> Self {
nodes.into_iter().fold(self, Self::push_node)
}
pub fn push_anchor(mut self, anchor: Anchor<'a, I, Theme>) -> Self {
if self.anchor_lookup.contains_key(&anchor.id) {
debug_assert!(
false,
"duplicate anchor id {:?}: the duplicate push is ignored (first wins)",
anchor.id,
);
return self;
}
self.anchor_lookup
.insert(anchor.id.clone(), self.anchors.len());
self.anchors.push(anchor);
self
}
pub fn anchors(self, anchors: impl IntoIterator<Item = Anchor<'a, I, Theme>>) -> Self {
anchors.into_iter().fold(self, Self::push_anchor)
}
pub fn push_edge(mut self, edge: Edge<'a, I, Theme>) -> Self {
self.edges.push(edge);
self
}
pub fn edges(mut self, edges: impl IntoIterator<Item = Edge<'a, I, Theme>>) -> Self {
self.edges.extend(edges);
self
}
pub(super) fn node_id_at(&self, index: usize) -> Option<&I::NodeId> {
self.nodes.get(index).map(|node| &node.id)
}
pub(super) fn node_index(&self, id: &I::NodeId) -> Option<usize> {
self.node_lookup.get(id).copied()
}
pub(super) fn anchor_index(&self, id: &I::AnchorId) -> Option<usize> {
self.anchor_lookup.get(id).copied()
}
pub(super) fn host_selection(&self) -> HashSet<usize> {
self.nodes
.iter()
.enumerate()
.filter(|(_, node)| node.selected)
.map(|(index, _)| index)
.collect()
}
pub(in crate::node_graph) fn resolved_selection(
&self,
state: &state::NodeGraphState,
) -> HashSet<usize> {
match &state.pending_selection {
Some(pending) => pending.clone(),
None => self.host_selection(),
}
}
pub(super) fn selection_indices(selection: &HashSet<usize>) -> Vec<usize> {
let mut indices: Vec<usize> = selection.iter().copied().collect();
indices.sort_unstable();
indices
}
pub(super) fn selection_ids(&self, selection: &HashSet<usize>) -> Vec<I::NodeId> {
self.node_ids_at(&Self::selection_indices(selection))
}
pub(super) fn node_ids_at(&self, indices: &[usize]) -> Vec<I::NodeId> {
indices
.iter()
.filter_map(|&index| self.node_id_at(index).cloned())
.collect()
}
pub fn graph_style(mut self, f: impl Fn(&Theme) -> GraphStyle + 'a) -> Self
where
Theme::GraphClass<'a>: From<GraphStyleFn<'a, Theme>>,
{
self.graph_class = (Box::new(f) as GraphStyleFn<'a, Theme>).into();
self
}
pub fn graph_class(mut self, class: impl Into<Theme::GraphClass<'a>>) -> Self {
self.graph_class = class.into();
self
}
pub fn selection_box_style(mut self, f: impl Fn(&Theme) -> SelectionBoxStyle + 'a) -> Self
where
Theme::SelectionBoxClass<'a>: From<SelectionBoxStyleFn<'a, Theme>>,
{
self.selection_box_class = (Box::new(f) as SelectionBoxStyleFn<'a, Theme>).into();
self
}
pub fn selection_box_class(mut self, class: impl Into<Theme::SelectionBoxClass<'a>>) -> Self {
self.selection_box_class = class.into();
self
}
pub fn cutting_tool_style(mut self, f: impl Fn(&Theme) -> CuttingToolStyle + 'a) -> Self
where
Theme::CuttingToolClass<'a>: From<CuttingToolStyleFn<'a, Theme>>,
{
self.cutting_tool_class = (Box::new(f) as CuttingToolStyleFn<'a, Theme>).into();
self
}
pub fn cutting_tool_class(mut self, class: impl Into<Theme::CuttingToolClass<'a>>) -> Self {
self.cutting_tool_class = class.into();
self
}
pub fn minimap(mut self, minimap: Minimap) -> Self {
self.minimap = Some(minimap);
self
}
pub fn minimap_style(mut self, f: impl Fn(&Theme) -> MinimapStyle + 'a) -> Self
where
Theme::MinimapClass<'a>: From<MinimapStyleFn<'a, Theme>>,
{
self.minimap_class = (Box::new(f) as MinimapStyleFn<'a, Theme>).into();
self
}
pub fn minimap_class(mut self, class: impl Into<Theme::MinimapClass<'a>>) -> Self {
self.minimap_class = class.into();
self
}
pub fn dragging_edge_style(
mut self,
f: impl Fn(&Theme, PinInfo<'_, I>) -> EdgeStyle + 'a,
) -> Self
where
Theme::DragEdgeClass<'a, I>: From<DragEdgeStyleFn<'a, Theme, I>>,
{
self.drag_edge_class = (Box::new(f) as DragEdgeStyleFn<'a, Theme, I>).into();
self
}
pub fn dragging_edge_class(mut self, class: impl Into<Theme::DragEdgeClass<'a, I>>) -> Self {
self.drag_edge_class = class.into();
self
}
pub fn dragging_anchor_style(
mut self,
f: impl Fn(&Theme, AnchorStatus) -> AnchorStyle + 'a,
) -> Self
where
Theme::AnchorClass<'a>: From<AnchorStyleFn<'a, Theme>>,
{
self.drag_anchor_class = (Box::new(f) as AnchorStyleFn<'a, Theme>).into();
self
}
pub fn dragging_anchor_class(mut self, class: impl Into<Theme::AnchorClass<'a>>) -> Self {
self.drag_anchor_class = class.into();
self
}
pub fn can_connect(mut self, f: impl Fn(PinEnd<'_, I>, PinEnd<'_, I>) -> 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<I>, PinRef<I>) -> Message + 'a) -> Self {
self.on_connect = Some(Box::new(f));
self
}
pub fn on_disconnect(mut self, f: impl Fn(PinRef<I>, PinRef<I>) -> Message + 'a) -> Self {
self.on_disconnect = Some(Box::new(f));
self
}
pub fn on_connect_refused(mut self, f: impl Fn(PinRef<I>, PinRef<I>) -> Message + 'a) -> Self {
self.on_connect_refused = Some(Box::new(f));
self
}
pub fn on_move(mut self, f: impl Fn(Vector, Vec<I::NodeId>) -> Message + 'a) -> Self {
self.on_move = Some(Box::new(f));
self
}
pub fn on_resize(mut self, f: impl Fn(I::NodeId, Size) -> Message + 'a) -> Self {
self.on_resize = Some(Box::new(f));
self
}
pub fn on_anchor_move(mut self, f: impl Fn(I::AnchorId, Point) -> Message + 'a) -> Self {
self.on_anchor_move = Some(Box::new(f));
self
}
pub fn on_anchor_create(mut self, f: impl Fn(I::EdgeId, Point) -> Message + 'a) -> Self {
self.on_anchor_create = Some(Box::new(f));
self
}
pub fn on_route_attach(mut self, f: impl Fn(I::EdgeId, I::AnchorId) -> Message + 'a) -> Self {
self.on_route_attach = Some(Box::new(f));
self
}
pub fn on_route_detach(mut self, f: impl Fn(I::EdgeId, I::AnchorId) -> Message + 'a) -> Self {
self.on_route_detach = Some(Box::new(f));
self
}
pub fn on_anchor_delete(mut self, f: impl Fn(I::AnchorId) -> Message + 'a) -> Self {
self.on_anchor_delete = Some(Box::new(f));
self
}
pub fn on_select(mut self, f: impl Fn(Vec<I::NodeId>) -> Message + 'a) -> Self {
self.on_select = Some(Box::new(f));
self
}
pub fn on_clone(mut self, f: impl Fn(Vec<I::NodeId>) -> Message + 'a) -> Self {
self.on_clone = Some(Box::new(f));
self
}
pub fn on_delete(mut self, f: impl Fn(Vec<I::NodeId>) -> Message + 'a) -> Self {
self.on_delete = Some(Box::new(f));
self
}
pub fn on_edge_delete(mut self, f: impl Fn(Vec<I::EdgeId>) -> Message + 'a) -> Self {
self.on_edge_delete = Some(Box::new(f));
self
}
pub fn on_drag_start(mut self, f: impl Fn(DragInfo<I>) -> 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_camera(mut self, f: impl Fn(Point, f32) -> Message + 'a) -> Self {
self.on_camera = 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 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, &Element<'a, Message, Theme, Renderer>)> {
self.nodes.iter().map(|node| (node.position, &node.element))
}
pub(super) fn elements_iter_mut(
&mut self,
) -> impl Iterator<Item = (Point, &mut Element<'a, Message, Theme, Renderer>)> {
self.nodes
.iter_mut()
.map(|node| (node.position, &mut node.element))
}
}