feat: Node view and node spawn button
This commit is contained in:
parent
502a15273f
commit
1e2b1aec40
4 changed files with 140 additions and 57 deletions
64
src/graph.rs
Normal file
64
src/graph.rs
Normal file
|
|
@ -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<Node>,
|
||||||
|
pub sockets: Vec<Socket>,
|
||||||
|
pub connections: Vec<SocketConnection>,
|
||||||
|
pub current_drag_socket: Option<usize>,
|
||||||
|
pub current_drag_node: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Element<'_, crate::Message>> = 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),
|
||||||
|
}
|
||||||
19
src/main.rs
19
src/main.rs
|
|
@ -1,26 +1,33 @@
|
||||||
mod node;
|
mod node;
|
||||||
mod socket;
|
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)]
|
#[derive(Default)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
nodes: Nodes
|
graph: Graph
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
NodeMessage(NodeMessage)
|
GraphMessage(GraphMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
pub fn view(&self) -> Element<'_, Message> {
|
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) {
|
pub fn update(&mut self, message: Message) {
|
||||||
match message {
|
match message {
|
||||||
Message::NodeMessage(message) => self.nodes.update(message)
|
Message::GraphMessage(message) => self.graph.update(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
105
src/node.rs
105
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<Node>,
|
|
||||||
pub sockets: Vec<Socket>,
|
|
||||||
pub connections: Vec<SocketConnection>,
|
|
||||||
pub current_drag_socket: Option<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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 struct Node {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub inputs: Vec<usize>,
|
pub inputs: Vec<usize>,
|
||||||
pub outputs: Vec<usize>,
|
pub outputs: Vec<usize>,
|
||||||
pub position: Point,
|
pub position: Point,
|
||||||
pub function: Box<dyn Fn([&SocketData]) -> Vec<SocketData>>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn node(from: &Node, node_index: usize) -> Element<'_,crate::Message> {
|
impl Node {
|
||||||
todo!();
|
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<String>) -> Self {
|
||||||
|
self.name = text.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use iced::{Element, widget::{Container, Space, container::rounded_box}};
|
||||||
|
|
||||||
pub struct Socket {
|
pub struct Socket {
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub kind: SocketType,
|
pub kind: SocketType,
|
||||||
|
|
@ -5,6 +7,13 @@ pub struct Socket {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
pub fn compatable(&self, to: &Socket) -> bool {
|
||||||
self.data.compatable(&to.data)
|
self.data.compatable(&to.data)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue