From 1e2b1aec40d3b0446d690f590605e83731eef7ec Mon Sep 17 00:00:00 2001 From: Rendo Date: Mon, 10 Aug 2026 18:39:39 +0500 Subject: [PATCH] feat: Node view and node spawn button --- src/graph.rs | 64 ++++++++++++++++++++++++++++++ src/main.rs | 19 ++++++--- src/node.rs | 105 ++++++++++++++++++++++++++------------------------ src/socket.rs | 9 +++++ 4 files changed, 140 insertions(+), 57 deletions(-) create mode 100644 src/graph.rs diff --git a/src/graph.rs b/src/graph.rs new file mode 100644 index 0000000..b53e9aa --- /dev/null +++ b/src/graph.rs @@ -0,0 +1,64 @@ +use crate::node::Node; +use crate::socket::{Socket, SocketConnection}; + +use iced::widget::stack; +use iced::{Element,Point}; + +#[derive(Default)] +pub struct Graph { + pub nodes: Vec, + pub sockets: Vec, + pub connections: Vec, + pub current_drag_socket: Option, + pub current_drag_node: Option, +} + +impl Graph { + pub fn update(&mut self, message: GraphMessage) { + match message { + GraphMessage::SocketDragStart(socket) => { + self.current_drag_socket = Some(socket); + }, + GraphMessage::SocketDragEnd(socket) => { + if let Some(first_socket) = self.current_drag_socket { + if self.sockets[first_socket].compatable(&self.sockets[socket]){ + self.toggle_connection(first_socket, socket); + } + self.current_drag_socket = None; + } + }, + GraphMessage::NodeStartDrag(index) => self.current_drag_node = Some(index), + GraphMessage::NodeDrag(point, idx) if let Some(node_index) = self.current_drag_node && idx == node_index => self.nodes[node_index].position = point, + GraphMessage::NodeStopDrag => self.current_drag_node = None, + GraphMessage::SpawnNode(node) => self.nodes.push(node), + _ => {}, + } + } + pub fn view(&self) -> Element<'_, crate::Message> { + let nodes: Vec> = self.nodes.iter().enumerate().map(|(idx, node)| node.view(&self.sockets,idx)).collect(); + stack(nodes).into() + } + + pub fn toggle_connection(&mut self, from: usize, to: usize) { + let connection = SocketConnection(from, to); + let found = self.connections.iter().position(|el| *el == connection); + if let Some(found_index) = found { + self.connections.remove(found_index); + } + else { + self.connections.push(connection); + } + } +} + +#[derive(Clone)] +pub enum GraphMessage { + NodeStartDrag(usize), + NodeDrag(Point, usize), + NodeStopDrag, + + SocketDragStart(usize), + SocketDragEnd(usize), + + SpawnNode(Node), +} diff --git a/src/main.rs b/src/main.rs index 5bd9b71..e69c6aa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,26 +1,33 @@ mod node; mod socket; +mod graph; -use iced::{Element}; +use iced::{Element, widget::{button, column}}; -use crate::node::{NodeMessage, Nodes}; +use crate::{graph::{Graph,GraphMessage}, node::Node}; #[derive(Default)] pub struct State { - nodes: Nodes + graph: Graph } +#[derive(Clone)] pub enum Message { - NodeMessage(NodeMessage) + GraphMessage(GraphMessage) } impl State { pub fn view(&self) -> Element<'_, Message> { - todo!(); + let spawn_button = button("Spawn node"). + on_press(Message::GraphMessage(GraphMessage::SpawnNode(Node::new()))); + column![ + spawn_button, + self.graph.view() + ].into() } pub fn update(&mut self, message: Message) { match message { - Message::NodeMessage(message) => self.nodes.update(message) + Message::GraphMessage(message) => self.graph.update(message) } } } diff --git a/src/node.rs b/src/node.rs index 51681ba..6bb20f8 100644 --- a/src/node.rs +++ b/src/node.rs @@ -1,62 +1,65 @@ -use iced::{Element, Point}; +use iced::widget::container::{bordered_box, rounded_box}; +use iced::{Element, Length, Point}; +use iced::widget::{Column, column, container, mouse_area, row, space, text}; -use crate::socket::{Socket, SocketConnection, SocketData}; +use crate::socket::Socket; +use crate::graph::GraphMessage; +use crate::Message; -#[derive(Default)] -pub struct Nodes { - pub nodes: Vec, - pub sockets: Vec, - pub connections: Vec, - pub current_drag_socket: Option, -} - -impl Nodes { - pub fn update(&mut self, message: NodeMessage) { - match message { - NodeMessage::MoveNode(node, point) => self.nodes[node].position = point, - NodeMessage::SocketDragStart(socket) => { - self.current_drag_socket = Some(socket); - }, - NodeMessage::SocketDragEnd(socket) => { - if let Some(first_socket) = self.current_drag_socket { - if self.sockets[first_socket].compatable(&self.sockets[socket]){ - self.toggle_connection(first_socket, socket); - } - self.current_drag_socket = None; - } - }, - } - } - pub fn view(&self) -> Element<'_, crate::Message> { - todo!(); - } - - pub fn toggle_connection(&mut self, from: usize, to: usize) { - let connection = SocketConnection(from, to); - let found = self.connections.iter().position(|el| *el == connection); - if let Some(found_index) = found { - self.connections.remove(found_index); - } - else { - self.connections.push(connection); - } - } -} - -pub enum NodeMessage { - MoveNode(usize, Point), - SocketDragStart(usize), - SocketDragEnd(usize), -} +#[derive(Clone)] pub struct Node { pub name: String, pub inputs: Vec, pub outputs: Vec, pub position: Point, - pub function: Box Vec> } -pub fn node(from: &Node, node_index: usize) -> Element<'_,crate::Message> { - todo!(); +impl Node { + pub fn new() -> Self { + Self { name: "Example Node".to_string(), + inputs: vec![], + outputs: vec![], + position: Point::new(0., 0.), + } + } + pub fn view<'a>(&self, sockets: &'a [Socket], index: usize) -> Element<'a, Message> { + let title = text!("{}", self.name); + + let header = mouse_area(container(title) + .padding(6) + .width(Length::Fill) + .style(rounded_box)) + .on_press(Message::GraphMessage(GraphMessage::NodeStartDrag(index))) + .on_move(move |point| Message::GraphMessage(GraphMessage::NodeDrag(point, index))) + .on_release(Message::GraphMessage(GraphMessage::NodeStopDrag)); + + let inputs = Column::with_children( + self.inputs.iter().map(|socket_id| + sockets[*socket_id].view() + ) + ).spacing(4); + let outputs = Column::with_children( + self.outputs.iter().map(|socket_id| + sockets[*socket_id].view() + ) + ).spacing(4); + + let body = row![inputs, space().width(Length::Fixed(40.)), outputs]; + + let content = column![header,body].spacing(8).padding(8); + container(content) + .style(bordered_box) + .width(Length::Fixed(100.)) + .into() + } + pub fn position(mut self, point: Point) -> Self { + self.position = point; + self + } + + pub fn title(mut self, text: impl Into) -> Self { + self.name = text.into(); + self + } } diff --git a/src/socket.rs b/src/socket.rs index c9d5dbe..252957f 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -1,3 +1,5 @@ +use iced::{Element, widget::{Container, Space, container::rounded_box}}; + pub struct Socket { pub name: Option, pub kind: SocketType, @@ -5,6 +7,13 @@ pub struct Socket { } impl Socket { + pub fn view(&self) -> Element<'_, crate::Message> { + Container::new(Space::new()) + .width(4) + .height(4) + .style(rounded_box) + .into() + } pub fn compatable(&self, to: &Socket) -> bool { self.data.compatable(&to.data) }