252 lines
7.6 KiB
Text
252 lines
7.6 KiB
Text
import { ScrollView, HorizontalBox, Button } from "std-widgets.slint";
|
|
|
|
struct Port {
|
|
x: length,
|
|
y: length,
|
|
name: string,
|
|
}
|
|
|
|
struct Node {
|
|
x: length,
|
|
y: length,
|
|
z: float,
|
|
ports-width: [length],
|
|
text: string,
|
|
in-ports: [Port],
|
|
out-ports: [Port],
|
|
}
|
|
|
|
component Node inherits Rectangle {
|
|
in-out property <[Node]> nodes;
|
|
in property <int> idx;
|
|
private property <Node> node: nodes[idx];
|
|
callback port_clicked(int, bool);
|
|
|
|
x: node.x;
|
|
y: node.y;
|
|
|
|
width: self.preferred-width;
|
|
height: self.preferred-height;
|
|
|
|
drop-shadow-blur: 5px;
|
|
drop-shadow-color: black;
|
|
|
|
border-radius: 8px;
|
|
|
|
VerticalLayout {
|
|
spacing: -heading.border-width;
|
|
|
|
heading := Rectangle {
|
|
border-radius: root.border-radius;
|
|
border-bottom-right-radius: self.border-bottom-left-radius;
|
|
border-bottom-left-radius: {
|
|
if node.in-ports.length == 0 && node.out-ports.length == 0 {
|
|
root.border-radius
|
|
} else {
|
|
0px
|
|
}
|
|
};
|
|
|
|
background: #383838;
|
|
border-width: 1px;
|
|
border-color: white.with-alpha(0.15);
|
|
|
|
HorizontalLayout {
|
|
padding: 5px;
|
|
spacing: 10px;
|
|
|
|
Text {
|
|
color: white;
|
|
text: node.text;
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
background: #303030;
|
|
border-width: heading.border-width;
|
|
border-color: heading.border-color;
|
|
|
|
border-bottom-right-radius: root.border-radius;
|
|
border-bottom-left-radius: self.border-bottom-right-radius;
|
|
|
|
HorizontalLayout {
|
|
for ports[idx] in [node.in-ports, node.out-ports]: ports-layout := VerticalLayout {
|
|
private property <bool> is-input: idx == 0;
|
|
|
|
alignment: start;
|
|
padding-top: 5px;
|
|
padding-bottom: 5px;
|
|
spacing: 5px;
|
|
|
|
for idx in ports.length: port-layout := HorizontalLayout {
|
|
alignment: is-input ? start : end;
|
|
x: 15px / 2 * (is-input ? -1 : 1);
|
|
Rectangle {
|
|
area := TouchArea {
|
|
clicked => {
|
|
root.port_clicked(idx, is-input);
|
|
}
|
|
}
|
|
|
|
border-radius: 3px;
|
|
|
|
background: #f8e45c;
|
|
|
|
min-width: 15px;
|
|
height: 20px;
|
|
|
|
drop-shadow-blur: 5px;
|
|
drop-shadow-color: black;
|
|
|
|
HorizontalLayout {
|
|
padding: 2px;
|
|
Text {
|
|
color: black;
|
|
horizontal-alignment: center;
|
|
text: ports[idx].name;
|
|
}
|
|
}
|
|
|
|
init => {
|
|
ports[idx].y = //
|
|
heading.preferred-height//
|
|
+ ports-layout.padding-top//
|
|
+ port-layout.preferred-height / 2//
|
|
+ (ports-layout.spacing + port-layout.preferred-height) * idx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for idx in node.out-ports.length: Rectangle {
|
|
init => {
|
|
node.out-ports[idx].x = root.preferred-width;
|
|
}
|
|
}
|
|
|
|
TouchArea {
|
|
moved => {
|
|
if self.pressed {
|
|
nodes[idx].x += self.mouse-x - self.pressed-x;
|
|
nodes[idx].y += self.mouse-y - self.pressed-y;
|
|
}
|
|
}
|
|
mouse-cursor: move;
|
|
}
|
|
}
|
|
|
|
struct Link { x1: length, x2: length, y1: length, y2: length, color: brush, }
|
|
|
|
component Link inherits Path {
|
|
in property <Link> link;
|
|
|
|
viewbox-width: self.width / 1px;
|
|
viewbox-height: self.height / 1px;
|
|
|
|
stroke: link.color;
|
|
stroke-width: 5px;
|
|
|
|
MoveTo {
|
|
x: link.x1 / 1px;
|
|
y: link.y1 / 1px;
|
|
}
|
|
|
|
// LineTo {
|
|
// x: link.x2 / 1px;
|
|
// y: link.y2 / 1px;
|
|
// }
|
|
|
|
private property <int> h: link.y2 / 1px - link.y1 / 1px;
|
|
private property <int> w: link.x2 / 1px - link.x1 / 1px;
|
|
|
|
private property <{ x: int, y: int}> c1: {
|
|
x: max(abs(h / 2), abs(w / 2)),
|
|
y: max(h / 4, -w / 4),
|
|
};
|
|
private property <{ x: int, y: int}> c2: {
|
|
x: max(abs(h / 2), abs(w / 2)),
|
|
y: min(h / 4, w / 4 + abs(h)),
|
|
};
|
|
|
|
CubicTo {
|
|
x: link.x2 / 1px;
|
|
y: link.y2 / 1px;
|
|
control-1-x: link.x1 / 1px + c1.x;
|
|
control-1-y: link.y1 / 1px + c1.y;
|
|
control-2-x: link.x2 / 1px - c2.x;
|
|
control-2-y: link.y2 / 1px - c2.y;
|
|
}
|
|
}
|
|
|
|
struct Connection { node1: int, port1: int, node2: int, port2: int, color: brush}
|
|
|
|
export component MainWindow inherits Window {
|
|
|
|
background: #242424;
|
|
|
|
min-width: 800px;
|
|
min-height: 400px;
|
|
|
|
in-out property window_title <=> self.title;
|
|
in-out property <[Node]> nodes;
|
|
in-out property <[Connection]> connections;
|
|
property <[Link]> links;
|
|
property <Connection> PendingConnection: { node1: -1, port1: -1, node2: -1, port2: -1, color: #ffa500 };
|
|
|
|
callback add_connection_helper(Connection);
|
|
|
|
preferred-height: 500px;
|
|
preferred-width: 500px;
|
|
|
|
public pure function create-link-object(start: { node: int, port: int}, end: { node: int, port: int}, color: brush) -> Link {
|
|
{
|
|
x1: nodes[start.node].x + nodes[start.node].out-ports[start.port].x,
|
|
y1: nodes[start.node].y + nodes[start.node].out-ports[start.port].y,
|
|
x2: nodes[end.node].x + nodes[end.node].in-ports[end.port].x,
|
|
y2: nodes[end.node].y + nodes[end.node].in-ports[end.port].y,
|
|
color: color,
|
|
}
|
|
}
|
|
|
|
VerticalLayout {
|
|
ScrollView {
|
|
for c in connections: Link {
|
|
link: root.create-link-object({ node: c.node1, port: c.port1 }, { node: c.node2, port: c.port2 }, c.color);
|
|
}
|
|
|
|
for node[idx] in nodes: Node {
|
|
nodes <=> nodes;
|
|
idx: idx;
|
|
port_clicked(port, is-input) => {
|
|
debug("Node", idx, "Port", port, "Input", is-input);
|
|
if (root.PendingConnection.node1 == -1) {
|
|
if (!is-input) {
|
|
root.PendingConnection.node1 = idx;
|
|
root.PendingConnection.port1 = port;
|
|
root.PendingConnection.color = #ffa500;
|
|
} else {
|
|
root.PendingConnection.node1 = -1;
|
|
}
|
|
} else if (root.PendingConnection.node1 != idx) {
|
|
if (is-input) {
|
|
root.PendingConnection.node2 = idx;
|
|
root.PendingConnection.port2 = port;
|
|
// Use rust code to add the connection because we can't do it in pure slint
|
|
// Ugh
|
|
root.add_connection_helper(root.PendingConnection);
|
|
}
|
|
root.PendingConnection.node1 = -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
height: 10%;
|
|
}
|
|
}
|
|
}
|