graphic design tool
git clone https://git.lucas.co/cce-designer.git
Update design interface node configurations, geometry, render, and project logic
default_project.json | 96 ++-
nodes/add.json | 12 +-
nodes/box.json | 38 ++
nodes/camera.json | 4 +-
nodes/input.json | 7 +
nodes/line.json | 4 +-
nodes/opencl.json | 8 +-
nodes/output.json | 9 +
nodes/scatter.json | 25 +
nodes/sphere.json | 27 +-
nodes/subnet.json | 9 +
nodes/transform.json | 4 +-
project.json | 96 ++-
scratch/test_errors.log | 1507 +++++++++++++++++++++++++++++++++++++++++++++
scratch/test_errors2.log | 1044 +++++++++++++++++++++++++++++++
scratch/test_selection.py | 29 +
src/app.rs | 1364 +++++++++++++++++++++++-----------------
src/geometry.rs | 739 +++++++++++++++++++++-
src/main.rs | 217 ++++++-
src/project.rs | 364 +++++------
src/render.rs | 242 +++++++-
src/window.rs | 101 +--
22 files changed, 5002 insertions(+), 944 deletions(-)
diff --git a/default_project.json b/default_project.json
index 5ba33f6..3add471 100644
--- a/default_project.json
+++ b/default_project.json
@@ -47,8 +47,11 @@
},
{
"name": "Sphere 1",
- "type": "sphere",
- "children": [],
+ "type": "node",
+ "position": [
+ 4.0,
+ 2.0
+ ],
"params": [
{
"name": "Radius",
@@ -61,40 +64,69 @@
"step": null
}
],
- "position": [
- 4.0,
- 2.0
- ]
- },
- {
- "name": "Transform 1",
- "type": "transform",
- "children": [],
- "params": [
+ "children": [
{
- "name": "Input",
- "label": "",
- "type": "text",
- "default": "Sphere 1",
- "options": [],
- "min": null,
- "max": null,
- "step": null
+ "name": "opencl1",
+ "type": "opencl",
+ "position": [
+ 4.0,
+ 2.0
+ ],
+ "params": [
+ {
+ "name": "Input",
+ "label": "",
+ "type": "text",
+ "default": "",
+ "options": [],
+ "min": null,
+ "max": null,
+ "step": null
+ },
+ {
+ "name": "Code",
+ "label": "",
+ "type": "code",
+ "default": "__kernel void process(__global const float* in_pos, __global const float* in_col, int in_count, __global float* out_pos, __global float* out_col, __global int* out_count, int max_vertices) {\n int id = get_global_id(0);\n if (id == 0) {\n float radius = chf(\"Radius\", 0.5f);\n float center_x = 0.0f;\n float center_y = 0.55f;\n float center_z = 0.0f;\n int lat_steps = 16;\n int lon_steps = 24;\n int count = 0;\n for (int lat = 0; lat < lat_steps; lat++) {\n float theta0 = 3.14159265f * (float)lat / (float)lat_steps;\n float theta1 = 3.14159265f * (float)(lat + 1) / (float)lat_steps;\n for (int lon = 0; lon < lon_steps; lon++) {\n float phi0 = 6.2831853f * (float)lon / (float)lon_steps;\n float phi1 = 6.2831853f * (float)(lon + 1) / (float)lon_steps;\n float x00 = radius * sin(theta0) * cos(phi0);\n float y00 = radius * cos(theta0);\n float z00 = radius * sin(theta0) * sin(phi0);\n float x10 = radius * sin(theta1) * cos(phi0);\n float y10 = radius * cos(theta1);\n float z10 = radius * sin(theta1) * sin(phi0);\n float x11 = radius * sin(theta1) * cos(phi1);\n float y11 = radius * cos(theta1);\n float z11 = radius * sin(theta1) * sin(phi1);\n float x01 = radius * sin(theta0) * cos(phi1);\n float y01 = radius * cos(theta0);\n float z01 = radius * sin(theta0) * sin(phi1);\n float px[6] = {x00, x10, x11, x00, x11, x01};\n float py[6] = {y00, y10, y11, y00, y11, y01};\n float pz[6] = {z00, z10, z11, z00, z11, z01};\n for (int v = 0; v < 6; v++) {\n int idx = count++;\n if (idx < max_vertices) {\n out_pos[idx * 3 + 0] = center_x + px[v];\n out_pos[idx * 3 + 1] = center_y + py[v];\n out_pos[idx * 3 + 2] = center_z + pz[v];\n float nx = px[v];\n float ny = py[v];\n float nz = pz[v];\n float len = sqrt(nx*nx + ny*ny + nz*nz);\n if (len > 0.0f) {\n nx /= len;\n ny /= len;\n nz /= len;\n }\n out_col[idx * 3 + 0] = 0.35f + fabs(nx) * 0.35f;\n out_col[idx * 3 + 1] = 0.45f + fabs(ny) * 0.35f;\n out_col[idx * 3 + 2] = 0.85f;\n }\n }\n }\n }\n *out_count = count;\n }\n}",
+ "options": [],
+ "min": null,
+ "max": null,
+ "step": null
+ },
+ {
+ "name": "Update Parameters",
+ "label": "",
+ "type": "button",
+ "default": "",
+ "options": [],
+ "min": null,
+ "max": null,
+ "step": null
+ }
+ ],
+ "children": []
},
{
- "name": "Translation",
- "label": "",
- "type": "float3",
- "default": "1.00:0.50:0.00",
- "options": [],
- "min": -10.0,
- "max": 10.0,
- "step": null
+ "name": "output1",
+ "type": "output",
+ "position": [
+ 4.0,
+ 3.0
+ ],
+ "params": [
+ {
+ "name": "Input",
+ "label": "",
+ "type": "text",
+ "default": "opencl1",
+ "options": [],
+ "min": null,
+ "max": null,
+ "step": null
+ }
+ ],
+ "children": []
}
- ],
- "position": [
- 4.0,
- 3.0
]
}
],
diff --git a/nodes/add.json b/nodes/add.json
index a1995a8..2d1ab26 100644
--- a/nodes/add.json
+++ b/nodes/add.json
@@ -1,14 +1,16 @@
{
- "name": "Add 1",
+ "name": "Add",
"type": "add",
+ "inputs": 0,
+ "outputs": 1,
"params": [
{
"name": "Points",
- "default": "100",
"type": "spinbox",
- "min": 1.0,
- "max": 1000.0,
- "step": 1.0
+ "default": "100",
+ "min": 10.0,
+ "max": 5000.0,
+ "step": 10.0
}
]
}
diff --git a/nodes/box.json b/nodes/box.json
new file mode 100644
index 0000000..fd45e8e
--- /dev/null
+++ b/nodes/box.json
@@ -0,0 +1,38 @@
+{
+ "name": "Box",
+ "type": "node",
+ "inputs": 1,
+ "outputs": 1,
+ "params": [
+ { "name": "Input", "default": "", "type": "text" },
+ { "name": "Scale", "default": "1.0", "type": "slider" },
+ { "name": "Wireframe", "default": "false", "type": "toggle" }
+ ],
+ "children": [
+ {
+ "name": "input1",
+ "type": "input",
+ "position": [4.0, 1.0]
+ },
+ {
+ "name": "opencl1",
+ "type": "opencl",
+ "params": [
+ { "name": "Input", "default": "input1" },
+ {
+ "name": "Code",
+ "default": "void add_box(float cx, float cy, float cz, float hx, float hy, float hz, __global float* out_pos, __global float* out_col, int* count, int max_vertices) {\n float temp_pos[108] = {\n -hx, -hy, hz, hx, -hy, hz, hx, hy, hz,\n -hx, -hy, hz, hx, hy, hz, -hx, hy, hz,\n -hx, -hy, -hz, -hx, hy, -hz, hx, hy, -hz,\n -hx, -hy, -hz, hx, hy, -hz, hx, -hy, -hz,\n -hx, -hy, -hz, -hx, -hy, hz, -hx, hy, hz,\n -hx, -hy, -hz, -hx, hy, hz, -hx, hy, -hz,\n hx, -hy, -hz, hx, hy, -hz, hx, hy, hz,\n hx, -hy, -hz, hx, hy, hz, hx, -hy, hz,\n -hx, hy, -hz, -hx, hy, hz, hx, hy, hz,\n -hx, hy, -hz, hx, hy, hz, hx, hy, -hz,\n -hx, -hy, -hz, hx, -hy, -hz, hx, -hy, hz,\n -hx, -hy, -hz, hx, -hy, hz, -hx, -hy, hz\n };\n int start = *count;\n for (int i = 0; i < 36; i++) {\n int idx = start + i;\n if (idx < max_vertices) {\n out_pos[idx * 3 + 0] = cx + temp_pos[i * 3 + 0];\n out_pos[idx * 3 + 1] = cy + temp_pos[i * 3 + 1];\n out_pos[idx * 3 + 2] = cz + temp_pos[i * 3 + 2];\n out_col[idx * 3 + 0] = 0.8f;\n out_col[idx * 3 + 1] = 0.2f;\n out_col[idx * 3 + 2] = 0.2f;\n }\n }\n *count = start + 36;\n}\n\n__kernel void process(__global const float* in_pos, __global const float* in_col, int in_count, __global float* out_pos, __global float* out_col, __global int* out_count, int max_vertices) {\n int id = get_global_id(0);\n if (id == 0) {\n float scale = chf(\"Scale\", 1.0f);\n int wireframe = chb(\"Wireframe\", 0);\n float dx = 0.5f * scale;\n float dy = 0.5f * scale;\n float dz = 0.5f * scale;\n float center_x = 0.0f;\n float center_y = 0.55f;\n float center_z = 0.0f;\n int count = 0;\n if (wireframe) {\n float t_line = 0.008f * scale;\n float t_corner = 0.012f * scale;\n for (int ix = 0; ix < 2; ix++) {\n float cx = center_x + (ix == 0 ? -dx : dx);\n for (int iy = 0; iy < 2; iy++) {\n float cy = center_y + (iy == 0 ? -dy : dy);\n for (int iz = 0; iz < 2; iz++) {\n float cz = center_z + (iz == 0 ? -dz : dz);\n add_box(cx, cy, cz, t_corner, t_corner, t_corner, out_pos, out_col, &count, max_vertices);\n }\n }\n }\n for (int iy = 0; iy < 2; iy++) {\n float cy = center_y + (iy == 0 ? -dy : dy);\n for (int iz = 0; iz < 2; iz++) {\n float cz = center_z + (iz == 0 ? -dz : dz);\n add_box(center_x, cy, cz, dx, t_line, t_line, out_pos, out_col, &count, max_vertices);\n }\n }\n for (int ix = 0; ix < 2; ix++) {\n float cx = center_x + (ix == 0 ? -dx : dx);\n for (int iz = 0; iz < 2; iz++) {\n float cz = center_z + (iz == 0 ? -dz : dz);\n add_box(cx, center_y, cz, t_line, dy, t_line, out_pos, out_col, &count, max_vertices);\n }\n }\n for (int ix = 0; ix < 2; ix++) {\n float cx = center_x + (ix == 0 ? -dx : dx);\n for (int iy = 0; iy < 2; iy++) {\n float cy = center_y + (iy == 0 ? -dy : dy);\n add_box(cx, cy, center_z, t_line, t_line, dz, out_pos, out_col, &count, max_vertices);\n }\n }\n } else {\n add_box(center_x, center_y, center_z, dx, dy, dz, out_pos, out_col, &count, max_vertices);\n }\n *out_count = count;\n }\n}"
+ }
+ ],
+ "position": [4.0, 2.0]
+ },
+ {
+ "name": "output1",
+ "type": "output",
+ "params": [
+ { "name": "Input", "default": "opencl1" }
+ ],
+ "position": [4.0, 3.0]
+ }
+ ]
+}
diff --git a/nodes/camera.json b/nodes/camera.json
index 59b3324..50c3f14 100644
--- a/nodes/camera.json
+++ b/nodes/camera.json
@@ -1,6 +1,8 @@
{
- "name": "Camera 1",
+ "name": "Camera",
"type": "camera",
+ "inputs": 0,
+ "outputs": 0,
"params": [
{ "name": "Position", "type": "float3", "default": "2.50:1.80:2.50", "min": -10.0, "max": 10.0 },
{ "name": "Rotation", "type": "float3", "default": "0.00:0.00:0.00", "min": -180.0, "max": 180.0 },
diff --git a/nodes/input.json b/nodes/input.json
new file mode 100644
index 0000000..1b48b51
--- /dev/null
+++ b/nodes/input.json
@@ -0,0 +1,7 @@
+{
+ "name": "Input",
+ "type": "input",
+ "inputs": 0,
+ "outputs": 1,
+ "params": []
+}
diff --git a/nodes/line.json b/nodes/line.json
index bed2c6c..c512f8d 100644
--- a/nodes/line.json
+++ b/nodes/line.json
@@ -1,6 +1,8 @@
{
- "name": "Line 1",
+ "name": "Line",
"type": "line",
+ "inputs": 0,
+ "outputs": 1,
"params": [
{ "name": "Length", "default": "1.0", "type": "slider" },
{ "name": "Thickness", "default": "0.02", "type": "slider" }
diff --git a/nodes/opencl.json b/nodes/opencl.json
index e65898f..8d913f8 100644
--- a/nodes/opencl.json
+++ b/nodes/opencl.json
@@ -1,11 +1,15 @@
{
- "name": "OpenCL 1",
+ "name": "OpenCL",
"type": "opencl",
+ "inputs": 1,
+ "outputs": 1,
"params": [
+ { "name": "Input", "default": "", "type": "text" },
{
"name": "Code",
"type": "code",
"default": "__kernel void process(__global float* pos, __global float* col, int count) {\n int id = get_global_id(0);\n if (id < count) {\n // Default deformation kernel (sine wave deforming Y based on X)\n pos[id * 3 + 1] += sin(pos[id * 3] * 4.0f) * 0.15f;\n }\n}"
- }
+ },
+ { "name": "Update Parameters", "type": "button", "default": "" }
]
}
diff --git a/nodes/output.json b/nodes/output.json
new file mode 100644
index 0000000..d0a33e7
--- /dev/null
+++ b/nodes/output.json
@@ -0,0 +1,9 @@
+{
+ "name": "Output",
+ "type": "output",
+ "inputs": 1,
+ "outputs": 0,
+ "params": [
+ { "name": "Input", "default": "", "type": "text" }
+ ]
+}
diff --git a/nodes/scatter.json b/nodes/scatter.json
new file mode 100644
index 0000000..269a284
--- /dev/null
+++ b/nodes/scatter.json
@@ -0,0 +1,25 @@
+{
+ "name": "Scatter",
+ "type": "scatter",
+ "inputs": 1,
+ "outputs": 1,
+ "params": [
+ { "name": "Input", "default": "", "type": "text" },
+ {
+ "name": "Points",
+ "type": "spinbox",
+ "default": "100",
+ "min": 10.0,
+ "max": 5000.0,
+ "step": 10.0
+ },
+ {
+ "name": "Radius",
+ "type": "slider",
+ "default": "0.02",
+ "min": 0.005,
+ "max": 0.1,
+ "step": 0.005
+ }
+ ]
+}
diff --git a/nodes/sphere.json b/nodes/sphere.json
index be69a94..0a5409a 100644
--- a/nodes/sphere.json
+++ b/nodes/sphere.json
@@ -1,7 +1,30 @@
{
- "name": "Sphere 1",
- "type": "sphere",
+ "name": "Sphere",
+ "type": "node",
+ "inputs": 0,
+ "outputs": 1,
"params": [
{ "name": "Radius", "default": "0.5", "type": "slider" }
+ ],
+ "children": [
+ {
+ "name": "opencl1",
+ "type": "opencl",
+ "params": [
+ {
+ "name": "Code",
+ "default": "__kernel void process(__global const float* in_pos, __global const float* in_col, int in_count, __global float* out_pos, __global float* out_col, __global int* out_count, int max_vertices) {\n int id = get_global_id(0);\n if (id == 0) {\n float radius = chf(\"Radius\", 0.5f);\n float center_x = 0.0f;\n float center_y = 0.55f;\n float center_z = 0.0f;\n int lat_steps = 16;\n int lon_steps = 24;\n int count = 0;\n for (int lat = 0; lat < lat_steps; lat++) {\n float theta0 = 3.14159265f * (float)lat / (float)lat_steps;\n float theta1 = 3.14159265f * (float)(lat + 1) / (float)lat_steps;\n for (int lon = 0; lon < lon_steps; lon++) {\n float phi0 = 6.2831853f * (float)lon / (float)lon_steps;\n float phi1 = 6.2831853f * (float)(lon + 1) / (float)lon_steps;\n float x00 = radius * sin(theta0) * cos(phi0);\n float y00 = radius * cos(theta0);\n float z00 = radius * sin(theta0) * sin(phi0);\n float x10 = radius * sin(theta1) * cos(phi0);\n float y10 = radius * cos(theta1);\n float z10 = radius * sin(theta1) * sin(phi0);\n float x11 = radius * sin(theta1) * cos(phi1);\n float y11 = radius * cos(theta1);\n float z11 = radius * sin(theta1) * sin(phi1);\n float x01 = radius * sin(theta0) * cos(phi1);\n float y01 = radius * cos(theta0);\n float z01 = radius * sin(theta0) * sin(phi1);\n float px[6] = {x00, x10, x11, x00, x11, x01};\n float py[6] = {y00, y10, y11, y00, y11, y01};\n float pz[6] = {z00, z10, z11, z00, z11, z01};\n for (int v = 0; v < 6; v++) {\n int idx = count++;\n if (idx < max_vertices) {\n out_pos[idx * 3 + 0] = center_x + px[v];\n out_pos[idx * 3 + 1] = center_y + py[v];\n out_pos[idx * 3 + 2] = center_z + pz[v];\n float nx = px[v];\n float ny = py[v];\n float nz = pz[v];\n float len = sqrt(nx*nx + ny*ny + nz*nz);\n if (len > 0.0f) {\n nx /= len;\n ny /= len;\n nz /= len;\n }\n out_col[idx * 3 + 0] = 0.35f + fabs(nx) * 0.35f;\n out_col[idx * 3 + 1] = 0.45f + fabs(ny) * 0.35f;\n out_col[idx * 3 + 2] = 0.85f;\n }\n }\n }\n }\n *out_count = count;\n }\n}"
+ }
+ ],
+ "position": [4.0, 2.0]
+ },
+ {
+ "name": "output1",
+ "type": "output",
+ "params": [
+ { "name": "Input", "default": "opencl1" }
+ ],
+ "position": [4.0, 3.0]
+ }
]
}
diff --git a/nodes/subnet.json b/nodes/subnet.json
new file mode 100644
index 0000000..df329ae
--- /dev/null
+++ b/nodes/subnet.json
@@ -0,0 +1,9 @@
+{
+ "name": "Subnet",
+ "type": "node",
+ "inputs": 1,
+ "outputs": 1,
+ "params": [
+ { "name": "Input", "default": "", "type": "text" }
+ ]
+}
diff --git a/nodes/transform.json b/nodes/transform.json
index cce62ca..fde13df 100644
--- a/nodes/transform.json
+++ b/nodes/transform.json
@@ -1,6 +1,8 @@
{
- "name": "Transform 1",
+ "name": "Transform",
"type": "transform",
+ "inputs": 1,
+ "outputs": 1,
"params": [
{ "name": "Input", "default": "", "type": "text" },
{ "name": "Translation", "default": "0.00:0.00:0.00", "type": "float3", "min": -10.0, "max": 10.0 }
diff --git a/project.json b/project.json
index 3e53fbe..1b466ca 100644
--- a/project.json
+++ b/project.json
@@ -47,8 +47,11 @@
},
{
"name": "Sphere 1",
- "type": "sphere",
- "children": [],
+ "type": "node",
+ "position": [
+ 4.0,
+ 2.0
+ ],
"params": [
{
"name": "Radius",
@@ -61,40 +64,69 @@
"step": null
}
],
- "position": [
- 4.0,
- 2.0
- ]
- },
- {
- "name": "Transform 1",
- "type": "transform",
- "children": [],
- "params": [
+ "children": [
{
- "name": "Input",
- "label": "",
- "type": "text",
- "default": "Sphere 1",
- "options": [],
- "min": null,
- "max": null,
- "step": null
+ "name": "opencl1",
+ "type": "opencl",
+ "position": [
+ 4.0,
+ 2.0
+ ],
+ "params": [
+ {
+ "name": "Input",
+ "label": "",
+ "type": "text",
+ "default": "",
+ "options": [],
+ "min": null,
+ "max": null,
+ "step": null
+ },
+ {
+ "name": "Code",
+ "label": "",
+ "type": "code",
+ "default": "__kernel void process(__global const float* in_pos, __global const float* in_col, int in_count, __global float* out_pos, __global float* out_col, __global int* out_count, int max_vertices) {\n int id = get_global_id(0);\n if (id == 0) {\n float radius = chf(\"Radius\", 0.5f);\n float center_x = 0.0f;\n float center_y = 0.55f;\n float center_z = 0.0f;\n int lat_steps = 16;\n int lon_steps = 24;\n int count = 0;\n for (int lat = 0; lat < lat_steps; lat++) {\n float theta0 = 3.14159265f * (float)lat / (float)lat_steps;\n float theta1 = 3.14159265f * (float)(lat + 1) / (float)lat_steps;\n for (int lon = 0; lon < lon_steps; lon++) {\n float phi0 = 6.2831853f * (float)lon / (float)lon_steps;\n float phi1 = 6.2831853f * (float)(lat + 1) / (float)lon_steps;\n float x00 = radius * sin(theta0) * cos(phi0);\n float y00 = radius * cos(theta0);\n float z00 = radius * sin(theta0) * sin(phi0);\n float x10 = radius * sin(theta1) * cos(phi0);\n float y10 = radius * cos(theta1);\n float z10 = radius * sin(theta1) * sin(phi0);\n float x11 = radius * sin(theta1) * cos(phi1);\n float y11 = radius * cos(theta1);\n float z11 = radius * sin(theta1) * sin(phi1);\n float x01 = radius * sin(theta0) * cos(phi1);\n float y01 = radius * cos(theta0);\n float z01 = radius * sin(theta0) * sin(phi1);\n float px[6] = {x00, x10, x11, x00, x11, x01};\n float py[6] = {y00, y10, y11, y00, y11, y01};\n float pz[6] = {z00, z10, z11, z00, z11, z01};\n for (int v = 0; v < 6; v++) {\n int idx = count++;\n if (idx < max_vertices) {\n out_pos[idx * 3 + 0] = center_x + px[v];\n out_pos[idx * 3 + 1] = center_y + py[v];\n out_pos[idx * 3 + 2] = center_z + pz[v];\n float nx = px[v];\n float ny = py[v];\n float nz = pz[v];\n float len = sqrt(nx*nx + ny*ny + nz*nz);\n if (len > 0.0f) {\n nx /= len;\n ny /= len;\n nz /= len;\n }\n out_col[idx * 3 + 0] = 0.35f + fabs(nx) * 0.35f;\n out_col[idx * 3 + 1] = 0.45f + fabs(ny) * 0.35f;\n out_col[idx * 3 + 2] = 0.85f;\n }\n }\n }\n }\n *out_count = count;\n }\n}",
+ "options": [],
+ "min": null,
+ "max": null,
+ "step": null
+ },
+ {
+ "name": "Update Parameters",
+ "label": "",
+ "type": "button",
+ "default": "",
+ "options": [],
+ "min": null,
+ "max": null,
+ "step": null
+ }
+ ],
+ "children": []
},
{
- "name": "Translation",
- "label": "",
- "type": "float3",
- "default": "1.00:0.50:0.00",
- "options": [],
- "min": -10.0,
- "max": 10.0,
- "step": null
+ "name": "output1",
+ "type": "output",
+ "position": [
+ 4.0,
+ 3.0
+ ],
+ "params": [
+ {
+ "name": "Input",
+ "label": "",
+ "type": "text",
+ "default": "opencl1",
+ "options": [],
+ "min": null,
+ "max": null,
+ "step": null
+ }
+ ],
+ "children": []
}
- ],
- "position": [
- 4.0,
- 3.0
]
}
],
diff --git a/scratch/test_errors.log b/scratch/test_errors.log
new file mode 100644
index 0000000..2c019a6
--- /dev/null
+++ b/scratch/test_errors.log
@@ -0,0 +1,1507 @@
+warning: unused import: `KeyEvent`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/json_layout.rs:3:5
+ |
+3 | KeyEvent, MouseButton, ElementState, focus, Slider, Event, UiContext,
+ | ^^^^^^^^
+ |
+ = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default
+
+warning: unused import: `Ordering`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/mod.rs:67:38
+ |
+67 | use std::sync::atomic::{AtomicUsize, Ordering};
+ | ^^^^^^^^
+
+warning: unused import: `wl_shm`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/backend/window_runner.rs:24:61
+ |
+24 | protocol::{wl_keyboard, wl_output, wl_pointer, wl_seat, wl_shm, wl_s...
+ | ^^^^^^
+
+warning: unused imports: `SwashCache`, `TextAtlas`, and `TextRenderer`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/backend/window_runner.rs:34:36
+ |
+34 | Cache, FontSystem, Resolution, SwashCache, TextArea, TextAtlas,
+ | ^^^^^^^^^^ ^^^^^^^^^
+35 | TextBounds, TextRenderer, Viewport, Buffer, Attrs, Metrics,
+ | ^^^^^^^^^^^^
+
+warning: unused imports: `KeyEvent` and `MouseScrollDelta`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/context.rs:2:57
+ |
+2 | ...e, Key, KeyEvent, MouseButton, ElementState, MouseScrollDelta};
+ | ^^^^^^^^ ^^^^^^^^^^^^^^^^
+
+warning: unused import: `crate::widget::TextBox`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/context.rs:5:5
+ |
+5 | use crate::widget::TextBox;
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+warning: variable does not need to be mutable
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/backend/window_runner.rs:925:13
+ |
+925 | ... let mut adapter = WgpuAdapter::new(display_ptr, surface_ptr, pw, ...
+ | ----^^^^^^^
+ | |
+ | help: remove this `mut`
+ |
+ = note: `#[warn(unused_mut)]` (part of `#[warn(unused)]`) on by default
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/json_layout.rs:381:39
+ |
+381 | ...abels_with_bounds(&self, ctx: &UiContext) -> Vec<(TextLabel, Option<...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+ |
+ = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default
+
+warning: unused variable: `x`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/json_layout.rs:440:49
+ |
+440 | Event::MouseButton { button, state, x, y } => {
+ | ^ help: try ignoring the field: `x: _`
+
+warning: unused variable: `y`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/json_layout.rs:440:52
+ |
+440 | Event::MouseButton { button, state, x, y } => {
+ | ^ help: try ignoring the field: `y: _`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/button.rs:98:30
+ |
+98 | fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, ...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/slider.rs:200:75
+ |
+200 | ...Delta, px: f32, py: f32, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/slider.rs:307:52
+ |
+307 | ... self, event: &KeyEvent, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/spinbox.rs:209:52
+ |
+209 | ... self, event: &KeyEvent, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/color_selector.rs:158:34
+ |
+158 | fn tick(&mut self, _dt: f32, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/color_selector.rs:201:52
+ |
+201 | ... self, event: &KeyEvent, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/dropdown.rs:264:52
+ |
+264 | ... self, event: &KeyEvent, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/text_box.rs:565:52
+ |
+565 | ... self, event: &KeyEvent, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/text_box.rs:918:39
+ |
+918 | ...abels_with_bounds(&self, ctx: &UiContext) -> Vec<(TextLabel, Option<...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/text_box.rs:923:48
+ |
+923 | ...h_font_and_bounds(&self, ctx: &UiContext) -> Vec<(TextLabel, Option<...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/trackpad.rs:147:91
+ |
+147 | ...State, px: f32, py: f32, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/button_strip.rs:216:91
+ |
+216 | ...State, px: f32, py: f32, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: value assigned to `next` is never read
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/button_strip.rs:359:24
+ |
+359 | let mut next = current;
+ | ^^^^^^^
+ |
+ = help: maybe it is overwritten before being read?
+ = note: `#[warn(unused_assignments)]` (part of `#[warn(unused)]`) on by default
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/container.rs:25:22
+ |
+25 | fn parent(&self, ctx: &UiContext) -> Option<*mut (dyn Element + 'sta...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/container.rs:26:76
+ |
+26 | ... (dyn Element + 'static)>, ctx: &mut UiContext) { self.parent = paren...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/container.rs:27:24
+ |
+27 | fn children(&self, ctx: &UiContext) -> Vec<*mut (dyn Element + 'stat...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/container.rs:28:66
+ |
+28 | ...t (dyn Element + 'static), ctx: &mut UiContext) { self.children.push(...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/container.rs:29:34
+ |
+29 | ... clear_children(&mut self, ctx: &mut UiContext) { self.children.clear...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/menu.rs:577:23
+ |
+577 | fn focused(&self, ctx: &UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/menu.rs:1258:22
+ |
+1258 | fn parent(&self, ctx: &UiContext) -> Option<*mut (dyn Element + 's...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/menu.rs:1262:76
+ |
+1262 | ...dyn Element + 'static)>, ctx: &mut UiContext) {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/menu.rs:1270:23
+ |
+1270 | fn focused(&self, ctx: &UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/breadcrumb.rs:60:92
+ |
+60 | ...tState, px: f32, _py: f32, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/spreadsheet.rs:233:33
+ |
+233 | fn tick(&mut self, dt: f32, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/scroll_box.rs:68:31
+ |
+68 | fn highlight_color(&self, ctx: &UiContext) -> Option<[f32; 4]> { None }
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/scroll_box.rs:149:52
+ |
+149 | ... self, event: &KeyEvent, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/scroll_box.rs:191:22
+ |
+191 | fn parent(&self, ctx: &UiContext) -> Option<*mut (dyn Element + 'st...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/scroll_box.rs:192:76
+ |
+192 | ...dyn Element + 'static)>, ctx: &mut UiContext) { self.parent = parent; }
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/scroll_box.rs:193:24
+ |
+193 | fn children(&self, ctx: &UiContext) -> Vec<*mut (dyn Element + 'sta...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/scroll_box.rs:194:66
+ |
+194 | ...(dyn Element + 'static), ctx: &mut UiContext) { self.children.push(c...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/scroll_box.rs:195:34
+ |
+195 | ...lear_children(&mut self, ctx: &mut UiContext) { self.children.clear(...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/paginator.rs:719:39
+ |
+719 | ...abels_with_bounds(&self, ctx: &UiContext) -> Vec<(TextLabel, Option<...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/paginator.rs:967:22
+ |
+967 | fn parent(&self, ctx: &UiContext) -> Option<*mut (dyn Element + 'st...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/paginator.rs:971:76
+ |
+971 | ...dyn Element + 'static)>, ctx: &mut UiContext) {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/paginator.rs:975:24
+ |
+975 | fn children(&self, ctx: &UiContext) -> Vec<*mut (dyn Element + 'sta...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/paginator.rs:984:67
+ |
+984 | ...(dyn Element + 'static), ctx: &mut UiContext) {}
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/paginator.rs:985:34
+ |
+985 | fn clear_children(&mut self, ctx: &mut UiContext) {}
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/panel.rs:37:30
+ |
+37 | fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, ...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/float3.rs:101:91
+ |
+101 | ...State, px: f32, py: f32, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/float3.rs:152:52
+ |
+152 | ... self, event: &KeyEvent, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/progress_bar.rs:25:30
+ |
+25 | fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, ...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/list_item.rs:48:30
+ |
+48 | fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, ...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/graph.rs:253:53
+ |
+253 | ... self, px: f32, py: f32, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/graph.rs:267:91
+ |
+267 | ...State, px: f32, py: f32, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/usage_bar.rs:35:30
+ |
+35 | fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, ...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/layout_preview.rs:55:30
+ |
+55 | fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, ...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/font_preview.rs:26:30
+ |
+26 | fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, ...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/info_box.rs:25:30
+ |
+25 | fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, ...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/status_dot.rs:32:30
+ |
+32 | fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, ...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: variable does not need to be mutable
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/layout.rs:1918:31
+ |
+1918 | pub fn with_section_count(mut self, count: usize) -> Self {
+ | ----^^^^
+ | |
+ | help: remove this `mut`
+
+warning: field `min_col_width` is never read
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/layout.rs:1793:5
+ |
+1791 | pub struct GridLayout {
+ | ---------- field in this struct
+1792 | grid: Option<Grid>,
+1793 | min_col_width: f32,
+ | ^^^^^^^^^^^^^
+ |
+ = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default
+
+warning: `clear-ui` (lib) generated 61 warnings (run `cargo fix --lib -p clear-ui` to apply 59 suggestions)
+ Compiling cce-design-interface v0.1.0 (/home/lsgalante/Dropbox/Clear/cce-design-interface)
+error[E0425]: cannot find function `get_next_visible_pane` in this scope
+ --> src/main.rs:606:20
+ |
+606 | assert_eq!(get_next_visible_pane(LEFT_MENUBAR_IDX, true, true, true, false, false), RIGHT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this function
+ |
+392 + use crate::app::get_next_visible_pane;
+ |
+
+error[E0425]: cannot find value `LEFT_MENUBAR_IDX` in this scope
+ --> src/main.rs:606:42
+ |
+606 | assert_eq!(get_next_visible_pane(LEFT_MENUBAR_IDX, true, true, true, false, false), RIGHT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::LEFT_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find value `RIGHT_MENUBAR_IDX` in this scope
+ --> src/main.rs:606:93
+ |
+606 | assert_eq!(get_next_visible_pane(LEFT_MENUBAR_IDX, true, true, true, false, false), RIGHT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::RIGHT_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find function `get_next_visible_pane` in this scope
+ --> src/main.rs:607:20
+ |
+607 | assert_eq!(get_next_visible_pane(RIGHT_MENUBAR_IDX, true, true, true, false, false), PARAM_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this function
+ |
+392 + use crate::app::get_next_visible_pane;
+ |
+
+error[E0425]: cannot find value `RIGHT_MENUBAR_IDX` in this scope
+ --> src/main.rs:607:42
+ |
+607 | assert_eq!(get_next_visible_pane(RIGHT_MENUBAR_IDX, true, true, true, false, false), PARAM_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::RIGHT_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find value `PARAM_MENUBAR_IDX` in this scope
+ --> src/main.rs:607:94
+ |
+607 | assert_eq!(get_next_visible_pane(RIGHT_MENUBAR_IDX, true, true, true, false, false), PARAM_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::PARAM_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find function `get_next_visible_pane` in this scope
+ --> src/main.rs:608:20
+ |
+608 | assert_eq!(get_next_visible_pane(PARAM_MENUBAR_IDX, true, true, true, false, false), LEFT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this function
+ |
+392 + use crate::app::get_next_visible_pane;
+ |
+
+error[E0425]: cannot find value `PARAM_MENUBAR_IDX` in this scope
+ --> src/main.rs:608:42
+ |
+608 | assert_eq!(get_next_visible_pane(PARAM_MENUBAR_IDX, true, true, true, false, false), LEFT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::PARAM_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find value `LEFT_MENUBAR_IDX` in this scope
+ --> src/main.rs:608:94
+ |
+608 | assert_eq!(get_next_visible_pane(PARAM_MENUBAR_IDX, true, true, true, false, false), LEFT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::LEFT_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find function `get_next_visible_pane` in this scope
+ --> src/main.rs:611:20
+ |
+611 | assert_eq!(get_next_visible_pane(LEFT_MENUBAR_IDX, true, true, true, true, false), RIGHT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this function
+ |
+392 + use crate::app::get_next_visible_pane;
+ |
+
+error[E0425]: cannot find value `LEFT_MENUBAR_IDX` in this scope
+ --> src/main.rs:611:42
+ |
+611 | assert_eq!(get_next_visible_pane(LEFT_MENUBAR_IDX, true, true, true, true, false), RIGHT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::LEFT_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find value `RIGHT_MENUBAR_IDX` in this scope
+ --> src/main.rs:611:92
+ |
+611 | assert_eq!(get_next_visible_pane(LEFT_MENUBAR_IDX, true, true, true, true, false), RIGHT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::RIGHT_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find function `get_next_visible_pane` in this scope
+ --> src/main.rs:612:20
+ |
+612 | assert_eq!(get_next_visible_pane(RIGHT_MENUBAR_IDX, true, true, true, true, false), PARAM_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this function
+ |
+392 + use crate::app::get_next_visible_pane;
+ |
+
+error[E0425]: cannot find value `RIGHT_MENUBAR_IDX` in this scope
+ --> src/main.rs:612:42
+ |
+612 | assert_eq!(get_next_visible_pane(RIGHT_MENUBAR_IDX, true, true, true, true, false), PARAM_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::RIGHT_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find value `PARAM_MENUBAR_IDX` in this scope
+ --> src/main.rs:612:93
+ |
+612 | assert_eq!(get_next_visible_pane(RIGHT_MENUBAR_IDX, true, true, true, true, false), PARAM_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::PARAM_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find function `get_next_visible_pane` in this scope
+ --> src/main.rs:613:20
+ |
+613 | assert_eq!(get_next_visible_pane(PARAM_MENUBAR_IDX, true, true, true, true, false), SPREADSHEET_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this function
+ |
+392 + use crate::app::get_next_visible_pane;
+ |
+
+error[E0425]: cannot find value `PARAM_MENUBAR_IDX` in this scope
+ --> src/main.rs:613:42
+ |
+613 | assert_eq!(get_next_visible_pane(PARAM_MENUBAR_IDX, true, true, true, true, false), SPREADSHEET_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::PARAM_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find value `SPREADSHEET_MENUBAR_IDX` in this scope
+ --> src/main.rs:613:93
+ |
+613 | assert_eq!(get_next_visible_pane(PARAM_MENUBAR_IDX, true, true, true, true, false), SPREADSHEET_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::SPREADSHEET_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find function `get_next_visible_pane` in this scope
+ --> src/main.rs:614:20
+ |
+614 | assert_eq!(get_next_visible_pane(SPREADSHEET_MENUBAR_IDX, true, true, true, true, false), LEFT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this function
+ |
+392 + use crate::app::get_next_visible_pane;
+ |
+
+error[E0425]: cannot find value `SPREADSHEET_MENUBAR_IDX` in this scope
+ --> src/main.rs:614:42
+ |
+614 | assert_eq!(get_next_visible_pane(SPREADSHEET_MENUBAR_IDX, true, true, true, true, false), LEFT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::SPREADSHEET_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find value `LEFT_MENUBAR_IDX` in this scope
+ --> src/main.rs:614:99
+ |
+614 | assert_eq!(get_next_visible_pane(SPREADSHEET_MENUBAR_IDX, true, true, true, true, false), LEFT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::LEFT_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find function `get_next_visible_pane` in this scope
+ --> src/main.rs:617:20
+ |
+617 | assert_eq!(get_next_visible_pane(LEFT_MENUBAR_IDX, true, true, true, false, true), PARAM_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this function
+ |
+392 + use crate::app::get_next_visible_pane;
+ |
+
+error[E0425]: cannot find value `LEFT_MENUBAR_IDX` in this scope
+ --> src/main.rs:617:42
+ |
+617 | assert_eq!(get_next_visible_pane(LEFT_MENUBAR_IDX, true, true, true, false, true), PARAM_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::LEFT_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find value `PARAM_MENUBAR_IDX` in this scope
+ --> src/main.rs:617:92
+ |
+617 | assert_eq!(get_next_visible_pane(LEFT_MENUBAR_IDX, true, true, true, false, true), PARAM_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::PARAM_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find function `get_next_visible_pane` in this scope
+ --> src/main.rs:618:20
+ |
+618 | assert_eq!(get_next_visible_pane(PARAM_MENUBAR_IDX, true, true, true, false, true), RIGHT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this function
+ |
+392 + use crate::app::get_next_visible_pane;
+ |
+
+error[E0425]: cannot find value `PARAM_MENUBAR_IDX` in this scope
+ --> src/main.rs:618:42
+ |
+618 | assert_eq!(get_next_visible_pane(PARAM_MENUBAR_IDX, true, true, true, false, true), RIGHT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::PARAM_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find value `RIGHT_MENUBAR_IDX` in this scope
+ --> src/main.rs:618:93
+ |
+618 | assert_eq!(get_next_visible_pane(PARAM_MENUBAR_IDX, true, true, true, false, true), RIGHT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::RIGHT_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find function `get_next_visible_pane` in this scope
+ --> src/main.rs:619:20
+ |
+619 | assert_eq!(get_next_visible_pane(RIGHT_MENUBAR_IDX, true, true, true, false, true), LEFT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this function
+ |
+392 + use crate::app::get_next_visible_pane;
+ |
+
+error[E0425]: cannot find value `RIGHT_MENUBAR_IDX` in this scope
+ --> src/main.rs:619:42
+ |
+619 | assert_eq!(get_next_visible_pane(RIGHT_MENUBAR_IDX, true, true, true, false, true), LEFT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::RIGHT_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find value `LEFT_MENUBAR_IDX` in this scope
+ --> src/main.rs:619:93
+ |
+619 | assert_eq!(get_next_visible_pane(RIGHT_MENUBAR_IDX, true, true, true, false, true), LEFT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::LEFT_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find function `get_next_visible_pane` in this scope
+ --> src/main.rs:622:20
+ |
+622 | assert_eq!(get_next_visible_pane(LEFT_MENUBAR_IDX, true, true, true, true, true), SPREADSHEET_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this function
+ |
+392 + use crate::app::get_next_visible_pane;
+ |
+
+error[E0425]: cannot find value `LEFT_MENUBAR_IDX` in this scope
+ --> src/main.rs:622:42
+ |
+622 | assert_eq!(get_next_visible_pane(LEFT_MENUBAR_IDX, true, true, true, true, true), SPREADSHEET_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::LEFT_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find value `SPREADSHEET_MENUBAR_IDX` in this scope
+ --> src/main.rs:622:91
+ |
+622 | assert_eq!(get_next_visible_pane(LEFT_MENUBAR_IDX, true, true, true, true, true), SPREADSHEET_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::SPREADSHEET_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find function `get_next_visible_pane` in this scope
+ --> src/main.rs:623:20
+ |
+623 | assert_eq!(get_next_visible_pane(SPREADSHEET_MENUBAR_IDX, true, true, true, true, true), PARAM_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this function
+ |
+392 + use crate::app::get_next_visible_pane;
+ |
+
+error[E0425]: cannot find value `SPREADSHEET_MENUBAR_IDX` in this scope
+ --> src/main.rs:623:42
+ |
+623 | assert_eq!(get_next_visible_pane(SPREADSHEET_MENUBAR_IDX, true, true, true, true, true), PARAM_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::SPREADSHEET_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find value `PARAM_MENUBAR_IDX` in this scope
+ --> src/main.rs:623:98
+ |
+623 | assert_eq!(get_next_visible_pane(SPREADSHEET_MENUBAR_IDX, true, true, true, true, true), PARAM_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::PARAM_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find function `get_next_visible_pane` in this scope
+ --> src/main.rs:624:20
+ |
+624 | assert_eq!(get_next_visible_pane(PARAM_MENUBAR_IDX, true, true, true, true, true), RIGHT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this function
+ |
+392 + use crate::app::get_next_visible_pane;
+ |
+
+error[E0425]: cannot find value `PARAM_MENUBAR_IDX` in this scope
+ --> src/main.rs:624:42
+ |
+624 | assert_eq!(get_next_visible_pane(PARAM_MENUBAR_IDX, true, true, true, true, true), RIGHT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::PARAM_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find value `RIGHT_MENUBAR_IDX` in this scope
+ --> src/main.rs:624:92
+ |
+624 | assert_eq!(get_next_visible_pane(PARAM_MENUBAR_IDX, true, true, true, true, true), RIGHT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::RIGHT_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find function `get_next_visible_pane` in this scope
+ --> src/main.rs:625:20
+ |
+625 | assert_eq!(get_next_visible_pane(RIGHT_MENUBAR_IDX, true, true, true, true, true), LEFT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this function
+ |
+392 + use crate::app::get_next_visible_pane;
+ |
+
+error[E0425]: cannot find value `RIGHT_MENUBAR_IDX` in this scope
+ --> src/main.rs:625:42
+ |
+625 | assert_eq!(get_next_visible_pane(RIGHT_MENUBAR_IDX, true, true, true, true, true), LEFT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::RIGHT_MENUBAR_IDX;
+ |
+
+error[E0425]: cannot find value `LEFT_MENUBAR_IDX` in this scope
+ --> src/main.rs:625:92
+ |
+625 | assert_eq!(get_next_visible_pane(RIGHT_MENUBAR_IDX, true, true, true, true, true), LEFT_MENUBAR_IDX);
+ | ^^^^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this constant
+ |
+392 + use crate::app::LEFT_MENUBAR_IDX;
+ |
+
+error[E0433]: cannot find type `Shortcut` in this scope
+ --> src/main.rs:824:22
+ |
+824 | let ctrl_g = Shortcut::parse("Ctrl+g").unwrap();
+ | ^^^^^^^^ use of undeclared type `Shortcut`
+ |
+help: consider importing this struct
+ |
+392 + use crate::shortcut::Shortcut;
+ |
+
+error[E0433]: cannot find type `Shortcut` in this scope
+ --> src/main.rs:832:23
+ |
+832 | let complex = Shortcut::parse("Ctrl+Shift+Alt+Logo+s").unwrap();
+ | ^^^^^^^^ use of undeclared type `Shortcut`
+ |
+help: consider importing this struct
+ |
+392 + use crate::shortcut::Shortcut;
+ |
+
+error[E0433]: cannot find type `Shortcut` in this scope
+ --> src/main.rs:840:22
+ |
+840 | let tab_sc = Shortcut::parse("Tab").unwrap();
+ | ^^^^^^^^ use of undeclared type `Shortcut`
+ |
+help: consider importing this struct
+ |
+392 + use crate::shortcut::Shortcut;
+ |
+
+error[E0433]: cannot find type `Shortcut` in this scope
+ --> src/main.rs:844:23
+ |
+844 | let case_sc = Shortcut::parse("cTrL+sHiFt+ArrowDown").unwrap();
+ | ^^^^^^^^ use of undeclared type `Shortcut`
+ |
+help: consider importing this struct
+ |
+392 + use crate::shortcut::Shortcut;
+ |
+
+error[E0433]: cannot find type `ShortcutManager` in this scope
+ --> src/main.rs:850:23
+ |
+850 | let mut mgr = ShortcutManager::new();
+ | ^^^^^^^^^^^^^^^ use of undeclared type `ShortcutManager`
+ |
+help: consider importing this struct
+ |
+392 + use crate::shortcut::ShortcutManager;
+ |
+
+error[E0433]: cannot find type `Action` in this scope
+ --> src/main.rs:851:32
+ |
+851 | mgr.register("Ctrl+g", Action::ToggleGrid).unwrap();
+ | ^^^^^^ use of undeclared type `Action`
+ |
+help: an enum with a similar name exists
+ |
+851 - mgr.register("Ctrl+g", Action::ToggleGrid).unwrap();
+851 + mgr.register("Ctrl+g", Option::ToggleGrid).unwrap();
+ |
+help: consider importing this enum
+ |
+392 + use crate::shortcut::Action;
+ |
+
+error[E0433]: cannot find type `Action` in this scope
+ --> src/main.rs:852:27
+ |
+852 | mgr.register("`", Action::ToggleSpreadsheet).unwrap();
+ | ^^^^^^ use of undeclared type `Action`
+ |
+help: an enum with a similar name exists
+ |
+852 - mgr.register("`", Action::ToggleSpreadsheet).unwrap();
+852 + mgr.register("`", Option::ToggleSpreadsheet).unwrap();
+ |
+help: consider importing this enum
+ |
+392 + use crate::shortcut::Action;
+ |
+
+error[E0433]: cannot find type `Action` in this scope
+ --> src/main.rs:857:63
+ |
+857 | assert_eq!(mgr.match_action(&mods_ctrl, &key_g), Some(Action::ToggleGrid));
+ | ^^^^^^ use of undeclared type `Action`
+ |
+help: an enum with a similar name exists
+ |
+857 - assert_eq!(mgr.match_action(&mods_ctrl, &key_g), Some(Action::ToggleGrid));
+857 + assert_eq!(mgr.match_action(&mods_ctrl, &key_g), Some(Option::ToggleGrid));
+ |
+help: consider importing this enum
+ |
+392 + use crate::shortcut::Action;
+ |
+
+error[E0433]: cannot find type `Action` in this scope
+ --> src/main.rs:866:66
+ |
+866 | assert_eq!(mgr.match_action(&mods_none, &key_tick), Some(Action::ToggleSpreadsheet));
+ | ^^^^^^ use of undeclared type `Action`
+ |
+help: an enum with a similar name exists
+ |
+866 - assert_eq!(mgr.match_action(&mods_none, &key_tick), Some(Action::ToggleSpreadsheet));
+866 + assert_eq!(mgr.match_action(&mods_none, &key_tick), Some(Option::ToggleSpreadsheet));
+ |
+help: consider importing this enum
+ |
+392 + use crate::shortcut::Action;
+ |
+
+warning: unused import: `std::time::Instant`
+ --> src/main.rs:1:5
+ |
+1 | use std::time::Instant;
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default
+
+warning: unused imports: `Deserialize` and `Serialize`
+ --> src/main.rs:7:13
+ |
+7 | use serde::{Deserialize, Serialize};
+ | ^^^^^^^^^^^ ^^^^^^^^^
+
+warning: unused imports: `ElementState`, `KeyEvent`, `MouseButton`, and `MouseScrollDelta`
+ --> src/main.rs:9:24
+ |
+9 | use clear_ui::widget::{ElementState, MouseButton, MouseScrollDelta, KeyEvent, Key, NamedKey};
+ | ^^^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^^^^^^^^
+
+warning: unused imports: `Capability`, `CompositorHandler`, `CursorIcon`, `PointerHandler`, `ProvidesRegistryState`, `SeatHandler`, `ShmHandler`, `ThemeSpec`, `ThemedPointer`, `Window as XdgWindow`, `WindowConfigure`, `WindowDecorations`, `WindowHandler`, `delegate_compositor`, `delegate_keyboard`, `delegate_output`, `delegate_pointer`, `delegate_registry`, `delegate_seat`, `delegate_shm`, `delegate_xdg_shell`, `delegate_xdg_window`, and `keyboard::KeyboardHandler`
+ --> src/main.rs:12:18
+ |
+12 | compositor::{CompositorHandler, CompositorState},
+ | ^^^^^^^^^^^^^^^^^
+13 | delegate_compositor, delegate_keyboard, delegate_pointer, delegate_registry,
+ | ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
+14 | delegate_seat, delegate_shm, delegate_xdg_shell, delegate_xdg_window, delegate_output,
+ | ^^^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
+15 | registry::{ProvidesRegistryState, RegistryState},
+ | ^^^^^^^^^^^^^^^^^^^^^
+...
+18 | keyboard::KeyboardHandler,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+19 | pointer::{PointerHandler, ThemedPointer, ThemeSpec, CursorIcon},
+ | ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^
+20 | Capability, SeatHandler, SeatState,
+ | ^^^^^^^^^^ ^^^^^^^^^^^
+...
+24 | window::{Window as XdgWindow, WindowConfigure, WindowHandler, WindowDecorations},
+ | ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
+...
+29 | shm::{Shm, ShmHandler},
+ | ^^^^^^^^^^
+
+warning: unused imports: `Proxy`, `QueueHandle`, `wl_keyboard`, `wl_output`, `wl_pointer`, `wl_seat`, `wl_shm`, and `wl_surface`
+ --> src/main.rs:33:16
+ |
+33 | protocol::{wl_keyboard, wl_output, wl_pointer, wl_seat, wl_shm, wl_surface},
+ | ^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^ ^^^^^^ ^^^^^^^^^^
+34 | Connection, QueueHandle, Proxy,
+ | ^^^^^^^^^^^ ^^^^^
+
+warning: unused import: `wgpu::util::DeviceExt`
+ --> src/main.rs:38:5
+ |
+38 | use wgpu::util::DeviceExt;
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+warning: unused imports: `Breadcrumb`, `Button`, `Canvas`, `Checkbox`, `ColorSelector`, `GraphNode`, `Graph`, `Label`, `MenuBar`, `Paginator`, `ParametersBg`, `Plate`, `ScrollingList`, `Slider`, `Spinbox`, `Splitter`, `Spreadsheet`, `StatusBar`, `Switcher`, `TextLabel`, and `ViewportBg`
+ --> src/main.rs:39:24
+ |
+39 | ...::{Breadcrumb, Canvas, MenuBar, Plate, ParametersBg, Splitter, Spreadsheet, StatusBar, TextLabel, ViewportBg, Element, GraphNode, Graph, Paginator, Button, Checkbox, Slider, Spinbox, ScrollingList, Label, ColorSelector, Switcher};
+ | ^^^^^^^^^^ ^^^^^^ ^^^^^^^ ^^^^^ ^^^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^ ^^^^^ ^^^^^^^^^ ^^^^^^ ^^^^^^^^ ^^^^^^ ^^^^^^^ ^^^^^^^^^^^^^ ^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^
+
+warning: unused import: `clear_ui::colors`
+ --> src/main.rs:40:5
+ |
+40 | use clear_ui::colors;
+ | ^^^^^^^^^^^^^^^^
+
+warning: unused imports: `Attrs`, `Buffer`, `Cache`, `FontSystem`, `Metrics`, `Resolution`, `SwashCache`, `TextArea`, `TextAtlas`, `TextBounds`, `TextRenderer`, and `Viewport`
+ --> src/main.rs:41:15
+ |
+41 | ...::{Attrs, Buffer, Cache, FontSystem, Metrics, Resolution, SwashCache, TextArea, TextAtlas, TextBounds, TextRenderer, Viewport};
+ | ^^^^^ ^^^^^^ ^^^^^ ^^^^^^^^^^ ^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^
+
+warning: unused import: `Mat4`
+ --> src/main.rs:42:12
+ |
+42 | use glam::{Mat4, Vec3};
+ | ^^^^
+
+warning: unused import: `std::net::TcpListener`
+ --> src/app.rs:4:5
+ |
+4 | use std::net::TcpListener;
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+warning: unused import: `BufReader`
+ --> src/app.rs:5:24
+ |
+5 | use std::io::{BufRead, BufReader, Read, Write};
+ | ^^^^^^^^^
+
+warning: unused imports: `Capability`, `CompositorHandler`, `CursorIcon`, `OutputHandler`, `OutputState`, `PointerHandler`, `ProvidesRegistryState`, `RegistryState`, `SeatHandler`, `SeatState`, `ShmHandler`, `Shm`, `ThemeSpec`, `ThemedPointer`, `WindowConfigure`, `delegate_compositor`, `delegate_keyboard`, `delegate_output`, `delegate_pointer`, `delegate_registry`, `delegate_seat`, `delegate_shm`, `delegate_xdg_shell`, `delegate_xdg_window`, and `keyboard::KeyboardHandler`
+ --> src/app.rs:12:18
+ |
+12 | compositor::{CompositorHandler, CompositorState},
+ | ^^^^^^^^^^^^^^^^^
+13 | delegate_compositor, delegate_keyboard, delegate_pointer, delegate_registry,
+ | ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
+14 | delegate_seat, delegate_shm, delegate_xdg_shell, delegate_xdg_window, delegate_output,
+ | ^^^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
+15 | registry::{ProvidesRegistryState, RegistryState},
+ | ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^
+16 | output::{OutputHandler, OutputState},
+ | ^^^^^^^^^^^^^ ^^^^^^^^^^^
+17 | seat::{
+18 | keyboard::KeyboardHandler,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+19 | pointer::{PointerHandler, ThemedPointer, ThemeSpec, CursorIcon},
+ | ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^
+20 | Capability, SeatHandler, SeatState,
+ | ^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^
+...
+24 | window::{Window as XdgWindow, WindowConfigure, WindowHandler, WindowDecorations},
+ | ^^^^^^^^^^^^^^^
+...
+29 | shm::{Shm, ShmHandler},
+ | ^^^ ^^^^^^^^^^
+
+warning: unused imports: `globals::registry_queue_init`, `wl_keyboard`, `wl_output`, `wl_pointer`, `wl_seat`, and `wl_shm`
+ --> src/app.rs:32:5
+ |
+32 | globals::registry_queue_init,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+33 | protocol::{wl_keyboard, wl_output, wl_pointer, wl_seat, wl_shm, wl_surface},
+ | ^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^ ^^^^^^
+
+warning: unused import: `calloop_wayland_source::WaylandSource`
+ --> src/app.rs:36:5
+ |
+36 | use calloop_wayland_source::WaylandSource;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: unused import: `Switcher`
+ --> src/app.rs:39:241
+ |
+39 | ...kbox, Slider, Spinbox, ScrollingList, Label, ColorSelector, Switcher};
+ | ^^^^^^^^
+
+warning: unused imports: `SwashCache`, `TextArea`, and `TextBounds`
+ --> src/app.rs:42:70
+ |
+42 | use glyphon::{Attrs, Buffer, Cache, FontSystem, Metrics, Resolution, SwashCache, TextArea, TextAtlas, TextBounds, TextRenderer, View...
+ | ^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^^^
+
+warning: unused import: `crate::project::*`
+ --> src/app.rs:46:5
+ |
+46 | use crate::project::*;
+ | ^^^^^^^^^^^^^^^^^
+
+warning: unused import: `crate::render::*`
+ --> src/app.rs:47:5
+ |
+47 | use crate::render::*;
+ | ^^^^^^^^^^^^^^^^
+
+warning: unused import: `Shortcut`
+ --> src/app.rs:48:23
+ |
+48 | use crate::shortcut::{Shortcut, ShortcutManager, Action};
+ | ^^^^^^^^
+
+warning: unused import: `LocalPosition`
+ --> src/app.rs:51:44
+ |
+51 | use crate::window::{AppState, WindowEvent, LocalPosition};
+ | ^^^^^^^^^^^^^
+
+warning: unused import: `clear_ui::engine::Vertex`
+ --> src/graphics.rs:1:5
+ |
+1 | use clear_ui::engine::Vertex;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: unused import: `std::time::Instant`
+ --> src/window.rs:1:5
+ |
+1 | use std::time::Instant;
+ | ^^^^^^^^^^^^^^^^^^
+
+warning: unused import: `std::fs`
+ --> src/window.rs:2:5
+ |
+2 | use std::fs;
+ | ^^^^^^^
+
+warning: unused import: `std::net::TcpListener`
+ --> src/window.rs:4:5
+ |
+4 | use std::net::TcpListener;
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+warning: unused imports: `BufReader` and `Write`
+ --> src/window.rs:5:24
+ |
+5 | use std::io::{BufRead, BufReader, Read, Write};
+ | ^^^^^^^^^ ^^^^^
+
+warning: unused imports: `Deserialize` and `Serialize`
+ --> src/window.rs:7:13
+ |
+7 | use serde::{Deserialize, Serialize};
+ | ^^^^^^^^^^^ ^^^^^^^^^
+
+warning: unused imports: `ElementState`, `KeyEvent`, `Key`, `MouseButton`, `MouseScrollDelta`, and `NamedKey`
+ --> src/window.rs:9:24
+ |
+9 | use clear_ui::widget::{ElementState, MouseButton, MouseScrollDelta, KeyEvent, Key, NamedKey};
+ | ^^^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^^ ^^^^^^^^
+
+warning: unused import: `WindowDecorations`
+ --> src/window.rs:24:75
+ |
+24 | window::{Window as XdgWindow, WindowConfigure, WindowHandler, WindowDecorations},
+ | ^^^^^^^^^^^^^^^^^
+
+warning: unused imports: `Proxy`, `globals::registry_queue_init`, and `wl_shm`
+ --> src/window.rs:32:5
+ |
+32 | globals::registry_queue_init,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+33 | protocol::{wl_keyboard, wl_output, wl_pointer, wl_seat, wl_shm, wl_surface},
+ | ^^^^^^
+34 | Connection, QueueHandle, Proxy,
+ | ^^^^^
+
+warning: unused import: `calloop_wayland_source::WaylandSource`
+ --> src/window.rs:36:5
+ |
+36 | use calloop_wayland_source::WaylandSource;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: unused import: `wgpu::util::DeviceExt`
+ --> src/window.rs:38:5
+ |
+38 | use wgpu::util::DeviceExt;
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+warning: unused imports: `Breadcrumb`, `Button`, `Canvas`, `Checkbox`, `ColorSelector`, `GraphNode`, `Graph`, `Label`, `MenuBar`, `Paginator`, `ParametersBg`, `Plate`, `ScrollingList`, `Slider`, `Spinbox`, `Splitter`, `Spreadsheet`, `StatusBar`, `Switcher`, `TextLabel`, and `ViewportBg`
+ --> src/window.rs:39:24
+ |
+39 | ...::{Breadcrumb, Canvas, MenuBar, Plate, ParametersBg, Splitter, Spreadsheet, StatusBar, TextLabel, ViewportBg, Element, GraphNode, Graph, Paginator, Button, Checkbox, Slider, Spinbox, ScrollingList, Label, ColorSelector, Switcher};
+ | ^^^^^^^^^^ ^^^^^^ ^^^^^^^ ^^^^^ ^^^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^ ^^^^^ ^^^^^^^^^ ^^^^^^ ^^^^^^^^ ^^^^^^ ^^^^^^^ ^^^^^^^^^^^^^ ^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^
+
+warning: unused import: `clear_ui::colors`
+ --> src/window.rs:40:5
+ |
+40 | use clear_ui::colors;
+ | ^^^^^^^^^^^^^^^^
+
+warning: unused imports: `Attrs`, `Buffer`, `Cache`, `FontSystem`, `Metrics`, `Resolution`, `SwashCache`, `TextArea`, `TextAtlas`, `TextBounds`, `TextRenderer`, and `Viewport`
+ --> src/window.rs:41:15
+ |
+41 | ...::{Attrs, Buffer, Cache, FontSystem, Metrics, Resolution, SwashCache, TextArea, TextAtlas, TextBounds, TextRenderer, Viewport};
+ | ^^^^^ ^^^^^^ ^^^^^ ^^^^^^^^^^ ^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^
+
+warning: unused imports: `Mat4` and `Vec3`
+ --> src/window.rs:42:12
+ |
+42 | use glam::{Mat4, Vec3};
+ | ^^^^ ^^^^
+
+warning: unused import: `crate::geometry::*`
+ --> src/window.rs:44:5
+ |
+44 | use crate::geometry::*;
+ | ^^^^^^^^^^^^^^^^^^
+
+warning: unused import: `crate::project::*`
+ --> src/window.rs:45:5
+ |
+45 | use crate::project::*;
+ | ^^^^^^^^^^^^^^^^^
+
+warning: unused import: `crate::render::*`
+ --> src/window.rs:46:5
+ |
+46 | use crate::render::*;
+ | ^^^^^^^^^^^^^^^^
+
+warning: unused imports: `ShortcutManager` and `Shortcut`
+ --> src/window.rs:47:23
+ |
+47 | use crate::shortcut::{Shortcut, ShortcutManager, Action};
+ | ^^^^^^^^ ^^^^^^^^^^^^^^^
+
+warning: unused imports: `DesignSettings`, `NODE_PALETTE_IDX`, and `NodePalette`
+ --> src/window.rs:48:78
+ |
+48 | ...tion, ModifiersState, TouchPhase, DesignSettings, param_display, NodePalette, NODE_PALETTE_IDX, LEFT_MENUBAR_IDX, RIGHT_MENUBAR_I...
+ | ^^^^^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
+
+warning: unused import: `clear_ui::engine::Vertex`
+ --> src/window.rs:49:5
+ |
+49 | use clear_ui::engine::Vertex;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: unused import: `PathBuf`
+ --> src/project.rs:2:23
+ |
+2 | use std::path::{Path, PathBuf};
+ | ^^^^^^^
+
+warning: unused import: `std::time::Instant`
+ --> src/project.rs:3:5
+ |
+3 | use std::time::Instant;
+ | ^^^^^^^^^^^^^^^^^^
+
+warning: unused import: `std::time::Instant`
+ --> src/render.rs:1:5
+ |
+1 | use std::time::Instant;
+ | ^^^^^^^^^^^^^^^^^^
+
+warning: unused import: `std::path::Path`
+ --> src/render.rs:2:5
+ |
+2 | use std::path::Path;
+ | ^^^^^^^^^^^^^^^
+
+warning: unused imports: `CustomEvent`, `HEADER_H`, `SPLITTER1_IDX`, `SPLITTER2_IDX`, and `SPLITTER_W`
+ --> src/render.rs:10:20
+ |
+10 | State, FsNode, CustomEvent,
+ | ^^^^^^^^^^^
+...
+14 | SPREADSHEET_MENUBAR_IDX, SPLITTER1_IDX, SPLITTER2_IDX,
+ | ^^^^^^^^^^^^^ ^^^^^^^^^^^^^
+15 | MENUBAR_H, STATUS_H, HEADER_H, SPLITTER_W,
+ | ^^^^^^^^ ^^^^^^^^^^
+
+warning: unused import: `network_sphere_vertices_with_errors`
+ --> src/render.rs:22:5
+ |
+22 | network_sphere_vertices_with_errors,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: unused imports: `ParamDef`, `TouchPhase`, and `param_display`
+ --> src/main.rs:53:59
+ |
+53 | ...ion, ModifiersState, TouchPhase, DesignSettings, param_display, FsNode, Project, ProjectViewState, ParamDef};
+ | ^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^
+
+warning: unused import: `api::start_http_server`
+ --> src/main.rs:55:5
+ |
+55 | use api::start_http_server;
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+warning: unused import: `project::*`
+ --> src/main.rs:57:5
+ |
+57 | use project::*;
+ | ^^^^^^^^^^
+
+warning: unused import: `render::*`
+ --> src/main.rs:58:5
+ |
+58 | use render::*;
+ | ^^^^^^^^^
+
+warning: use of deprecated type alias `wgpu::ImageCopyTexture`: This has been renamed to `TexelCopyTextureInfo`, and will be removed in 25.0.0.
+ --> src/app.rs:5548:23
+ |
+5548 | wgpu::ImageCopyTexture {
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(deprecated)]` on by default
+
+warning: use of deprecated type alias `wgpu::ImageCopyTexture`: This has been renamed to `TexelCopyTextureInfo`, and will be removed in 25.0.0.
+ --> src/app.rs:5554:23
+ |
+5554 | wgpu::ImageCopyTexture {
+ | ^^^^^^^^^^^^^^^^
+
+warning: use of deprecated associated function `opencl3::command_queue::CommandQueue::create`: From CL_VERSION_2_0 use create_command_queue_with_properties
+ --> src/geometry.rs:400:40
+ |
+400 | let queue = unsafe { CommandQueue::create(&context, device_id, 0) }.ok()?;
+ | ^^^^^^
+
+warning: unused import: `Write`
+ --> src/app.rs:5:41
+ |
+5 | use std::io::{BufRead, BufReader, Read, Write};
+ | ^^^^^
+
+warning: unused import: `BufRead`
+ --> src/app.rs:5:15
+ |
+5 | use std::io::{BufRead, BufReader, Read, Write};
+ | ^^^^^^^
+
+warning: unused import: `Read`
+ --> src/app.rs:5:35
+ |
+5 | use std::io::{BufRead, BufReader, Read, Write};
+ | ^^^^
+
+warning: unused import: `WindowHandler`
+ --> src/app.rs:24:60
+ |
+24 | window::{Window as XdgWindow, WindowConfigure, WindowHandler, WindowDecorations},
+ | ^^^^^^^^^^^^^
+
+warning: unused import: `WaylandSurface`
+ --> src/window.rs:27:9
+ |
+27 | WaylandSurface,
+ | ^^^^^^^^^^^^^^
+
+warning: unused import: `Read`
+ --> src/window.rs:5:35
+ |
+5 | use std::io::{BufRead, BufReader, Read, Write};
+ | ^^^^
+
+warning: unused import: `BufRead`
+ --> src/window.rs:5:15
+ |
+5 | use std::io::{BufRead, BufReader, Read, Write};
+ | ^^^^^^^
+
+warning: unused import: `Element`
+ --> src/project.rs:5:32
+ |
+5 | use clear_ui::widget::{Button, Element};
+ | ^^^^^^^
+
+warning: unused import: `OutputHandler`
+ --> src/main.rs:16:14
+ |
+16 | output::{OutputHandler, OutputState},
+ | ^^^^^^^^^^^^^
+
+warning: unused import: `WaylandSurface`
+ --> src/main.rs:27:9
+ |
+27 | WaylandSurface,
+ | ^^^^^^^^^^^^^^
+
+warning: unused import: `Element`
+ --> src/main.rs:39:131
+ |
+39 | ...Bg, Splitter, Spreadsheet, StatusBar, TextLabel, ViewportBg, Element, GraphNode, Graph, Paginator, Button, Checkbox, Slider, Spin...
+ | ^^^^^^^
+
+warning: unused variable: `surface`
+ --> src/app.rs:2036:13
+ |
+2036 | let surface = &wgpu_adapter.surface;
+ | ^^^^^^^ help: if this is intentional, prefix it with an underscore: `_surface`
+ |
+ = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default
+
+warning: variable `sp_menub_y` is assigned to, but never used
+ --> src/app.rs:3137:21
+ |
+3137 | let mut sp_menub_y = 0.0;
+ | ^^^^^^^^^^^^^^
+ |
+ = note: consider using `_sp_menub_y` instead
+
+warning: variable `sp_menub_h` is assigned to, but never used
+ --> src/app.rs:3138:21
+ |
+3138 | let mut sp_menub_h = 0.0;
+ | ^^^^^^^^^^^^^^
+ |
+ = note: consider using `_sp_menub_h` instead
+
+warning: value assigned to `sp_menub_y` is never read
+ --> src/app.rs:3156:25
+ |
+3156 | sp_menub_y = 0.0;
+ | ^^^^^^^^^^^^^^^^
+ |
+ = help: maybe it is overwritten before being read?
+ = note: `#[warn(unused_assignments)]` (part of `#[warn(unused)]`) on by default
+
+warning: value assigned to `sp_menub_y` is never read
+ --> src/app.rs:3148:25
+ |
+3148 | sp_menub_y = 0.0;
+ | ^^^^^^^^^^^^^^^^
+ |
+ = help: maybe it is overwritten before being read?
+
+warning: value assigned to `sp_menub_h` is never read
+ --> src/app.rs:3157:25
+ |
+3157 | sp_menub_h = 0.0;
+ | ^^^^^^^^^^^^^^^^
+ |
+ = help: maybe it is overwritten before being read?
+
+warning: value assigned to `sp_menub_h` is never read
+ --> src/app.rs:3149:25
+ |
+3149 | sp_menub_h = 0.0;
+ | ^^^^^^^^^^^^^^^^
+ |
+ = help: maybe it is overwritten before being read?
+
+warning: unused variable: `cx`
+ --> src/window.rs:276:22
+ |
+276 | let (cx, cy) = clear_ui::wayland::scale_pointer_pos(event.position, st.scale);
+ | ^^ help: if this is intentional, prefix it with an underscore: `_cx`
+
+warning: unused variable: `cy`
+ --> src/window.rs:276:26
+ |
+276 | let (cx, cy) = clear_ui::wayland::scale_pointer_pos(event.position, st.scale);
+ | ^^ help: if this is intentional, prefix it with an underscore: `_cy`
+
+warning: unused variable: `idx`
+ --> src/geometry.rs:656:17
+ |
+656 | let idx = *count;
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_idx`
+
+warning: unused variable: `idx`
+ --> src/geometry.rs:665:17
+ |
+665 | let idx = *count;
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_idx`
+
+Some errors have detailed explanations: E0425, E0433.
+For more information about an error, try `rustc --explain E0425`.
+warning: `cce-design-interface` (bin "cce-design-interface" test) generated 77 warnings
+error: could not compile `cce-design-interface` (bin "cce-design-interface" test) due to 51 previous errors; 77 warnings emitted
diff --git a/scratch/test_errors2.log b/scratch/test_errors2.log
new file mode 100644
index 0000000..b08d524
--- /dev/null
+++ b/scratch/test_errors2.log
@@ -0,0 +1,1044 @@
+warning: unused import: `KeyEvent`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/json_layout.rs:3:5
+ |
+3 | KeyEvent, MouseButton, ElementState, focus, Slider, Event, UiContext,
+ | ^^^^^^^^
+ |
+ = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default
+
+warning: unused import: `Ordering`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/mod.rs:67:38
+ |
+67 | use std::sync::atomic::{AtomicUsize, Ordering};
+ | ^^^^^^^^
+
+warning: unused import: `wl_shm`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/backend/window_runner.rs:24:61
+ |
+24 | protocol::{wl_keyboard, wl_output, wl_pointer, wl_seat, wl_shm, wl_s...
+ | ^^^^^^
+
+warning: unused imports: `SwashCache`, `TextAtlas`, and `TextRenderer`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/backend/window_runner.rs:34:36
+ |
+34 | Cache, FontSystem, Resolution, SwashCache, TextArea, TextAtlas,
+ | ^^^^^^^^^^ ^^^^^^^^^
+35 | TextBounds, TextRenderer, Viewport, Buffer, Attrs, Metrics,
+ | ^^^^^^^^^^^^
+
+warning: unused imports: `KeyEvent` and `MouseScrollDelta`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/context.rs:2:57
+ |
+2 | ...e, Key, KeyEvent, MouseButton, ElementState, MouseScrollDelta};
+ | ^^^^^^^^ ^^^^^^^^^^^^^^^^
+
+warning: unused import: `crate::widget::TextBox`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/context.rs:5:5
+ |
+5 | use crate::widget::TextBox;
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+warning: variable does not need to be mutable
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/backend/window_runner.rs:925:13
+ |
+925 | ... let mut adapter = WgpuAdapter::new(display_ptr, surface_ptr, pw, ...
+ | ----^^^^^^^
+ | |
+ | help: remove this `mut`
+ |
+ = note: `#[warn(unused_mut)]` (part of `#[warn(unused)]`) on by default
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/json_layout.rs:381:39
+ |
+381 | ...abels_with_bounds(&self, ctx: &UiContext) -> Vec<(TextLabel, Option<...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+ |
+ = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default
+
+warning: unused variable: `x`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/json_layout.rs:440:49
+ |
+440 | Event::MouseButton { button, state, x, y } => {
+ | ^ help: try ignoring the field: `x: _`
+
+warning: unused variable: `y`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/json_layout.rs:440:52
+ |
+440 | Event::MouseButton { button, state, x, y } => {
+ | ^ help: try ignoring the field: `y: _`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/button.rs:98:30
+ |
+98 | fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, ...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/slider.rs:200:75
+ |
+200 | ...Delta, px: f32, py: f32, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/slider.rs:307:52
+ |
+307 | ... self, event: &KeyEvent, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/spinbox.rs:209:52
+ |
+209 | ... self, event: &KeyEvent, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/color_selector.rs:158:34
+ |
+158 | fn tick(&mut self, _dt: f32, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/color_selector.rs:201:52
+ |
+201 | ... self, event: &KeyEvent, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/dropdown.rs:264:52
+ |
+264 | ... self, event: &KeyEvent, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/text_box.rs:565:52
+ |
+565 | ... self, event: &KeyEvent, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/text_box.rs:918:39
+ |
+918 | ...abels_with_bounds(&self, ctx: &UiContext) -> Vec<(TextLabel, Option<...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/text_box.rs:923:48
+ |
+923 | ...h_font_and_bounds(&self, ctx: &UiContext) -> Vec<(TextLabel, Option<...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/trackpad.rs:147:91
+ |
+147 | ...State, px: f32, py: f32, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/button_strip.rs:216:91
+ |
+216 | ...State, px: f32, py: f32, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: value assigned to `next` is never read
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/input/button_strip.rs:359:24
+ |
+359 | let mut next = current;
+ | ^^^^^^^
+ |
+ = help: maybe it is overwritten before being read?
+ = note: `#[warn(unused_assignments)]` (part of `#[warn(unused)]`) on by default
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/container.rs:25:22
+ |
+25 | fn parent(&self, ctx: &UiContext) -> Option<*mut (dyn Element + 'sta...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/container.rs:26:76
+ |
+26 | ... (dyn Element + 'static)>, ctx: &mut UiContext) { self.parent = paren...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/container.rs:27:24
+ |
+27 | fn children(&self, ctx: &UiContext) -> Vec<*mut (dyn Element + 'stat...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/container.rs:28:66
+ |
+28 | ...t (dyn Element + 'static), ctx: &mut UiContext) { self.children.push(...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/container.rs:29:34
+ |
+29 | ... clear_children(&mut self, ctx: &mut UiContext) { self.children.clear...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/menu.rs:577:23
+ |
+577 | fn focused(&self, ctx: &UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/menu.rs:1258:22
+ |
+1258 | fn parent(&self, ctx: &UiContext) -> Option<*mut (dyn Element + 's...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/menu.rs:1262:76
+ |
+1262 | ...dyn Element + 'static)>, ctx: &mut UiContext) {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/menu.rs:1270:23
+ |
+1270 | fn focused(&self, ctx: &UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/breadcrumb.rs:60:92
+ |
+60 | ...tState, px: f32, _py: f32, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/spreadsheet.rs:233:33
+ |
+233 | fn tick(&mut self, dt: f32, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/scroll_box.rs:68:31
+ |
+68 | fn highlight_color(&self, ctx: &UiContext) -> Option<[f32; 4]> { None }
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/scroll_box.rs:149:52
+ |
+149 | ... self, event: &KeyEvent, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/scroll_box.rs:191:22
+ |
+191 | fn parent(&self, ctx: &UiContext) -> Option<*mut (dyn Element + 'st...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/scroll_box.rs:192:76
+ |
+192 | ...dyn Element + 'static)>, ctx: &mut UiContext) { self.parent = parent; }
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/scroll_box.rs:193:24
+ |
+193 | fn children(&self, ctx: &UiContext) -> Vec<*mut (dyn Element + 'sta...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/scroll_box.rs:194:66
+ |
+194 | ...(dyn Element + 'static), ctx: &mut UiContext) { self.children.push(c...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/scroll_box.rs:195:34
+ |
+195 | ...lear_children(&mut self, ctx: &mut UiContext) { self.children.clear(...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/paginator.rs:719:39
+ |
+719 | ...abels_with_bounds(&self, ctx: &UiContext) -> Vec<(TextLabel, Option<...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/paginator.rs:967:22
+ |
+967 | fn parent(&self, ctx: &UiContext) -> Option<*mut (dyn Element + 'st...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/paginator.rs:971:76
+ |
+971 | ...dyn Element + 'static)>, ctx: &mut UiContext) {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/paginator.rs:975:24
+ |
+975 | fn children(&self, ctx: &UiContext) -> Vec<*mut (dyn Element + 'sta...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/paginator.rs:984:67
+ |
+984 | ...(dyn Element + 'static), ctx: &mut UiContext) {}
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/container/paginator.rs:985:34
+ |
+985 | fn clear_children(&mut self, ctx: &mut UiContext) {}
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/panel.rs:37:30
+ |
+37 | fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, ...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/float3.rs:101:91
+ |
+101 | ...State, px: f32, py: f32, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/float3.rs:152:52
+ |
+152 | ... self, event: &KeyEvent, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/progress_bar.rs:25:30
+ |
+25 | fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, ...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/list_item.rs:48:30
+ |
+48 | fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, ...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/graph.rs:253:53
+ |
+253 | ... self, px: f32, py: f32, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/graph.rs:267:91
+ |
+267 | ...State, px: f32, py: f32, ctx: &mut UiContext) -> bool {
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/usage_bar.rs:35:30
+ |
+35 | fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, ...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/layout_preview.rs:55:30
+ |
+55 | fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, ...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/font_preview.rs:26:30
+ |
+26 | fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, ...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/info_box.rs:25:30
+ |
+25 | fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, ...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: unused variable: `ctx`
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/widget/display/status_dot.rs:32:30
+ |
+32 | fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, ...
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
+
+warning: variable does not need to be mutable
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/layout.rs:1918:31
+ |
+1918 | pub fn with_section_count(mut self, count: usize) -> Self {
+ | ----^^^^
+ | |
+ | help: remove this `mut`
+
+warning: field `min_col_width` is never read
+ --> /home/lsgalante/Dropbox/Clear/clear-ui/src/layout.rs:1793:5
+ |
+1791 | pub struct GridLayout {
+ | ---------- field in this struct
+1792 | grid: Option<Grid>,
+1793 | min_col_width: f32,
+ | ^^^^^^^^^^^^^
+ |
+ = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default
+
+warning: `clear-ui` (lib) generated 61 warnings (run `cargo fix --lib -p clear-ui` to apply 59 suggestions)
+ Compiling cce-design-interface v0.1.0 (/home/lsgalante/Dropbox/Clear/cce-design-interface)
+error[E0433]: cannot find type `Shortcut` in this scope
+ --> src/main.rs:825:22
+ |
+825 | let ctrl_g = Shortcut::parse("Ctrl+g").unwrap();
+ | ^^^^^^^^ use of undeclared type `Shortcut`
+ |
+help: consider importing this struct
+ |
+392 + use crate::shortcut::Shortcut;
+ |
+
+error[E0433]: cannot find type `Shortcut` in this scope
+ --> src/main.rs:833:23
+ |
+833 | let complex = Shortcut::parse("Ctrl+Shift+Alt+Logo+s").unwrap();
+ | ^^^^^^^^ use of undeclared type `Shortcut`
+ |
+help: consider importing this struct
+ |
+392 + use crate::shortcut::Shortcut;
+ |
+
+error[E0433]: cannot find type `Shortcut` in this scope
+ --> src/main.rs:841:22
+ |
+841 | let tab_sc = Shortcut::parse("Tab").unwrap();
+ | ^^^^^^^^ use of undeclared type `Shortcut`
+ |
+help: consider importing this struct
+ |
+392 + use crate::shortcut::Shortcut;
+ |
+
+error[E0433]: cannot find type `Shortcut` in this scope
+ --> src/main.rs:845:23
+ |
+845 | let case_sc = Shortcut::parse("cTrL+sHiFt+ArrowDown").unwrap();
+ | ^^^^^^^^ use of undeclared type `Shortcut`
+ |
+help: consider importing this struct
+ |
+392 + use crate::shortcut::Shortcut;
+ |
+
+error[E0433]: cannot find type `ShortcutManager` in this scope
+ --> src/main.rs:851:23
+ |
+851 | let mut mgr = ShortcutManager::new();
+ | ^^^^^^^^^^^^^^^ use of undeclared type `ShortcutManager`
+ |
+help: consider importing this struct
+ |
+392 + use crate::shortcut::ShortcutManager;
+ |
+
+error[E0433]: cannot find type `Action` in this scope
+ --> src/main.rs:852:32
+ |
+852 | mgr.register("Ctrl+g", Action::ToggleGrid).unwrap();
+ | ^^^^^^ use of undeclared type `Action`
+ |
+help: an enum with a similar name exists
+ |
+852 - mgr.register("Ctrl+g", Action::ToggleGrid).unwrap();
+852 + mgr.register("Ctrl+g", Option::ToggleGrid).unwrap();
+ |
+help: consider importing this enum
+ |
+392 + use crate::shortcut::Action;
+ |
+
+error[E0433]: cannot find type `Action` in this scope
+ --> src/main.rs:853:27
+ |
+853 | mgr.register("`", Action::ToggleSpreadsheet).unwrap();
+ | ^^^^^^ use of undeclared type `Action`
+ |
+help: an enum with a similar name exists
+ |
+853 - mgr.register("`", Action::ToggleSpreadsheet).unwrap();
+853 + mgr.register("`", Option::ToggleSpreadsheet).unwrap();
+ |
+help: consider importing this enum
+ |
+392 + use crate::shortcut::Action;
+ |
+
+error[E0433]: cannot find type `Action` in this scope
+ --> src/main.rs:858:63
+ |
+858 | assert_eq!(mgr.match_action(&mods_ctrl, &key_g), Some(Action::ToggleGrid));
+ | ^^^^^^ use of undeclared type `Action`
+ |
+help: an enum with a similar name exists
+ |
+858 - assert_eq!(mgr.match_action(&mods_ctrl, &key_g), Some(Action::ToggleGrid));
+858 + assert_eq!(mgr.match_action(&mods_ctrl, &key_g), Some(Option::ToggleGrid));
+ |
+help: consider importing this enum
+ |
+392 + use crate::shortcut::Action;
+ |
+
+error[E0433]: cannot find type `Action` in this scope
+ --> src/main.rs:867:66
+ |
+867 | assert_eq!(mgr.match_action(&mods_none, &key_tick), Some(Action::ToggleSpreadsheet));
+ | ^^^^^^ use of undeclared type `Action`
+ |
+help: an enum with a similar name exists
+ |
+867 - assert_eq!(mgr.match_action(&mods_none, &key_tick), Some(Action::ToggleSpreadsheet));
+867 + assert_eq!(mgr.match_action(&mods_none, &key_tick), Some(Option::ToggleSpreadsheet));
+ |
+help: consider importing this enum
+ |
+392 + use crate::shortcut::Action;
+ |
+
+warning: unused import: `std::time::Instant`
+ --> src/main.rs:1:5
+ |
+1 | use std::time::Instant;
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default
+
+warning: unused imports: `Deserialize` and `Serialize`
+ --> src/main.rs:7:13
+ |
+7 | use serde::{Deserialize, Serialize};
+ | ^^^^^^^^^^^ ^^^^^^^^^
+
+warning: unused imports: `ElementState`, `KeyEvent`, `MouseButton`, and `MouseScrollDelta`
+ --> src/main.rs:9:24
+ |
+9 | use clear_ui::widget::{ElementState, MouseButton, MouseScrollDelta, KeyEvent, Key, NamedKey};
+ | ^^^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^^^^^^^^
+
+warning: unused imports: `Capability`, `CompositorHandler`, `CursorIcon`, `PointerHandler`, `ProvidesRegistryState`, `SeatHandler`, `ShmHandler`, `ThemeSpec`, `ThemedPointer`, `Window as XdgWindow`, `WindowConfigure`, `WindowDecorations`, `WindowHandler`, `delegate_compositor`, `delegate_keyboard`, `delegate_output`, `delegate_pointer`, `delegate_registry`, `delegate_seat`, `delegate_shm`, `delegate_xdg_shell`, `delegate_xdg_window`, and `keyboard::KeyboardHandler`
+ --> src/main.rs:12:18
+ |
+12 | compositor::{CompositorHandler, CompositorState},
+ | ^^^^^^^^^^^^^^^^^
+13 | delegate_compositor, delegate_keyboard, delegate_pointer, delegate_registry,
+ | ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
+14 | delegate_seat, delegate_shm, delegate_xdg_shell, delegate_xdg_window, delegate_output,
+ | ^^^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
+15 | registry::{ProvidesRegistryState, RegistryState},
+ | ^^^^^^^^^^^^^^^^^^^^^
+...
+18 | keyboard::KeyboardHandler,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+19 | pointer::{PointerHandler, ThemedPointer, ThemeSpec, CursorIcon},
+ | ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^
+20 | Capability, SeatHandler, SeatState,
+ | ^^^^^^^^^^ ^^^^^^^^^^^
+...
+24 | window::{Window as XdgWindow, WindowConfigure, WindowHandler, WindowDecorations},
+ | ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
+...
+29 | shm::{Shm, ShmHandler},
+ | ^^^^^^^^^^
+
+warning: unused imports: `Proxy`, `QueueHandle`, `wl_keyboard`, `wl_output`, `wl_pointer`, `wl_seat`, `wl_shm`, and `wl_surface`
+ --> src/main.rs:33:16
+ |
+33 | protocol::{wl_keyboard, wl_output, wl_pointer, wl_seat, wl_shm, wl_surface},
+ | ^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^ ^^^^^^ ^^^^^^^^^^
+34 | Connection, QueueHandle, Proxy,
+ | ^^^^^^^^^^^ ^^^^^
+
+warning: unused import: `wgpu::util::DeviceExt`
+ --> src/main.rs:38:5
+ |
+38 | use wgpu::util::DeviceExt;
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+warning: unused imports: `Breadcrumb`, `Button`, `Canvas`, `Checkbox`, `ColorSelector`, `GraphNode`, `Graph`, `Label`, `MenuBar`, `Paginator`, `ParametersBg`, `Plate`, `ScrollingList`, `Slider`, `Spinbox`, `Splitter`, `Spreadsheet`, `StatusBar`, `Switcher`, `TextLabel`, and `ViewportBg`
+ --> src/main.rs:39:24
+ |
+39 | ...::{Breadcrumb, Canvas, MenuBar, Plate, ParametersBg, Splitter, Spreadsheet, StatusBar, TextLabel, ViewportBg, Element, GraphNode, Graph, Paginator, Button, Checkbox, Slider, Spinbox, ScrollingList, Label, ColorSelector, Switcher};
+ | ^^^^^^^^^^ ^^^^^^ ^^^^^^^ ^^^^^ ^^^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^ ^^^^^ ^^^^^^^^^ ^^^^^^ ^^^^^^^^ ^^^^^^ ^^^^^^^ ^^^^^^^^^^^^^ ^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^
+
+warning: unused import: `clear_ui::colors`
+ --> src/main.rs:40:5
+ |
+40 | use clear_ui::colors;
+ | ^^^^^^^^^^^^^^^^
+
+warning: unused imports: `Attrs`, `Buffer`, `Cache`, `FontSystem`, `Metrics`, `Resolution`, `SwashCache`, `TextArea`, `TextAtlas`, `TextBounds`, `TextRenderer`, and `Viewport`
+ --> src/main.rs:41:15
+ |
+41 | ...::{Attrs, Buffer, Cache, FontSystem, Metrics, Resolution, SwashCache, TextArea, TextAtlas, TextBounds, TextRenderer, Viewport};
+ | ^^^^^ ^^^^^^ ^^^^^ ^^^^^^^^^^ ^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^
+
+warning: unused import: `Mat4`
+ --> src/main.rs:42:12
+ |
+42 | use glam::{Mat4, Vec3};
+ | ^^^^
+
+warning: unused import: `std::net::TcpListener`
+ --> src/app.rs:4:5
+ |
+4 | use std::net::TcpListener;
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+warning: unused import: `BufReader`
+ --> src/app.rs:5:24
+ |
+5 | use std::io::{BufRead, BufReader, Read, Write};
+ | ^^^^^^^^^
+
+warning: unused imports: `Capability`, `CompositorHandler`, `CursorIcon`, `OutputHandler`, `OutputState`, `PointerHandler`, `ProvidesRegistryState`, `RegistryState`, `SeatHandler`, `SeatState`, `ShmHandler`, `Shm`, `ThemeSpec`, `ThemedPointer`, `WindowConfigure`, `delegate_compositor`, `delegate_keyboard`, `delegate_output`, `delegate_pointer`, `delegate_registry`, `delegate_seat`, `delegate_shm`, `delegate_xdg_shell`, `delegate_xdg_window`, and `keyboard::KeyboardHandler`
+ --> src/app.rs:12:18
+ |
+12 | compositor::{CompositorHandler, CompositorState},
+ | ^^^^^^^^^^^^^^^^^
+13 | delegate_compositor, delegate_keyboard, delegate_pointer, delegate_registry,
+ | ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
+14 | delegate_seat, delegate_shm, delegate_xdg_shell, delegate_xdg_window, delegate_output,
+ | ^^^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
+15 | registry::{ProvidesRegistryState, RegistryState},
+ | ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^
+16 | output::{OutputHandler, OutputState},
+ | ^^^^^^^^^^^^^ ^^^^^^^^^^^
+17 | seat::{
+18 | keyboard::KeyboardHandler,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+19 | pointer::{PointerHandler, ThemedPointer, ThemeSpec, CursorIcon},
+ | ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^
+20 | Capability, SeatHandler, SeatState,
+ | ^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^
+...
+24 | window::{Window as XdgWindow, WindowConfigure, WindowHandler, WindowDecorations},
+ | ^^^^^^^^^^^^^^^
+...
+29 | shm::{Shm, ShmHandler},
+ | ^^^ ^^^^^^^^^^
+
+warning: unused imports: `globals::registry_queue_init`, `wl_keyboard`, `wl_output`, `wl_pointer`, `wl_seat`, and `wl_shm`
+ --> src/app.rs:32:5
+ |
+32 | globals::registry_queue_init,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+33 | protocol::{wl_keyboard, wl_output, wl_pointer, wl_seat, wl_shm, wl_surface},
+ | ^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^ ^^^^^^
+
+warning: unused import: `calloop_wayland_source::WaylandSource`
+ --> src/app.rs:36:5
+ |
+36 | use calloop_wayland_source::WaylandSource;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: unused import: `Switcher`
+ --> src/app.rs:39:241
+ |
+39 | ...kbox, Slider, Spinbox, ScrollingList, Label, ColorSelector, Switcher};
+ | ^^^^^^^^
+
+warning: unused imports: `SwashCache`, `TextArea`, and `TextBounds`
+ --> src/app.rs:42:70
+ |
+42 | use glyphon::{Attrs, Buffer, Cache, FontSystem, Metrics, Resolution, SwashCache, TextArea, TextAtlas, TextBounds, TextRenderer, View...
+ | ^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^^^
+
+warning: unused import: `crate::project::*`
+ --> src/app.rs:46:5
+ |
+46 | use crate::project::*;
+ | ^^^^^^^^^^^^^^^^^
+
+warning: unused import: `crate::render::*`
+ --> src/app.rs:47:5
+ |
+47 | use crate::render::*;
+ | ^^^^^^^^^^^^^^^^
+
+warning: unused import: `Shortcut`
+ --> src/app.rs:48:23
+ |
+48 | use crate::shortcut::{Shortcut, ShortcutManager, Action};
+ | ^^^^^^^^
+
+warning: unused import: `LocalPosition`
+ --> src/app.rs:51:44
+ |
+51 | use crate::window::{AppState, WindowEvent, LocalPosition};
+ | ^^^^^^^^^^^^^
+
+warning: unused import: `clear_ui::engine::Vertex`
+ --> src/graphics.rs:1:5
+ |
+1 | use clear_ui::engine::Vertex;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: unused import: `std::time::Instant`
+ --> src/window.rs:1:5
+ |
+1 | use std::time::Instant;
+ | ^^^^^^^^^^^^^^^^^^
+
+warning: unused import: `std::fs`
+ --> src/window.rs:2:5
+ |
+2 | use std::fs;
+ | ^^^^^^^
+
+warning: unused import: `std::net::TcpListener`
+ --> src/window.rs:4:5
+ |
+4 | use std::net::TcpListener;
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+warning: unused imports: `BufReader` and `Write`
+ --> src/window.rs:5:24
+ |
+5 | use std::io::{BufRead, BufReader, Read, Write};
+ | ^^^^^^^^^ ^^^^^
+
+warning: unused imports: `Deserialize` and `Serialize`
+ --> src/window.rs:7:13
+ |
+7 | use serde::{Deserialize, Serialize};
+ | ^^^^^^^^^^^ ^^^^^^^^^
+
+warning: unused imports: `ElementState`, `KeyEvent`, `Key`, `MouseButton`, `MouseScrollDelta`, and `NamedKey`
+ --> src/window.rs:9:24
+ |
+9 | use clear_ui::widget::{ElementState, MouseButton, MouseScrollDelta, KeyEvent, Key, NamedKey};
+ | ^^^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^^ ^^^^^^^^
+
+warning: unused import: `WindowDecorations`
+ --> src/window.rs:24:75
+ |
+24 | window::{Window as XdgWindow, WindowConfigure, WindowHandler, WindowDecorations},
+ | ^^^^^^^^^^^^^^^^^
+
+warning: unused imports: `Proxy`, `globals::registry_queue_init`, and `wl_shm`
+ --> src/window.rs:32:5
+ |
+32 | globals::registry_queue_init,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+33 | protocol::{wl_keyboard, wl_output, wl_pointer, wl_seat, wl_shm, wl_surface},
+ | ^^^^^^
+34 | Connection, QueueHandle, Proxy,
+ | ^^^^^
+
+warning: unused import: `calloop_wayland_source::WaylandSource`
+ --> src/window.rs:36:5
+ |
+36 | use calloop_wayland_source::WaylandSource;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: unused import: `wgpu::util::DeviceExt`
+ --> src/window.rs:38:5
+ |
+38 | use wgpu::util::DeviceExt;
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+warning: unused imports: `Breadcrumb`, `Button`, `Canvas`, `Checkbox`, `ColorSelector`, `GraphNode`, `Graph`, `Label`, `MenuBar`, `Paginator`, `ParametersBg`, `Plate`, `ScrollingList`, `Slider`, `Spinbox`, `Splitter`, `Spreadsheet`, `StatusBar`, `Switcher`, `TextLabel`, and `ViewportBg`
+ --> src/window.rs:39:24
+ |
+39 | ...::{Breadcrumb, Canvas, MenuBar, Plate, ParametersBg, Splitter, Spreadsheet, StatusBar, TextLabel, ViewportBg, Element, GraphNode, Graph, Paginator, Button, Checkbox, Slider, Spinbox, ScrollingList, Label, ColorSelector, Switcher};
+ | ^^^^^^^^^^ ^^^^^^ ^^^^^^^ ^^^^^ ^^^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^ ^^^^^ ^^^^^^^^^ ^^^^^^ ^^^^^^^^ ^^^^^^ ^^^^^^^ ^^^^^^^^^^^^^ ^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^
+
+warning: unused import: `clear_ui::colors`
+ --> src/window.rs:40:5
+ |
+40 | use clear_ui::colors;
+ | ^^^^^^^^^^^^^^^^
+
+warning: unused imports: `Attrs`, `Buffer`, `Cache`, `FontSystem`, `Metrics`, `Resolution`, `SwashCache`, `TextArea`, `TextAtlas`, `TextBounds`, `TextRenderer`, and `Viewport`
+ --> src/window.rs:41:15
+ |
+41 | ...::{Attrs, Buffer, Cache, FontSystem, Metrics, Resolution, SwashCache, TextArea, TextAtlas, TextBounds, TextRenderer, Viewport};
+ | ^^^^^ ^^^^^^ ^^^^^ ^^^^^^^^^^ ^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^
+
+warning: unused imports: `Mat4` and `Vec3`
+ --> src/window.rs:42:12
+ |
+42 | use glam::{Mat4, Vec3};
+ | ^^^^ ^^^^
+
+warning: unused import: `crate::geometry::*`
+ --> src/window.rs:44:5
+ |
+44 | use crate::geometry::*;
+ | ^^^^^^^^^^^^^^^^^^
+
+warning: unused import: `crate::project::*`
+ --> src/window.rs:45:5
+ |
+45 | use crate::project::*;
+ | ^^^^^^^^^^^^^^^^^
+
+warning: unused import: `crate::render::*`
+ --> src/window.rs:46:5
+ |
+46 | use crate::render::*;
+ | ^^^^^^^^^^^^^^^^
+
+warning: unused imports: `ShortcutManager` and `Shortcut`
+ --> src/window.rs:47:23
+ |
+47 | use crate::shortcut::{Shortcut, ShortcutManager, Action};
+ | ^^^^^^^^ ^^^^^^^^^^^^^^^
+
+warning: unused imports: `DesignSettings`, `NODE_PALETTE_IDX`, and `NodePalette`
+ --> src/window.rs:48:78
+ |
+48 | ...tion, ModifiersState, TouchPhase, DesignSettings, param_display, NodePalette, NODE_PALETTE_IDX, LEFT_MENUBAR_IDX, RIGHT_MENUBAR_I...
+ | ^^^^^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
+
+warning: unused import: `clear_ui::engine::Vertex`
+ --> src/window.rs:49:5
+ |
+49 | use clear_ui::engine::Vertex;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: unused import: `PathBuf`
+ --> src/project.rs:2:23
+ |
+2 | use std::path::{Path, PathBuf};
+ | ^^^^^^^
+
+warning: unused import: `std::time::Instant`
+ --> src/project.rs:3:5
+ |
+3 | use std::time::Instant;
+ | ^^^^^^^^^^^^^^^^^^
+
+warning: unused import: `std::time::Instant`
+ --> src/render.rs:1:5
+ |
+1 | use std::time::Instant;
+ | ^^^^^^^^^^^^^^^^^^
+
+warning: unused import: `std::path::Path`
+ --> src/render.rs:2:5
+ |
+2 | use std::path::Path;
+ | ^^^^^^^^^^^^^^^
+
+warning: unused imports: `CustomEvent`, `HEADER_H`, `SPLITTER1_IDX`, `SPLITTER2_IDX`, and `SPLITTER_W`
+ --> src/render.rs:10:20
+ |
+10 | State, FsNode, CustomEvent,
+ | ^^^^^^^^^^^
+...
+14 | SPREADSHEET_MENUBAR_IDX, SPLITTER1_IDX, SPLITTER2_IDX,
+ | ^^^^^^^^^^^^^ ^^^^^^^^^^^^^
+15 | MENUBAR_H, STATUS_H, HEADER_H, SPLITTER_W,
+ | ^^^^^^^^ ^^^^^^^^^^
+
+warning: unused import: `network_sphere_vertices_with_errors`
+ --> src/render.rs:22:5
+ |
+22 | network_sphere_vertices_with_errors,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: unused imports: `ParamDef`, `TouchPhase`, and `param_display`
+ --> src/main.rs:53:59
+ |
+53 | ...ion, ModifiersState, TouchPhase, DesignSettings, param_display, FsNode, Project, ProjectViewState, ParamDef};
+ | ^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^
+
+warning: unused import: `api::start_http_server`
+ --> src/main.rs:55:5
+ |
+55 | use api::start_http_server;
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+warning: unused import: `project::*`
+ --> src/main.rs:57:5
+ |
+57 | use project::*;
+ | ^^^^^^^^^^
+
+warning: unused import: `render::*`
+ --> src/main.rs:58:5
+ |
+58 | use render::*;
+ | ^^^^^^^^^
+
+warning: use of deprecated type alias `wgpu::ImageCopyTexture`: This has been renamed to `TexelCopyTextureInfo`, and will be removed in 25.0.0.
+ --> src/app.rs:5548:23
+ |
+5548 | wgpu::ImageCopyTexture {
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(deprecated)]` on by default
+
+warning: use of deprecated type alias `wgpu::ImageCopyTexture`: This has been renamed to `TexelCopyTextureInfo`, and will be removed in 25.0.0.
+ --> src/app.rs:5554:23
+ |
+5554 | wgpu::ImageCopyTexture {
+ | ^^^^^^^^^^^^^^^^
+
+warning: use of deprecated associated function `opencl3::command_queue::CommandQueue::create`: From CL_VERSION_2_0 use create_command_queue_with_properties
+ --> src/geometry.rs:400:40
+ |
+400 | let queue = unsafe { CommandQueue::create(&context, device_id, 0) }.ok()?;
+ | ^^^^^^
+
+warning: unused import: `Write`
+ --> src/app.rs:5:41
+ |
+5 | use std::io::{BufRead, BufReader, Read, Write};
+ | ^^^^^
+
+warning: unused import: `BufRead`
+ --> src/app.rs:5:15
+ |
+5 | use std::io::{BufRead, BufReader, Read, Write};
+ | ^^^^^^^
+
+warning: unused import: `Read`
+ --> src/app.rs:5:35
+ |
+5 | use std::io::{BufRead, BufReader, Read, Write};
+ | ^^^^
+
+warning: unused import: `WindowHandler`
+ --> src/app.rs:24:60
+ |
+24 | window::{Window as XdgWindow, WindowConfigure, WindowHandler, WindowDecorations},
+ | ^^^^^^^^^^^^^
+
+warning: unused import: `WaylandSurface`
+ --> src/window.rs:27:9
+ |
+27 | WaylandSurface,
+ | ^^^^^^^^^^^^^^
+
+warning: unused import: `Read`
+ --> src/window.rs:5:35
+ |
+5 | use std::io::{BufRead, BufReader, Read, Write};
+ | ^^^^
+
+warning: unused import: `BufRead`
+ --> src/window.rs:5:15
+ |
+5 | use std::io::{BufRead, BufReader, Read, Write};
+ | ^^^^^^^
+
+warning: unused import: `Element`
+ --> src/project.rs:5:32
+ |
+5 | use clear_ui::widget::{Button, Element};
+ | ^^^^^^^
+
+warning: unused import: `OutputHandler`
+ --> src/main.rs:16:14
+ |
+16 | output::{OutputHandler, OutputState},
+ | ^^^^^^^^^^^^^
+
+warning: unused import: `WaylandSurface`
+ --> src/main.rs:27:9
+ |
+27 | WaylandSurface,
+ | ^^^^^^^^^^^^^^
+
+warning: unused import: `Element`
+ --> src/main.rs:39:131
+ |
+39 | ...Bg, Splitter, Spreadsheet, StatusBar, TextLabel, ViewportBg, Element, GraphNode, Graph, Paginator, Button, Checkbox, Slider, Spin...
+ | ^^^^^^^
+
+warning: unused variable: `surface`
+ --> src/app.rs:2036:13
+ |
+2036 | let surface = &wgpu_adapter.surface;
+ | ^^^^^^^ help: if this is intentional, prefix it with an underscore: `_surface`
+ |
+ = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default
+
+warning: variable `sp_menub_y` is assigned to, but never used
+ --> src/app.rs:3137:21
+ |
+3137 | let mut sp_menub_y = 0.0;
+ | ^^^^^^^^^^^^^^
+ |
+ = note: consider using `_sp_menub_y` instead
+
+warning: variable `sp_menub_h` is assigned to, but never used
+ --> src/app.rs:3138:21
+ |
+3138 | let mut sp_menub_h = 0.0;
+ | ^^^^^^^^^^^^^^
+ |
+ = note: consider using `_sp_menub_h` instead
+
+warning: value assigned to `sp_menub_y` is never read
+ --> src/app.rs:3156:25
+ |
+3156 | sp_menub_y = 0.0;
+ | ^^^^^^^^^^^^^^^^
+ |
+ = help: maybe it is overwritten before being read?
+ = note: `#[warn(unused_assignments)]` (part of `#[warn(unused)]`) on by default
+
+warning: value assigned to `sp_menub_y` is never read
+ --> src/app.rs:3148:25
+ |
+3148 | sp_menub_y = 0.0;
+ | ^^^^^^^^^^^^^^^^
+ |
+ = help: maybe it is overwritten before being read?
+
+warning: value assigned to `sp_menub_h` is never read
+ --> src/app.rs:3157:25
+ |
+3157 | sp_menub_h = 0.0;
+ | ^^^^^^^^^^^^^^^^
+ |
+ = help: maybe it is overwritten before being read?
+
+warning: value assigned to `sp_menub_h` is never read
+ --> src/app.rs:3149:25
+ |
+3149 | sp_menub_h = 0.0;
+ | ^^^^^^^^^^^^^^^^
+ |
+ = help: maybe it is overwritten before being read?
+
+warning: unused variable: `cx`
+ --> src/window.rs:276:22
+ |
+276 | let (cx, cy) = clear_ui::wayland::scale_pointer_pos(event.position, st.scale);
+ | ^^ help: if this is intentional, prefix it with an underscore: `_cx`
+
+warning: unused variable: `cy`
+ --> src/window.rs:276:26
+ |
+276 | let (cx, cy) = clear_ui::wayland::scale_pointer_pos(event.position, st.scale);
+ | ^^ help: if this is intentional, prefix it with an underscore: `_cy`
+
+warning: unused variable: `idx`
+ --> src/geometry.rs:656:17
+ |
+656 | let idx = *count;
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_idx`
+
+warning: unused variable: `idx`
+ --> src/geometry.rs:665:17
+ |
+665 | let idx = *count;
+ | ^^^ help: if this is intentional, prefix it with an underscore: `_idx`
+
+For more information about this error, try `rustc --explain E0433`.
+warning: `cce-design-interface` (bin "cce-design-interface" test) generated 77 warnings
+error: could not compile `cce-design-interface` (bin "cce-design-interface" test) due to 9 previous errors; 77 warnings emitted
diff --git a/scratch/test_selection.py b/scratch/test_selection.py
new file mode 100644
index 0000000..875770e
--- /dev/null
+++ b/scratch/test_selection.py
@@ -0,0 +1,29 @@
+import subprocess
+import time
+
+def run_clearctl(cmd_str):
+ full_cmd = f"env WAYLAND_DISPLAY=wayland-0 XDG_RUNTIME_DIR=/run/user/1000 /home/lsgalante/.local/bin/clearctl {cmd_str}"
+ res = subprocess.run(full_cmd, shell=True, capture_output=True, text=True)
+ return res.stdout.strip()
+
+def click_at(x, y):
+ print(f"Moving to {x}, {y}...")
+ run_clearctl(f"pointer-move-to {x} {y}")
+ time.sleep(0.2)
+ print(f"Clicking at {x}, {y}...")
+ run_clearctl("pointer-click left")
+ time.sleep(0.3)
+
+# Focus the design interface window
+run_clearctl("focus-window cce-design-interface")
+time.sleep(0.5)
+
+# Click on Box 1 (x=238, y=389)
+click_at(238, 389)
+subprocess.run("env WAYLAND_DISPLAY=wayland-0 XDG_RUNTIME_DIR=/run/user/1000 grim /home/lsgalante/.gemini/antigravity/brain/ab8b2bd9-c7ba-4730-96bb-8766ee711f3a/screenshot_click_box.png", shell=True)
+
+# Click on Scatter 1 (x=238, y=469)
+click_at(238, 469)
+subprocess.run("env WAYLAND_DISPLAY=wayland-0 XDG_RUNTIME_DIR=/run/user/1000 grim /home/lsgalante/.gemini/antigravity/brain/ab8b2bd9-c7ba-4730-96bb-8766ee711f3a/screenshot_click_scatter.png", shell=True)
+
+print("Done.")
diff --git a/src/app.rs b/src/app.rs
index 0a7a4b7..9f31eb6 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -135,6 +135,9 @@ pub fn generate_node_id() -> String {
format!("node_{:x}_{:x}", now, count)
}
+fn default_node_inputs() -> usize { 1 }
+fn default_node_outputs() -> usize { 1 }
+
#[derive(Clone, Deserialize, Serialize)]
pub struct FsNode {
#[serde(default = "generate_node_id")]
@@ -152,6 +155,10 @@ pub struct FsNode {
pub geometry_visible: bool,
#[serde(default = "default_node_position")]
pub position: (f32, f32),
+ #[serde(default = "default_node_inputs")]
+ pub inputs: usize,
+ #[serde(default = "default_node_outputs")]
+ pub outputs: usize,
}
fn default_node_type() -> String { "node".to_string() }
@@ -237,6 +244,8 @@ pub fn param_display(params: &[ParamDef]) -> Vec<(String, String, String)> {
let max = p.max.unwrap_or(10000.0) as i32;
let step = p.step.unwrap_or(1.0) as i32;
format!("spinbox:{}:{}:{}", min, max, step)
+ } else if p.param_type == "choice" {
+ format!("choice:{}", p.options.join(","))
} else {
p.param_type.clone()
};
@@ -245,23 +254,9 @@ pub fn param_display(params: &[ParamDef]) -> Vec<(String, String, String)> {
}
pub fn flatten_node_templates(root: &FsNode) -> Vec<NodeTemplate> {
- fn visit(node: &FsNode, path: &mut Vec<String>, out: &mut Vec<NodeTemplate>) {
- if !node.name.is_empty() {
- path.push(node.name.clone());
- out.push(NodeTemplate { label: path.join(" / "), node: node.clone() });
- }
- for child in &node.children {
- visit(child, path, out);
- }
- if !node.name.is_empty() {
- path.pop();
- }
- }
-
let mut out = Vec::new();
- let mut path = Vec::new();
for child in &root.children {
- visit(child, &mut path, &mut out);
+ out.push(NodeTemplate { label: child.name.clone(), node: child.clone() });
}
out
}
@@ -276,12 +271,46 @@ pub fn load_fs_tree() -> FsNode {
.filter(|p| p.extension().and_then(|e| e.to_str()) == Some("json"))
.collect();
paths.sort();
+
+ let mut raw_nodes = Vec::new();
for path in paths {
if let Ok(content) = fs::read_to_string(&path) {
if let Ok(node) = serde_json::from_str::<FsNode>(&content) {
- children.push(node);
+ raw_nodes.push(node);
+ }
+ }
+ }
+
+ for mut node in raw_nodes.clone() {
+ let mut resolved_children = Vec::new();
+ for child in &node.children {
+ let base_template = raw_nodes.iter().find(|t| {
+ t.node_type == child.node_type || t.name.to_lowercase() == child.node_type.to_lowercase()
+ });
+ if let Some(base) = base_template {
+ let mut resolved_child = base.clone();
+ resolved_child.id = child.id.clone();
+ if resolved_child.id.is_empty() || resolved_child.id == "node_0_0" || resolved_child.id.starts_with("node_") {
+ resolved_child.id = generate_node_id();
+ }
+ resolved_child.name = child.name.clone();
+ resolved_child.position = child.position;
+ // Merge parameters
+ for override_p in &child.params {
+ if let Some(base_p) = resolved_child.params.iter_mut().find(|p| p.name == override_p.name) {
+ base_p.default = override_p.default.clone();
+ }
+ }
+ resolved_children.push(resolved_child);
+ } else {
+ panic!(
+ "Node template of type '{}' not found for child '{}' in template '{}'",
+ child.node_type, child.name, node.name
+ );
}
}
+ node.children = resolved_children;
+ children.push(node);
}
}
FsNode {
@@ -292,6 +321,8 @@ pub fn load_fs_tree() -> FsNode {
params: vec![],
geometry_visible: true,
position: (0.0, 0.0),
+ inputs: 0,
+ outputs: 0,
}
}
@@ -302,13 +333,13 @@ fn default_node_color() -> [f32; 3] { [0.10, 0.45, 0.70] }
fn default_grid_color() -> [f32; 3] { [0.35, 0.35, 0.40] }
fn default_uniform_background() -> bool { false }
fn default_network_opacity() -> f32 { 0.95 }
+fn default_cell_opacity() -> f32 { 0.95 }
+fn default_gap_opacity() -> f32 { 0.95 }
fn default_cell_color() -> [f32; 3] { [0.13, 0.13, 0.16] }
fn default_gap_color() -> [f32; 3] { [0.07, 0.07, 0.09] }
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct DesignSettings {
- pub grid_snap_enabled: bool,
- pub network_grid_enabled: bool,
pub grid_size_x: f32,
pub grid_size_y: f32,
pub skipped_row_h: f32,
@@ -329,10 +360,6 @@ pub struct DesignSettings {
pub node_color: [f32; 3],
#[serde(default = "default_grid_color")]
pub grid_color: [f32; 3],
- #[serde(default = "default_uniform_background")]
- pub uniform_background: bool,
- #[serde(default = "default_network_opacity")]
- pub network_opacity: f32,
#[serde(default = "default_cell_color")]
pub cell_color: [f32; 3],
#[serde(default = "default_gap_color")]
@@ -342,8 +369,6 @@ pub struct DesignSettings {
impl Default for DesignSettings {
fn default() -> Self {
Self {
- grid_snap_enabled: true,
- network_grid_enabled: true,
grid_size_x: 80.0,
grid_size_y: 40.0,
skipped_row_h: 20.0,
@@ -359,8 +384,6 @@ impl Default for DesignSettings {
camera_pivot_size: 1.0,
node_color: default_node_color(),
grid_color: default_grid_color(),
- uniform_background: false,
- network_opacity: 0.95,
cell_color: default_cell_color(),
gap_color: default_gap_color(),
}
@@ -380,11 +403,10 @@ impl DesignSettings {
fn load() -> Self {
let path = Self::file_path();
if let Ok(content) = fs::read_to_string(&path) {
- if let Ok(settings) = serde_json::from_str::<Self>(&content) {
- return settings;
- }
+ serde_json::from_str::<Self>(&content).unwrap_or_else(|_| Self::default())
+ } else {
+ Self::default()
}
- Self::default()
}
fn save(&self) {
@@ -510,7 +532,7 @@ pub fn make_text_buffer_with_font(font_system: &mut FontSystem, text: &str, size
let family_name = font.map(|f| clear_ui::layout::parse_font_string(f).0);
if let Some(ref name) = family_name {
let family = match name.as_str() {
- "monospace" => glyphon::Family::Monospace,
+ "monospace" => glyphon::Family::Name(clear_ui::layout::get_system_monospace_font()),
"sans-serif" => glyphon::Family::SansSerif,
"serif" => glyphon::Family::Serif,
_ => glyphon::Family::Name(name),
@@ -710,6 +732,7 @@ pub struct State {
pub fs_root: FsNode,
pub node_templates: Vec<NodeTemplate>,
pub current_path: Vec<usize>,
+ pub node_clipboard: Option<FsNode>,
pub last_click: Option<(Instant, usize)>,
pub last_frame: Instant,
@@ -809,6 +832,8 @@ pub struct State {
pub active_menu_cloud_idx: Option<(usize, usize)>,
pub uniform_background: bool,
pub network_opacity: f32,
+ pub cell_opacity: f32,
+ pub gap_opacity: f32,
pub last_design_mod_time: Option<std::time::SystemTime>,
pub last_config_mod_time: Option<std::time::SystemTime>,
pub floating_network_layout: (f32, f32, f32, f32),
@@ -830,6 +855,9 @@ pub struct State {
pub recent_files_buttons: Vec<Button>,
pub text_buffer_cache: std::collections::HashMap<(String, u32, Option<String>), Buffer>,
pub viewport_dirty: bool,
+ pub text_dirty: bool,
+ pub last_popover_rects: Vec<(f32, f32, f32, f32)>,
+ pub last_status_text: String,
pub last_viewport_camera_pos: Vec3,
pub last_viewport_camera_rx: f32,
pub last_viewport_camera_ry: f32,
@@ -888,6 +916,10 @@ impl State {
false
}
+ pub fn palette(&self) -> &NodePalette {
+ self.widgets[NODE_PALETTE_IDX].as_any().downcast_ref::<NodePalette>().expect("not a NodePalette")
+ }
+
pub fn palette_mut(&mut self) -> &mut NodePalette {
self.widgets[NODE_PALETTE_IDX].as_any_mut().downcast_mut::<NodePalette>().expect("not a NodePalette")
}
@@ -999,8 +1031,6 @@ impl State {
pub fn save_settings(&mut self) {
let settings = DesignSettings {
- grid_snap_enabled: self.grid_snap_enabled,
- network_grid_enabled: self.network_grid_visible,
grid_size_x: self.grid_size_x,
grid_size_y: self.grid_size_y,
skipped_row_h: self.skipped_row_h,
@@ -1018,8 +1048,6 @@ impl State {
grid_thickness: self.grid_thickness,
show_camera_pivot_enabled: self.show_camera_pivot,
camera_pivot_size: self.camera_pivot_size,
- uniform_background: self.uniform_background,
- network_opacity: self.network_opacity,
};
settings.save();
self.last_design_mod_time = {
@@ -1028,238 +1056,7 @@ impl State {
};
}
- pub fn sync_settings_from_paginator(&mut self) {
- let active_menubar = self.focused_pane;
- let page_idx = if active_menubar == LEFT_MENUBAR_IDX {
- 3
- } else if active_menubar == RIGHT_MENUBAR_IDX {
- 4
- } else {
- return;
- };
-
- if page_idx >= self.paginator_page_widgets.len() {
- return;
- }
-
- let mut left_values = None;
- let mut right_values = None;
-
- {
- let widgets = &self.paginator_page_widgets[page_idx];
- if widgets.is_empty() {
- return;
- }
-
- if active_menubar == LEFT_MENUBAR_IDX {
- if widgets.len() >= 11 {
- let snap = widgets[0].value();
- let vis = widgets[1].value();
- let gx = widgets[2].value();
- let gy = widgets[3].value();
- let srh = widgets[4].value();
- let scw = widgets[5].value();
- let uni = widgets[6].value();
- let op = widgets[7].value();
- let node_col = widgets[8].color_u8();
- let cell_col = widgets[9].color_u8();
- let gap_col = widgets[10].color_u8();
- left_values = Some((snap, vis, gx, gy, srh, scw, uni, op, node_col, cell_col, gap_col));
- }
- } else if active_menubar == RIGHT_MENUBAR_IDX {
- if widgets.len() >= 13 {
- let sg = widgets[0].value();
- let sc = widgets[1].value();
- let so = widgets[2].value();
- let cp = widgets[3].value();
- let gt = widgets[4].value();
- let os = widgets[5].value();
- let cps = widgets[6].value();
- let bgr = widgets[7].value();
- let bgg = widgets[8].value();
- let bgb = widgets[9].value();
- let gcr = widgets[10].value();
- let gcg = widgets[11].value();
- let gcb = widgets[12].value();
- right_values = Some((sg, sc, so, cp, gt, os, cps, bgr, bgg, bgb, gcr, gcg, gcb));
- }
- }
- }
-
- let mut changed = false;
- if let Some((snap, vis, gx, gy, srh, scw, uni, op, node_col, cell_col, gap_col)) = left_values {
- // LEFT pane Settings
- // 0: Snap to Grid (Checkbox)
- let snap = snap == 1;
- if self.grid_snap_enabled != snap {
- self.grid_snap_enabled = snap;
- changed = true;
- }
- // 1: Grid Visible (Checkbox)
- let vis = vis == 1;
- if self.network_grid_visible != vis {
- self.network_grid_visible = vis;
- changed = true;
- }
- // 2: Grid X (Spinbox)
- let gx = gx as f32;
- if self.grid_size_x != gx {
- self.grid_size_x = gx;
- changed = true;
- }
- // 3: Grid Y (Spinbox)
- let gy = gy as f32;
- if self.grid_size_y != gy {
- self.grid_size_y = gy;
- changed = true;
- }
- // 4: Skipped Row H (Spinbox)
- let srh = srh as f32;
- if self.skipped_row_h != srh {
- self.skipped_row_h = srh;
- changed = true;
- }
- // 5: Skipped Col W (Spinbox)
- let scw = scw as f32;
- if self.skipped_col_w != scw {
- self.skipped_col_w = scw;
- changed = true;
- }
- // 6: Uniform Background (Checkbox)
- let uni = uni == 1;
- if self.uniform_background != uni {
- self.uniform_background = uni;
- changed = true;
- }
- // 7: Opacity (Slider, 0..100)
- let op = op as f32 / 100.0;
- if (self.network_opacity - op).abs() > 0.001 {
- self.network_opacity = op;
- changed = true;
- }
- // 8: Node Color (ColorSelector)
- if let Some(col) = node_col {
- let nr = col[0] as f32 / 255.0;
- let ng = col[1] as f32 / 255.0;
- let nb = col[2] as f32 / 255.0;
- if (self.node_color[0] - nr).abs() > 0.001
- || (self.node_color[1] - ng).abs() > 0.001
- || (self.node_color[2] - nb).abs() > 0.001
- {
- self.node_color = [nr, ng, nb];
- colors::set_node_color([nr, ng, nb, 1.0]);
- changed = true;
- }
- }
- // 9: Cell Color (ColorSelector)
- if let Some(col) = cell_col {
- let cr = col[0] as f32 / 255.0;
- let cg = col[1] as f32 / 255.0;
- let cb = col[2] as f32 / 255.0;
- if (self.cell_color[0] - cr).abs() > 0.001
- || (self.cell_color[1] - cg).abs() > 0.001
- || (self.cell_color[2] - cb).abs() > 0.001
- {
- self.cell_color = [cr, cg, cb];
- changed = true;
- }
- }
- // 10: Gap Color (ColorSelector)
- if let Some(col) = gap_col {
- let gr = col[0] as f32 / 255.0;
- let gg = col[1] as f32 / 255.0;
- let gb = col[2] as f32 / 255.0;
- if (self.gap_color[0] - gr).abs() > 0.001
- || (self.gap_color[1] - gg).abs() > 0.001
- || (self.gap_color[2] - gb).abs() > 0.001
- {
- self.gap_color = [gr, gg, gb];
- changed = true;
- }
- }
- } else if let Some((sg, sc, so, cp, gt, os, cps, bgr, bgg, bgb, gcr, gcg, gcb)) = right_values {
- // RIGHT pane Settings
- // 0: Show Grid Guide (Checkbox)
- let sg = sg == 1;
- if self.show_grid != sg {
- self.show_grid = sg;
- self.menu_mut(RIGHT_MENUBAR_IDX).set_item_checked(2, 0, sg);
- changed = true;
- }
- // 1: Show Reference Cube (Checkbox)
- let sc = sc == 1;
- if self.show_cube != sc {
- self.show_cube = sc;
- self.menu_mut(RIGHT_MENUBAR_IDX).set_item_checked(2, 1, sc);
- changed = true;
- }
- // 2: Show Origin Axes (Checkbox)
- let so = so == 1;
- if self.show_origin != so {
- self.show_origin = so;
- self.menu_mut(RIGHT_MENUBAR_IDX).set_item_checked(2, 2, so);
- changed = true;
- }
- // 3: Show Camera Pivot (Checkbox)
- let cp = cp == 1;
- if self.show_camera_pivot != cp {
- self.show_camera_pivot = cp;
- self.menu_mut(RIGHT_MENUBAR_IDX).set_item_checked(2, 3, cp);
- changed = true;
- }
- // 4: Grid Thickness (Spinbox, decimals 3)
- let gt = gt as f32 / 1000.0;
- if (self.grid_thickness - gt).abs() > 0.0001 {
- self.grid_thickness = gt;
- self.update_grid_geometry();
- changed = true;
- }
- // 5: Origin Guide Size (Spinbox, decimals 1)
- let os = os as f32 / 10.0;
- if (self.origin_size - os).abs() > 0.001 {
- self.origin_size = os;
- self.update_origin_geometry();
- changed = true;
- }
- // 6: Camera Pivot Size (Spinbox, decimals 1)
- let cps = cps as f32 / 10.0;
- if (self.camera_pivot_size - cps).abs() > 0.001 {
- self.camera_pivot_size = cps;
- self.update_pivot_geometry();
- changed = true;
- }
- // 7, 8, 9: BG Color (R, G, B Spinboxes, 0..255)
- let bgr = bgr as f32 / 255.0;
- let bgg = bgg as f32 / 255.0;
- let bgb = bgb as f32 / 255.0;
- if (self.viewport_bg_color[0] - bgr).abs() > 0.001
- || (self.viewport_bg_color[1] - bgg).abs() > 0.001
- || (self.viewport_bg_color[2] - bgb).abs() > 0.001
- {
- self.viewport_bg_color = [bgr, bgg, bgb];
- changed = true;
- }
- // 10, 11, 12: Grid Color (R, G, B Spinboxes, 0..255)
- let gcr = gcr as f32 / 255.0;
- let gcg = gcg as f32 / 255.0;
- let gcb = gcb as f32 / 255.0;
- if (self.grid_color[0] - gcr).abs() > 0.001
- || (self.grid_color[1] - gcg).abs() > 0.001
- || (self.grid_color[2] - gcb).abs() > 0.001
- {
- self.grid_color = [gcr, gcg, gcb];
- self.update_grid_geometry();
- changed = true;
- }
- }
-
- if changed {
- self.sync_grid_settings();
- self.save_settings();
- self.upload_vertices();
- }
- }
pub fn update_grid_geometry(&mut self) {
let grid_verts = grid_vertices(self.grid_thickness, self.grid_color);
@@ -1375,30 +1172,348 @@ impl State {
node
}
+ pub fn get_lowest_unused_name(&self, base_name: &str) -> String {
+ let dir = self.current_dir();
+ let mut index = 1;
+ loop {
+ let candidate = format!("{} {}", base_name, index);
+ if !dir.children.iter().any(|c| c.name == candidate) {
+ return candidate;
+ }
+ index += 1;
+ }
+ }
+
pub fn sync_parameters_to_project(&mut self) {
if !self.is_detached_network {
if let Some(slot_idx) = self.graph().selected_node() {
let updated_params = self.param().node_params();
+ let mut recent_file_to_open = None;
let dir = self.current_dir_mut();
if let Some(child) = dir.children.get_mut(slot_idx) {
let mut param_changed = false;
+ let mut triggered_buttons = Vec::new();
for (u_name, u_val, _) in &updated_params {
if let Some(p) = child.params.iter_mut().find(|p| p.name == *u_name) {
if p.default != *u_val {
p.default = u_val.clone();
param_changed = true;
+ if p.param_type == "button" && p.default == "clicked" {
+ triggered_buttons.push(p.name.clone());
+ p.default = "".to_string();
+ }
+ if p.name == "Open Recent" && p.default != "- Select -" && !p.default.is_empty() {
+ recent_file_to_open = Some(p.default.clone());
+ p.default = "- Select -".to_string();
+ }
+ }
+ }
+ }
+
+ if !triggered_buttons.is_empty() || recent_file_to_open.is_some() {
+ let mut disp_params = self.param().node_params();
+ for btn_name in &triggered_buttons {
+ if let Some(pos) = disp_params.iter().position(|p| p.0 == *btn_name) {
+ disp_params[pos].1 = "".to_string();
+ }
+ }
+ if recent_file_to_open.is_some() {
+ if let Some(pos) = disp_params.iter().position(|p| p.0 == "Open Recent") {
+ disp_params[pos].1 = "- Select -".to_string();
}
}
+ self.param_mut().set_display_params(&disp_params);
}
+
if param_changed {
+ self.apply_settings_from_menubar_subnets();
+ self.sync_grid_settings();
self.rebuild_scene_geometry();
self.sync_nodes();
+
+ for btn_name in triggered_buttons {
+ match btn_name.as_str() {
+ "Update Parameters" => {
+ if let Some(slot_idx) = self.graph().selected_node() {
+ let dir = self.current_dir_mut();
+ if let Some(child) = dir.children.get_mut(slot_idx) {
+ if child.node_type == "opencl" {
+ let code_val = child.params.iter()
+ .find(|p| p.name == "Code")
+ .map(|p| p.default.clone())
+ .unwrap_or_default();
+ let parsed_params = crate::geometry::parse_dynamic_params(&code_val);
+ let mut new_params = Vec::new();
+ for base_name in &["Input", "Code", "Update Parameters"] {
+ if let Some(p) = child.params.iter().find(|p| p.name == *base_name) {
+ new_params.push(p.clone());
+ }
+ }
+ for mut parsed in parsed_params {
+ if let Some(existing) = child.params.iter().find(|p| p.name == parsed.name) {
+ parsed.default = existing.default.clone();
+ }
+ new_params.push(parsed);
+ }
+ child.params = new_params;
+ let updated_disp = param_display(&child.params);
+ self.param_mut().set_display_params(&updated_disp);
+ self.rebuild_scene_geometry();
+ self.sync_nodes();
+ }
+ }
+ }
+ }
+ "New Project" | "New" => {
+ self.new_project();
+ }
+ "Open" => {
+ self.open_file_chooser();
+ }
+ "Save" => {
+ let path_opt = self.loaded_project_path.clone();
+ if let Some(path) = path_opt {
+ if let Err(e) = self.save_to_file(&path) {
+ eprintln!("Failed to save project: {:?}", e);
+ self.update_status_text(&format!("Failed to save: {:?}", e));
+ } else {
+ self.update_status_text(&format!("Project saved to {}", path.display()));
+ self.add_recent_file(path);
+ }
+ } else {
+ self.save_file_chooser();
+ }
+ }
+ "Save As" => {
+ self.save_file_chooser();
+ }
+ "Exit" => {
+ self.exit_requested = true;
+ }
+ "Zoom In" => {
+ self.zoom(1.15, None);
+ }
+ "Zoom Out" => {
+ self.zoom(1.0 / 1.15, None);
+ }
+ "Reset Zoom" => {
+ self.grid_size_x = 150.0;
+ self.grid_size_y = 75.0;
+ self.skipped_col_w = 37.5;
+ self.skipped_row_h = 37.5;
+ self.sync_grid_settings();
+ }
+ "Detach Circular Window" | "Detach Pane" => {
+ self.execute_action(Action::DetachCircularWindow);
+ }
+ "Show Network Pane" => {
+ self.show_network = !self.show_network;
+ self.widgets[CONTENT_IDX].set_visible(self.show_network);
+ self.widgets[LEFT_MENUBAR_IDX].set_visible(self.show_network);
+ self.widgets[BREADCRUMB_IDX].set_visible(self.show_network);
+ let val = self.show_network;
+ self.menu_mut(HEADER_IDX).set_item_checked(2, 4, val);
+ if !self.show_network && self.focused_pane == LEFT_MENUBAR_IDX {
+ self.focused_pane = get_next_visible_pane(
+ self.focused_pane,
+ self.show_network,
+ self.show_viewport,
+ self.show_parameters,
+ self.show_spreadsheet,
+ false,
+ );
+ }
+ self.rebuild_positions();
+ self.apply_layout();
+ self.sync_pane_focus();
+ }
+ "Show Viewport Pane" => {
+ self.show_viewport = !self.show_viewport;
+ self.widgets[VIEWPORT_IDX].set_visible(self.show_viewport);
+ self.widgets[RIGHT_MENUBAR_IDX].set_visible(self.show_viewport);
+ let val = self.show_viewport;
+ self.menu_mut(HEADER_IDX).set_item_checked(2, 5, val);
+ if !self.show_viewport && self.focused_pane == RIGHT_MENUBAR_IDX {
+ self.focused_pane = get_next_visible_pane(
+ self.focused_pane,
+ self.show_network,
+ self.show_viewport,
+ self.show_parameters,
+ self.show_spreadsheet,
+ false,
+ );
+ }
+ self.rebuild_positions();
+ self.apply_layout();
+ self.sync_pane_focus();
+ }
+ "Show Parameters Pane" => {
+ self.show_parameters = !self.show_parameters;
+ self.widgets[PARAM_IDX].set_visible(self.show_parameters);
+ self.widgets[PARAM_MENUBAR_IDX].set_visible(self.show_parameters);
+ let val = self.show_parameters;
+ self.menu_mut(HEADER_IDX).set_item_checked(2, 6, val);
+ if !self.show_parameters && self.focused_pane == PARAM_MENUBAR_IDX {
+ self.focused_pane = get_next_visible_pane(
+ self.focused_pane,
+ self.show_network,
+ self.show_viewport,
+ self.show_parameters,
+ self.show_spreadsheet,
+ false,
+ );
+ }
+ self.rebuild_positions();
+ self.apply_layout();
+ self.sync_pane_focus();
+ }
+ "Show Spreadsheet Pane" => {
+ self.show_spreadsheet = !self.show_spreadsheet;
+ self.widgets[SPREADSHEET_IDX].set_visible(self.show_spreadsheet);
+ self.widgets[SPREADSHEET_MENUBAR_IDX].set_visible(self.show_spreadsheet);
+ let val = self.show_spreadsheet;
+ self.menu_mut(HEADER_IDX).set_item_checked(2, 7, val);
+ if !self.show_spreadsheet && self.focused_pane == SPREADSHEET_MENUBAR_IDX {
+ self.focused_pane = get_next_visible_pane(
+ self.focused_pane,
+ self.show_network,
+ self.show_viewport,
+ self.show_parameters,
+ self.show_spreadsheet,
+ false,
+ );
+ }
+ self.rebuild_positions();
+ self.apply_layout();
+ self.sync_pane_focus();
+ }
+ "Close Pane" => {
+ let mut parent_name = "";
+ if self.current_path.len() >= 1 {
+ let root_idx = self.current_path[0];
+ if let Some(r_node) = self.fs_root.children.get(root_idx) {
+ parent_name = r_node.name.as_str();
+ }
+ }
+ match parent_name {
+ "Network" => {
+ self.show_network = false;
+ self.widgets[CONTENT_IDX].set_visible(false);
+ self.widgets[LEFT_MENUBAR_IDX].set_visible(false);
+ self.widgets[BREADCRUMB_IDX].set_visible(false);
+ self.menu_mut(HEADER_IDX).set_item_checked(2, 4, false);
+ if self.focused_pane == LEFT_MENUBAR_IDX {
+ self.focused_pane = get_next_visible_pane(
+ self.focused_pane,
+ self.show_network,
+ self.show_viewport,
+ self.show_parameters,
+ self.show_spreadsheet,
+ false,
+ );
+ }
+ self.rebuild_positions();
+ self.apply_layout();
+ self.sync_pane_focus();
+ }
+ "Viewport" => {
+ self.show_viewport = false;
+ self.widgets[VIEWPORT_IDX].set_visible(false);
+ self.widgets[RIGHT_MENUBAR_IDX].set_visible(false);
+ self.menu_mut(HEADER_IDX).set_item_checked(2, 5, false);
+ if self.focused_pane == RIGHT_MENUBAR_IDX {
+ self.focused_pane = get_next_visible_pane(
+ self.focused_pane,
+ self.show_network,
+ self.show_viewport,
+ self.show_parameters,
+ self.show_spreadsheet,
+ false,
+ );
+ }
+ self.rebuild_positions();
+ self.apply_layout();
+ self.sync_pane_focus();
+ }
+ "Parameters" => {
+ self.show_parameters = false;
+ self.widgets[PARAM_IDX].set_visible(false);
+ self.widgets[PARAM_MENUBAR_IDX].set_visible(false);
+ self.menu_mut(HEADER_IDX).set_item_checked(2, 6, false);
+ if self.focused_pane == PARAM_MENUBAR_IDX {
+ self.focused_pane = get_next_visible_pane(
+ self.focused_pane,
+ self.show_network,
+ self.show_viewport,
+ self.show_parameters,
+ self.show_spreadsheet,
+ false,
+ );
+ }
+ self.rebuild_positions();
+ self.apply_layout();
+ self.sync_pane_focus();
+ }
+ "Spreadsheet" => {
+ self.show_spreadsheet = false;
+ self.widgets[SPREADSHEET_IDX].set_visible(false);
+ self.widgets[SPREADSHEET_MENUBAR_IDX].set_visible(false);
+ self.menu_mut(HEADER_IDX).set_item_checked(2, 7, false);
+ if self.focused_pane == SPREADSHEET_MENUBAR_IDX {
+ self.focused_pane = get_next_visible_pane(
+ self.focused_pane,
+ self.show_network,
+ self.show_viewport,
+ self.show_parameters,
+ self.show_spreadsheet,
+ false,
+ );
+ }
+ self.rebuild_positions();
+ self.apply_layout();
+ self.sync_pane_focus();
+ }
+ _ => {}
+ }
+ }
+ _ => {}
+ }
+ }
+ }
+ }
+
+ if let Some(path_str) = recent_file_to_open {
+ let path = std::path::PathBuf::from(path_str);
+ if let Err(e) = self.load_from_file(&path) {
+ eprintln!("Failed to load recent file: {:?}", e);
+ self.update_status_text(&format!("Failed to load: {:?}", e));
+ } else {
+ self.update_status_text(&format!("Loaded project from {}", path.display()));
+ self.add_recent_file(path);
}
}
}
}
}
+ pub fn sync_parameters_pane(&mut self) {
+ let params = if !self.is_detached_network {
+ if let Some(slot_idx) = self.graph().selected_node() {
+ let dir = self.current_dir();
+ if slot_idx < dir.children.len() {
+ param_display(&dir.children[slot_idx].params)
+ } else {
+ vec![]
+ }
+ } else {
+ vec![]
+ }
+ } else {
+ vec![]
+ };
+ self.param_mut().set_display_params(¶ms);
+ }
+
pub fn current_path_names(&self) -> Vec<String> {
let mut node = &self.fs_root;
let mut names = Vec::new();
@@ -1442,14 +1557,34 @@ impl State {
// Find executable path
let exe_path = if let Ok(cur_exe) = std::env::current_exe() {
- let sibling = cur_exe.with_file_name("clear-filesystem-interface");
+ let sibling = cur_exe.with_file_name("cce-filesystem-interface");
if sibling.exists() {
sibling
+ } else if let Ok(home) = std::env::var("HOME") {
+ let path = std::path::PathBuf::from(home)
+ .join(".local")
+ .join("bin")
+ .join("cce-filesystem-interface");
+ if path.exists() {
+ path
+ } else {
+ std::path::PathBuf::from("cce-filesystem-interface")
+ }
+ } else {
+ std::path::PathBuf::from("cce-filesystem-interface")
+ }
+ } else if let Ok(home) = std::env::var("HOME") {
+ let path = std::path::PathBuf::from(home)
+ .join(".local")
+ .join("bin")
+ .join("cce-filesystem-interface");
+ if path.exists() {
+ path
} else {
- std::path::PathBuf::from("clear-filesystem-interface")
+ std::path::PathBuf::from("cce-filesystem-interface")
}
} else {
- std::path::PathBuf::from("clear-filesystem-interface")
+ std::path::PathBuf::from("cce-filesystem-interface")
};
let child = match Command::new(exe_path)
@@ -1459,7 +1594,7 @@ impl State {
{
Ok(c) => c,
Err(e) => {
- eprintln!("Failed to spawn clear-filesystem-interface: {:?}", e);
+ eprintln!("Failed to spawn cce-filesystem-interface: {:?}", e);
return;
}
};
@@ -1467,7 +1602,7 @@ impl State {
let output = match child.wait_with_output() {
Ok(o) => o,
Err(e) => {
- eprintln!("Failed to wait for clear-filesystem-interface: {:?}", e);
+ eprintln!("Failed to wait for cce-filesystem-interface: {:?}", e);
return;
}
};
@@ -1505,14 +1640,34 @@ impl State {
// Find executable path
let exe_path = if let Ok(cur_exe) = std::env::current_exe() {
- let sibling = cur_exe.with_file_name("clear-filesystem-interface");
+ let sibling = cur_exe.with_file_name("cce-filesystem-interface");
if sibling.exists() {
sibling
+ } else if let Ok(home) = std::env::var("HOME") {
+ let path = std::path::PathBuf::from(home)
+ .join(".local")
+ .join("bin")
+ .join("cce-filesystem-interface");
+ if path.exists() {
+ path
+ } else {
+ std::path::PathBuf::from("cce-filesystem-interface")
+ }
} else {
- std::path::PathBuf::from("clear-filesystem-interface")
+ std::path::PathBuf::from("cce-filesystem-interface")
+ }
+ } else if let Ok(home) = std::env::var("HOME") {
+ let path = std::path::PathBuf::from(home)
+ .join(".local")
+ .join("bin")
+ .join("cce-filesystem-interface");
+ if path.exists() {
+ path
+ } else {
+ std::path::PathBuf::from("cce-filesystem-interface")
}
} else {
- std::path::PathBuf::from("clear-filesystem-interface")
+ std::path::PathBuf::from("cce-filesystem-interface")
};
let child = match Command::new(exe_path)
@@ -1522,7 +1677,7 @@ impl State {
{
Ok(c) => c,
Err(e) => {
- eprintln!("Failed to spawn clear-filesystem-interface: {:?}", e);
+ eprintln!("Failed to spawn cce-filesystem-interface: {:?}", e);
return;
}
};
@@ -1530,7 +1685,7 @@ impl State {
let output = match child.wait_with_output() {
Ok(o) => o,
Err(e) => {
- eprintln!("Failed to wait for clear-filesystem-interface: {:?}", e);
+ eprintln!("Failed to wait for cce-filesystem-interface: {:?}", e);
return;
}
};
@@ -1579,14 +1734,24 @@ impl State {
pub fn place_selected_node(&mut self) -> bool {
let Some(&template_idx) = self.node_palette_filtered.get(self.node_palette_selected) else { return false; };
let mut node = self.node_templates[template_idx].node.clone();
+ let is_in_utility = !self.current_path.is_empty() && self.fs_root.children[self.current_path[0]].node_type == "utility";
+ if is_in_utility {
+ if crate::geometry::is_geometry_node_type(&node.node_type) {
+ self.update_status_text("Utility nodes cannot contain geometry.");
+ self.close_node_palette();
+ return false;
+ }
+ }
let (nx, ny) = self.find_empty_cell(self.grid_cursor_col as f32, self.grid_cursor_row as f32, None);
node.position = (nx, ny);
+ node.name = self.get_lowest_unused_name(&node.name);
self.current_dir_mut().children.push(node);
self.close_node_palette();
self.sync_nodes();
self.rebuild_positions();
self.apply_layout();
self.update_panel_bounds();
+ self.rebuild_scene_geometry();
self.upload_vertices();
true
}
@@ -1641,7 +1806,7 @@ impl State {
}
}
-fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String>>) {
+pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String>>) {
let mut headers = vec![
"Vertex".to_string(),
"Pos.x".to_string(),
@@ -1837,6 +2002,33 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
self.apply_layout();
self.update_panel_bounds();
self.upload_vertices();
+ self.sync_cursor_and_selection();
+ }
+
+ pub fn move_up(&mut self) -> bool {
+ if !self.current_path.is_empty() {
+ let exited_idx = self.current_path.pop();
+ self.on_path_changed();
+ if let Some(idx) = exited_idx {
+ let pos = {
+ let dir = self.current_dir();
+ if idx < dir.children.len() {
+ Some(dir.children[idx].position)
+ } else {
+ None
+ }
+ };
+ if let Some((pos_x, pos_y)) = pos {
+ self.grid_cursor_col = pos_x as i32;
+ self.grid_cursor_row = pos_y as i32;
+ self.sync_cursor_and_selection();
+ self.upload_vertices();
+ }
+ }
+ true
+ } else {
+ false
+ }
}
pub fn find_empty_cell(&self, start_x: f32, start_y: f32, skip_idx: Option<usize>) -> (f32, f32) {
@@ -1860,6 +2052,30 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
(x, y)
}
+ pub fn delete_node(&mut self, slot: usize) -> bool {
+ let len = self.current_dir().children.len();
+ if slot < len {
+ self.current_dir_mut().children.remove(slot);
+ if let Some(sel_idx) = self.graph().selected_node() {
+ if sel_idx == slot {
+ self.graph_mut().set_selected_node(None);
+ } else if sel_idx > slot {
+ self.graph_mut().set_selected_node(Some(sel_idx - 1));
+ }
+ }
+ self.sync_nodes();
+ self.rebuild_positions();
+ self.apply_layout();
+ self.update_panel_bounds();
+ self.rebuild_scene_geometry();
+ self.upload_vertices();
+ self.viewport_dirty = true;
+ true
+ } else {
+ false
+ }
+ }
+
pub fn sync_nodes(&mut self) {
let graph_nodes: Vec<GraphNode> = self.current_dir().children.iter().map(|c| {
GraphNode {
@@ -1868,6 +2084,9 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
position: c.position,
parameters: param_display(&c.params),
geom_visible: c.geometry_visible,
+ node_type: c.node_type.clone(),
+ inputs: c.inputs,
+ outputs: c.outputs,
}
}).collect();
self.graph_mut().set_nodes(&graph_nodes);
@@ -1916,57 +2135,17 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
}
}
- if !cache_hit {
+ if self.show_spreadsheet && !cache_hit {
let mut headers = Vec::new();
let mut rows = Vec::new();
if let Some(node) = selected_node {
- if node.node_type.eq_ignore_ascii_case("sphere") {
- if let Some(idx) = find_sphere_index(&self.fs_root, node) {
- let center = Vec3::new((idx % 4) as f32 * 1.25 - 1.875, 0.55, -((idx / 4) as f32) * 1.25);
- let radius = node_param_f32(node, "Radius", 0.5).max(0.05);
- let geom = sphere_vertices(center, radius);
- let (h, r) = Self::geometry_to_spreadsheet_data(&geom);
- headers = h;
- rows = r;
- }
- } else if node.node_type.eq_ignore_ascii_case("line") {
- if let Some(idx) = find_sphere_index(&self.fs_root, node) {
- let start = Vec3::new((idx % 4) as f32 * 1.25 - 1.875, 0.55, -((idx / 4) as f32) * 1.25);
- let length = node_param_f32(node, "Length", 1.0);
- let thickness = node_param_f32(node, "Thickness", 0.02);
- let end = start + Vec3::new(0.0, length, 0.0);
- let geom = line_vertices(start, end, thickness);
- let (h, r) = Self::geometry_to_spreadsheet_data(&geom);
- headers = h;
- rows = r;
- }
- } else if node.node_type.eq_ignore_ascii_case("add") {
- if let Some(idx) = find_sphere_index(&self.fs_root, node) {
- let center = Vec3::new((idx % 4) as f32 * 1.25 - 1.875, 0.55, -((idx / 4) as f32) * 1.25);
- let num_points = node_param_f32(node, "Points", 100.0) as i32;
- let mut geom = Geometry::new();
- for i in 0..num_points {
- let t = i as f32 / num_points.max(1) as f32;
- let angle = t * std::f32::consts::TAU * 3.0;
- let r = 0.4 * t;
- let px = center.x + r * angle.cos();
- let py = center.y + t * 0.5 - 0.25;
- let pz = center.z + r * angle.sin();
- let pt_center = Vec3::new(px, py, pz);
- geom.merge(sphere_vertices(pt_center, 0.02));
- }
- let (h, r) = Self::geometry_to_spreadsheet_data(&geom);
- headers = h;
- rows = r;
- }
- } else if node.node_type.eq_ignore_ascii_case("transform") {
- let mut visited = Vec::new();
- if let Some(geom) = resolve_transform_geometry(&self.fs_root, node, &mut visited) {
- let (h, r) = Self::geometry_to_spreadsheet_data(&geom);
- headers = h;
- rows = r;
- }
+ let mut visited = Vec::new();
+ let mut ocl_error = None;
+ if let Some(geom) = generate_single_node_geometry_with_errors(&self.fs_root, node, &mut visited, &mut ocl_error) {
+ let (h, r) = Self::geometry_to_spreadsheet_data(&geom);
+ headers = h;
+ rows = r;
}
}
@@ -2457,6 +2636,8 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
params: vec![],
geometry_visible: true,
position: (0.0, 0.0),
+ inputs: 0,
+ outputs: 0,
}
};
@@ -2493,8 +2674,8 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
Box::new(Plate::new(0.0, 0.0, 0.0, 0.0).with_color(colors::PARAM_BG).with_blur(true)),
Box::new(ParametersBg::new()),
Box::new(Canvas::new()),
- Box::new(MenuBar::new(0.0, 0.0, 0.0, MENUBAR_H).with_title("0: Network").with_label("Network Menu Bar").with_item("File", &["New", "Open", "Save", "Save As"]).with_item("Edit", &["Undo", "Redo"]).with_item("View", &["Zoom In", "Zoom Out", "Circular Pane", "Detach Pane", "Close Pane"]).with_item("Settings", &[]).with_context_options(context_opts.clone(), 0)),
- Box::new(MenuBar::new(0.0, 0.0, 0.0, MENUBAR_H).with_title("1: Viewport").with_label("Viewport Menu Bar").with_item("Camera", &["Perspective", "Orthographic"]).with_item("Display", &["Square Aspect"]).with_item("Guides", &["Show Grid", "Cube", "Origin", "Camera Pivot"]).with_item("View", &["Close Pane"]).with_item("Settings", &[]).with_context_options(context_opts.clone(), 1)),
+ Box::new(MenuBar::new(0.0, 0.0, 0.0, MENUBAR_H).with_title("0: Network").with_label("Network Menu Bar").with_item("File", &["New", "Open", "Save", "Save As"]).with_item("Edit", &["Undo", "Redo"]).with_item("View", &["Zoom In", "Zoom Out", "Circular Pane", "Detach Pane", "Close Pane"]).with_context_options(context_opts.clone(), 0)),
+ Box::new(MenuBar::new(0.0, 0.0, 0.0, MENUBAR_H).with_title("1: Viewport").with_label("Viewport Menu Bar").with_item("Camera", &["Perspective", "Orthographic"]).with_item("Display", &["Square Aspect"]).with_item("Guides", &["Show Grid", "Cube", "Origin", "Camera Pivot"]).with_item("View", &["Close Pane"]).with_context_options(context_opts.clone(), 1)),
Box::new(MenuBar::new(0.0, 0.0, 0.0, MENUBAR_H).with_title("2: Parameters").with_label("Parameters Menu Bar").with_item("Preset", &["Default", "Custom"]).with_item("Reset", &["All"]).with_item("View", &["Close Pane"]).with_context_options(context_opts.clone(), 2)),
Box::new(StatusBar::new().with_text("Ready")),
Box::new(Breadcrumb::new()),
@@ -2509,11 +2690,12 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
let network_panel = Plate::new(0.0, 0.0, 0.0, 0.0).with_color([0.10, 0.10, 0.13, 0.95]);
widgets.push(Box::new(network_panel));
- let paginator = Paginator::new(56.0, vec![])
+ let mut paginator = Paginator::new(56.0, vec![])
.with_sidebar_mode(true)
.with_column_layout(true)
.with_tabs_rotated(false)
.with_context_options(context_opts.clone(), 0);
+ paginator.set_page_hidden(true);
widgets.push(Box::new(paginator));
let mut positions = Vec::with_capacity(PAGINATOR_IDX + 1);
@@ -2602,6 +2784,7 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
fs_root: fs_root.clone(),
node_templates,
current_path,
+ node_clipboard: None,
last_click: None,
last_frame: Instant::now(),
shortcut_manager,
@@ -2638,8 +2821,8 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
physical_height: ph,
scale,
square_viewport: settings.square_viewport,
- grid_snap_enabled: settings.grid_snap_enabled,
- network_grid_visible: settings.network_grid_enabled,
+ grid_snap_enabled: true,
+ network_grid_visible: true,
grid_size_x: settings.grid_size_x,
grid_size_y: settings.grid_size_y,
skipped_row_h: settings.skipped_row_h,
@@ -2696,8 +2879,10 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
window_y: 0,
active_menu_cloud_pid: None,
active_menu_cloud_idx: None,
- uniform_background: settings.uniform_background,
- network_opacity: settings.network_opacity,
+ uniform_background: false,
+ network_opacity: 0.95,
+ cell_opacity: 0.95,
+ gap_opacity: 0.95,
last_design_mod_time: {
let design_path = DesignSettings::file_path();
std::fs::metadata(&design_path).and_then(|m| m.modified()).ok()
@@ -2737,6 +2922,9 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
recent_files_buttons,
text_buffer_cache: std::collections::HashMap::new(),
viewport_dirty: true,
+ text_dirty: true,
+ last_popover_rects: Vec::new(),
+ last_status_text: String::new(),
last_viewport_camera_pos: Vec3::ZERO,
last_viewport_camera_rx: 0.0,
last_viewport_camera_ry: 0.0,
@@ -2758,6 +2946,7 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
};
state.update_inertial_settings();
+ state.update_graph_settings_from_config();
state.update_window_title();
colors::set_node_color([
settings.node_color[0],
@@ -2798,6 +2987,8 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
state.update_panel_bounds();
state.sync_pane_focus();
state.upload_vertices();
+ state.sync_cursor_and_selection();
+ state.sync_parameters_pane();
state
}
@@ -2821,7 +3012,8 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
graph.set_grid_snap_enabled(grid_snap_enabled);
if let Some(graph) = self.widgets[CONTENT_IDX].as_any_mut().downcast_mut::<clear_ui::widget::Graph>() {
graph.set_uniform_background(self.uniform_background);
- graph.set_network_opacity(self.network_opacity);
+ graph.set_cell_opacity(self.cell_opacity);
+ graph.set_gap_opacity(self.gap_opacity);
graph.set_cell_color(self.cell_color);
graph.set_gap_color(self.gap_color);
}
@@ -2875,6 +3067,94 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
self.scroll_speed = speed;
}
+ pub fn update_graph_settings_from_config(&mut self) {
+ let config_paths = [
+ "/home/lsgalante/.config/cce/config.toml",
+ "/home/lsgalante/.config/ccec/config.toml",
+ ];
+
+ let mut show_grid = None;
+ let mut snap_enabled = None;
+ let mut uniform_background = None;
+ let mut cell_opacity = None;
+ let mut gap_opacity = None;
+ let mut network_opacity = None;
+
+ for path in &config_paths {
+ if let Ok(content) = std::fs::read_to_string(path) {
+ #[derive(serde::Deserialize)]
+ struct Layout {
+ pub graph_show_grid: Option<bool>,
+ pub graph_snap_enabled: Option<bool>,
+ pub graph_uniform_background: Option<bool>,
+ pub graph_network_opacity: Option<f32>,
+ pub graph_cell_opacity: Option<f32>,
+ pub graph_gap_opacity: Option<f32>,
+ }
+ #[derive(serde::Deserialize)]
+ struct Config {
+ pub layout: Option<Layout>,
+ }
+ if let Ok(cfg) = toml::from_str::<Config>(&content) {
+ if let Some(layout) = cfg.layout {
+ show_grid = layout.graph_show_grid;
+ snap_enabled = layout.graph_snap_enabled;
+ uniform_background = layout.graph_uniform_background;
+ network_opacity = layout.graph_network_opacity;
+ cell_opacity = layout.graph_cell_opacity.or(layout.graph_network_opacity);
+ gap_opacity = layout.graph_gap_opacity.or(layout.graph_network_opacity);
+ break;
+ }
+ }
+ }
+ }
+
+ let mut changed = false;
+ if let Some(val) = show_grid {
+ if self.show_grid != val {
+ self.show_grid = val;
+ self.menu_mut(RIGHT_MENUBAR_IDX).set_item_checked(2, 0, val);
+ changed = true;
+ }
+ }
+ if let Some(val) = snap_enabled {
+ if self.grid_snap_enabled != val {
+ self.grid_snap_enabled = val;
+ changed = true;
+ }
+ }
+ if let Some(val) = uniform_background {
+ if self.uniform_background != val {
+ self.uniform_background = val;
+ changed = true;
+ }
+ }
+ if let Some(val) = network_opacity {
+ if (self.network_opacity - val).abs() > 0.001 {
+ self.network_opacity = val;
+ changed = true;
+ }
+ }
+ if let Some(val) = cell_opacity {
+ if (self.cell_opacity - val).abs() > 0.001 {
+ self.cell_opacity = val;
+ changed = true;
+ }
+ }
+ if let Some(val) = gap_opacity {
+ if (self.gap_opacity - val).abs() > 0.001 {
+ self.gap_opacity = val;
+ changed = true;
+ }
+ }
+
+ if changed {
+ self.sync_grid_settings();
+ self.viewport_dirty = true;
+ }
+ }
+
+
pub fn zoom(&mut self, factor: f32, center: Option<(f32, f32)>) {
self.pan_velocity_x = 0.0;
@@ -3403,163 +3683,28 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
self.paginator_page_widgets.resize_with(num_pages, Vec::new);
for (page_idx, page_name) in menu_names.iter().enumerate() {
- if page_name == "Settings" {
- if active_menubar == LEFT_MENUBAR_IDX {
- // 0: Snap to Grid (Checkbox)
- let mut cb = Checkbox::new().with_label("Snap to Grid");
- cb.set_checked(self.grid_snap_enabled);
- cb.set_rect(0.0, 0.0, 0.0, 42.0);
- self.paginator_page_widgets[page_idx].push(Box::new(cb));
-
- // 1: Grid Visible (Checkbox)
- let mut cb = Checkbox::new().with_label("Grid Visible");
- cb.set_checked(self.network_grid_visible);
- cb.set_rect(0.0, 0.0, 0.0, 42.0);
- self.paginator_page_widgets[page_idx].push(Box::new(cb));
-
- // 2: Grid X (Spinbox, range 10-200)
- let mut sb = Spinbox::new(self.grid_size_x as i32, 10, 200, 1).with_label("Grid X");
- sb.set_rect(0.0, 0.0, 0.0, 42.0);
- self.paginator_page_widgets[page_idx].push(Box::new(sb));
-
- // 3: Grid Y (Spinbox, range 5-100)
- let mut sb = Spinbox::new(self.grid_size_y as i32, 5, 100, 1).with_label("Grid Y");
- sb.set_rect(0.0, 0.0, 0.0, 42.0);
- self.paginator_page_widgets[page_idx].push(Box::new(sb));
-
- // 4: Skipped Row H (Spinbox, range 0-150)
- let mut sb = Spinbox::new(self.skipped_row_h as i32, 0, 150, 1).with_label("Skipped Row H");
- sb.set_rect(0.0, 0.0, 0.0, 42.0);
- self.paginator_page_widgets[page_idx].push(Box::new(sb));
-
- // 5: Skipped Col W (Spinbox, range 0-150)
- let mut sb = Spinbox::new(self.skipped_col_w as i32, 0, 150, 1).with_label("Skipped Col W");
- sb.set_rect(0.0, 0.0, 0.0, 42.0);
- self.paginator_page_widgets[page_idx].push(Box::new(sb));
-
- // 6: Uniform Background (Checkbox)
- let mut cb = Checkbox::new().with_label("Uniform Background");
- cb.set_checked(self.uniform_background);
- cb.set_rect(0.0, 0.0, 0.0, 42.0);
- self.paginator_page_widgets[page_idx].push(Box::new(cb));
-
- // 7: Opacity (Slider, range 0.0-1.0)
- let mut sl = Slider::new().with_range(0.0, 1.0).with_value(self.network_opacity).with_label("Opacity").with_readout(true).with_scroll(true);
- sl.set_rect(0.0, 0.0, 0.0, 42.0);
- self.paginator_page_widgets[page_idx].push(Box::new(sl));
-
- // 8: Node Color (ColorSelector)
- let mut cs = ColorSelector::new([
- (self.node_color[0] * 255.0) as u8,
- (self.node_color[1] * 255.0) as u8,
- (self.node_color[2] * 255.0) as u8,
- ]).with_label("Node Color");
- cs.set_rect(0.0, 0.0, 0.0, 42.0);
- self.paginator_page_widgets[page_idx].push(Box::new(cs));
-
- // 9: Cell Color (ColorSelector)
- let mut cs = ColorSelector::new([
- (self.cell_color[0] * 255.0) as u8,
- (self.cell_color[1] * 255.0) as u8,
- (self.cell_color[2] * 255.0) as u8,
- ]).with_label("Cell Color");
- cs.set_rect(0.0, 0.0, 0.0, 42.0);
- self.paginator_page_widgets[page_idx].push(Box::new(cs));
-
- // 10: Gap Color (ColorSelector)
- let mut cs = ColorSelector::new([
- (self.gap_color[0] * 255.0) as u8,
- (self.gap_color[1] * 255.0) as u8,
- (self.gap_color[2] * 255.0) as u8,
- ]).with_label("Gap Color");
- cs.set_rect(0.0, 0.0, 0.0, 42.0);
- self.paginator_page_widgets[page_idx].push(Box::new(cs));
- } else if active_menubar == RIGHT_MENUBAR_IDX {
- // 0: Show Grid Guide (Checkbox)
- let mut cb = Checkbox::new().with_label("Show Grid Guide");
- cb.set_checked(self.show_grid);
- cb.set_rect(0.0, 0.0, 0.0, 42.0);
- self.paginator_page_widgets[page_idx].push(Box::new(cb));
-
- // 1: Show Reference Cube (Checkbox)
- let mut cb = Checkbox::new().with_label("Show Reference Cube");
- cb.set_checked(self.show_cube);
- cb.set_rect(0.0, 0.0, 0.0, 42.0);
- self.paginator_page_widgets[page_idx].push(Box::new(cb));
-
- // 2: Show Origin Axes (Checkbox)
- let mut cb = Checkbox::new().with_label("Show Origin Axes");
- cb.set_checked(self.show_origin);
- cb.set_rect(0.0, 0.0, 0.0, 42.0);
- self.paginator_page_widgets[page_idx].push(Box::new(cb));
-
- // 3: Show Camera Pivot (Checkbox)
- let mut cb = Checkbox::new().with_label("Show Camera Pivot");
- cb.set_checked(self.show_camera_pivot);
- cb.set_rect(0.0, 0.0, 0.0, 42.0);
+ let items = &menu_items[page_idx];
+ let checked_list = menu_checked.get(page_idx);
+ for (item_idx, item_name) in items.iter().enumerate() {
+ let is_checked = checked_list.and_then(|l| l.get(item_idx).copied().flatten());
+ if let Some(checked_val) = is_checked {
+ let mut cb = Checkbox::new().with_label(item_name);
+ cb.set_checked(checked_val);
self.paginator_page_widgets[page_idx].push(Box::new(cb));
-
- // 4: Grid Thickness (Spinbox, range 2-200, decimals 3)
- let mut sb = Spinbox::new((self.grid_thickness * 1000.0) as i32, 2, 200, 1).with_label("Grid Thickness").with_decimals(3);
- sb.set_rect(0.0, 0.0, 0.0, 42.0);
- self.paginator_page_widgets[page_idx].push(Box::new(sb));
-
- // 5: Origin Guide Size (Spinbox, range 1-50, decimals 1)
- let mut sb = Spinbox::new((self.origin_size * 10.0) as i32, 1, 50, 1).with_label("Origin Guide Size").with_decimals(1);
- sb.set_rect(0.0, 0.0, 0.0, 42.0);
- self.paginator_page_widgets[page_idx].push(Box::new(sb));
-
- // 6: Camera Pivot Size (Spinbox, range 1-50, decimals 1)
- let mut sb = Spinbox::new((self.camera_pivot_size * 10.0) as i32, 1, 50, 1).with_label("Camera Pivot Size").with_decimals(1);
- sb.set_rect(0.0, 0.0, 0.0, 42.0);
- self.paginator_page_widgets[page_idx].push(Box::new(sb));
-
- // 7, 8, 9: BG Color R, G, B (Spinbox, range 0-255)
- let mut sb = Spinbox::new((self.viewport_bg_color[0] * 255.0) as i32, 0, 255, 1).with_label("BG Color R");
- sb.set_rect(0.0, 0.0, 0.0, 42.0);
- self.paginator_page_widgets[page_idx].push(Box::new(sb));
- let mut sb = Spinbox::new((self.viewport_bg_color[1] * 255.0) as i32, 0, 255, 1).with_label("BG Color G");
- sb.set_rect(0.0, 0.0, 0.0, 42.0);
- self.paginator_page_widgets[page_idx].push(Box::new(sb));
- let mut sb = Spinbox::new((self.viewport_bg_color[2] * 255.0) as i32, 0, 255, 1).with_label("BG Color B");
- sb.set_rect(0.0, 0.0, 0.0, 42.0);
- self.paginator_page_widgets[page_idx].push(Box::new(sb));
-
- // 10, 11, 12: Grid Color R, G, B (Spinbox, range 0-255)
- let mut sb = Spinbox::new((self.grid_color[0] * 255.0) as i32, 0, 255, 1).with_label("Grid Color R");
- sb.set_rect(0.0, 0.0, 0.0, 42.0);
- self.paginator_page_widgets[page_idx].push(Box::new(sb));
- let mut sb = Spinbox::new((self.grid_color[1] * 255.0) as i32, 0, 255, 1).with_label("Grid Color G");
- sb.set_rect(0.0, 0.0, 0.0, 42.0);
- self.paginator_page_widgets[page_idx].push(Box::new(sb));
- let mut sb = Spinbox::new((self.grid_color[2] * 255.0) as i32, 0, 255, 1).with_label("Grid Color B");
- sb.set_rect(0.0, 0.0, 0.0, 42.0);
- self.paginator_page_widgets[page_idx].push(Box::new(sb));
- }
- } else {
- let items = &menu_items[page_idx];
- let checked_list = menu_checked.get(page_idx);
- for (item_idx, item_name) in items.iter().enumerate() {
- let is_checked = checked_list.and_then(|l| l.get(item_idx).copied().flatten());
- if let Some(checked_val) = is_checked {
- let mut cb = Checkbox::new().with_label(item_name);
- cb.set_checked(checked_val);
- self.paginator_page_widgets[page_idx].push(Box::new(cb));
- } else {
- let btn = Button::new(0.0, 0.0, 150.0, 24.0).with_label(item_name);
- self.paginator_page_widgets[page_idx].push(Box::new(btn));
- }
+ } else {
+ let btn = Button::new(0.0, 0.0, 150.0, 24.0).with_label(item_name);
+ self.paginator_page_widgets[page_idx].push(Box::new(btn));
}
+ }
- if menu_names[page_idx] == "File" {
- let mut recent_lbl = Label::new("Recent Files").with_font_size(11.0).with_color([0xd4, 0xd4, 0xd4]);
- recent_lbl.set_rect(0.0, 0.0, 150.0, 16.0);
- self.paginator_page_widgets[page_idx].push(Box::new(recent_lbl));
+ if page_name == "File" {
+ let mut recent_lbl = Label::new("Recent Files").with_font_size(11.0).with_color([0xd4, 0xd4, 0xd4]);
+ recent_lbl.set_rect(0.0, 0.0, 150.0, 16.0);
+ self.paginator_page_widgets[page_idx].push(Box::new(recent_lbl));
- let mut recent_list = ScrollingList::new(22.0, 2.0);
- recent_list.set_rect(0.0, 0.0, 150.0, 100.0);
- self.paginator_page_widgets[page_idx].push(Box::new(recent_list));
- }
+ let mut recent_list = ScrollingList::new(22.0, 2.0);
+ recent_list.set_rect(0.0, 0.0, 150.0, 100.0);
+ self.paginator_page_widgets[page_idx].push(Box::new(recent_list));
}
}
@@ -3772,7 +3917,7 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
}
pub fn sync_cursor_and_selection(&mut self) {
- if self.focused_widget != Some(CONTENT_IDX) {
+ if self.focused_pane != LEFT_MENUBAR_IDX {
return;
}
let dir = self.current_dir();
@@ -3797,6 +3942,25 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
}
}
+ pub fn sync_cursor_and_selection_from_loaded(&mut self) {
+ if let Some(sel_idx) = self.graph().selected_node() {
+ let pos = {
+ let dir = self.current_dir();
+ if sel_idx < dir.children.len() {
+ Some(dir.children[sel_idx].position)
+ } else {
+ None
+ }
+ };
+ if let Some((pos_x, pos_y)) = pos {
+ self.grid_cursor_col = pos_x as i32;
+ self.grid_cursor_row = pos_y as i32;
+ }
+ } else {
+ self.sync_cursor_and_selection();
+ }
+ }
+
@@ -3943,21 +4107,7 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
self.sync_parameters_to_project();
// Sync Parameters pane with selected node
- let params = if !self.is_detached_network {
- if let Some(slot_idx) = self.graph().selected_node() {
- let dir = self.current_dir();
- if slot_idx < dir.children.len() {
- param_display(&dir.children[slot_idx].params)
- } else {
- vec![]
- }
- } else {
- vec![]
- }
- } else {
- vec![]
- };
- self.param_mut().set_display_params(¶ms);
+ self.sync_parameters_pane();
self.sync_layout();
@@ -4217,6 +4367,9 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
if idx == NETWORK_PANEL_IDX {
self.sync_grid_settings();
}
+ if idx == PARAM_IDX {
+ self.sync_parameters_to_project();
+ }
}
}
}
@@ -4269,6 +4422,16 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
self.is_zooming_viewport = false;
self.zoom_accum = 0.0;
}
+ if self.node_palette_visible {
+ if *btn_state == ElementState::Pressed {
+ let (px, py, pw, ph) = self.palette().panel_rect();
+ if self.cursor_x < px || self.cursor_x > px + pw || self.cursor_y < py || self.cursor_y > py + ph {
+ self.close_node_palette();
+ }
+ }
+ self.upload_vertices();
+ return true;
+ }
let dialog_open = self.node_palette_visible;
let in_network_pane = self.in_network_pane();
let node_area_x = self.positions[CONTENT_IDX].0;
@@ -4344,7 +4507,7 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
ElementState::Pressed => {
let hits_any_menu = (0..self.widgets.len()).any(|i| {
hits_widget(self, i, self.cursor_x, self.cursor_y)
- && self.menu(i).get_menu_items_at(self.cursor_x, self.cursor_y).is_some()
+ && self.widgets[i].as_menu_controller().and_then(|m| m.get_menu_items_at(self.cursor_x, self.cursor_y)).is_some()
});
if !hits_any_menu {
@@ -4512,7 +4675,9 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
}
let mut click_target = None;
for i in 0..self.widgets.len() {
- if self.menu(i).is_menu_open() && hits_widget(self, i, self.cursor_x, self.cursor_y) {
+ if self.widgets[i].as_menu_controller().map(|m| m.is_menu_open()).unwrap_or(false)
+ && hits_widget(self, i, self.cursor_x, self.cursor_y)
+ {
click_target = Some(i);
break;
}
@@ -4630,12 +4795,15 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
if i != PARAM_IDX {
self.widgets[i].focus();
self.focused_widget = Some(i);
- if self.menu(i).is_menu_bar() && !self.widgets[i].focused(&self.ui_context) {
+ if self.widgets[i].as_menu_controller().map(|m| m.is_menu_bar()).unwrap_or(false)
+ && !self.widgets[i].focused(&self.ui_context)
+ {
self.widgets[i].unfocus();
self.focused_widget = None;
}
}
if i == CONTENT_IDX {
+ self.sync_parameters_pane();
if let Some(slot_idx) = self.graph().selected_node() {
let dir = self.current_dir();
if slot_idx < dir.children.len() {
@@ -4653,7 +4821,7 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
if let Some(dir_idx) = self.graph().double_clicked_node() {
self.graph_mut().clear_double_clicked_node();
let dir = self.current_dir();
- if dir_idx < dir.children.len() && (dir.children[dir_idx].node_type == "node" || dir.children[dir_idx].node_type == "opencl" || !dir.children[dir_idx].children.is_empty()) {
+ if dir_idx < dir.children.len() && (dir.children[dir_idx].node_type == "node" || dir.children[dir_idx].node_type == "utility" || !dir.children[dir_idx].children.is_empty()) {
self.current_path.push(dir_idx);
self.on_path_changed();
changed = true;
@@ -4707,13 +4875,31 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
self.drag_widget = None;
changed = true;
}
- let ctx = &mut self.ui_context;
- for w in &mut self.widgets {
- w.set_modifiers(self.modifiers.control_key(), self.modifiers.shift_key(), self.modifiers.alt_key());
- if w.mouse_input(*button, *btn_state, self.cursor_x, self.cursor_y, ctx) {
- changed = true;
+ let mut sync_params = false;
+ let mut sync_paginator = false;
+ {
+ let ctx = &mut self.ui_context;
+ for (i, w) in self.widgets.iter_mut().enumerate() {
+ w.set_modifiers(self.modifiers.control_key(), self.modifiers.shift_key(), self.modifiers.alt_key());
+ if w.mouse_input(*button, *btn_state, self.cursor_x, self.cursor_y, ctx) {
+ changed = true;
+ if i == PARAM_IDX {
+ sync_params = true;
+ }
+ if i == PAGINATOR_IDX {
+ sync_paginator = true;
+ }
+ }
}
}
+ if sync_params {
+ self.sync_parameters_to_project();
+ }
+ if sync_paginator {
+ self.update_paginator();
+ self.rebuild_positions();
+ self.apply_layout();
+ }
}
}
@@ -4724,6 +4910,20 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
changed = true;
}
+ if let Some((input_node_id, output_node_name)) = self.graph_mut().take_pending_connection() {
+ let dir = self.current_dir_mut();
+ if let Some(child) = dir.children.iter_mut().find(|c| c.id == input_node_id) {
+ if let Some(p) = child.params.iter_mut().find(|p| p.name == "Input") {
+ p.default = output_node_name;
+ self.sync_nodes();
+ self.rebuild_scene_geometry();
+ self.upload_vertices();
+ self.sync_parameters_pane();
+ changed = true;
+ }
+ }
+ }
+
if self.page_selector(PAGINATOR_IDX).is_page_hidden() != was_page_hidden {
self.rebuild_positions();
self.apply_layout();
@@ -4769,10 +4969,17 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
if self.node_palette_visible {
return self.handle_node_palette_key(event);
}
+
+ if event.state == ElementState::Pressed && event.logical_key == Key::Named(NamedKey::Escape) {
+ self.graph_mut().cancel_connecting();
+ self.upload_vertices();
+ return true;
+ }
let mut changed = false;
if event.state == ElementState::Pressed {
let is_plain_key = !self.modifiers.control_key() && !self.modifiers.alt_key() && !self.modifiers.super_key();
let is_alt_key = self.modifiers.alt_key() && !self.modifiers.control_key() && !self.modifiers.super_key();
+ let is_ctrl_only = self.modifiers.control_key() && !self.modifiers.alt_key() && !self.modifiers.super_key() && !self.modifiers.shift_key();
let mut delta = None;
@@ -4834,14 +5041,22 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
self.sync_cursor_and_selection();
changed = true;
} else {
- if let Key::Character(s) = &event.logical_key {
+ if event.logical_key == Key::Named(NamedKey::Delete) {
+ if is_plain_key && self.focused_pane == LEFT_MENUBAR_IDX {
+ if let Some(slot_idx) = self.graph().selected_node() {
+ if self.delete_node(slot_idx) {
+ changed = true;
+ }
+ }
+ }
+ } else if let Key::Character(s) = &event.logical_key {
if is_plain_key {
match s.as_str() {
"e" | "E" => {
- if self.focused_widget == Some(CONTENT_IDX) {
+ if self.focused_pane == LEFT_MENUBAR_IDX {
if let Some(slot_idx) = self.graph().selected_node() {
let dir = self.current_dir();
- if slot_idx < dir.children.len() {
+ if slot_idx < dir.children.len() && dir.children[slot_idx].node_type != "utility" {
let visible = !dir.children[slot_idx].geometry_visible;
self.current_dir_mut().children[slot_idx].geometry_visible = visible;
self.sync_nodes();
@@ -4851,6 +5066,13 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
}
}
}
+ "u" | "U" => {
+ if self.focused_pane == LEFT_MENUBAR_IDX {
+ if self.move_up() {
+ changed = true;
+ }
+ }
+ }
"r" | "R" => {
if self.focused_pane == RIGHT_MENUBAR_IDX {
if self.active_camera != "Default Camera" {
@@ -4869,26 +5091,15 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
changed = true;
}
}
- "u" | "U" => {
- if self.focused_pane == LEFT_MENUBAR_IDX {
- if !self.current_path.is_empty() {
- self.current_path.pop();
- self.on_path_changed();
- changed = true;
- }
- }
- }
"i" | "I" => {
if self.focused_pane == LEFT_MENUBAR_IDX {
- if self.focused_widget == Some(CONTENT_IDX) {
- if let Some(slot_idx) = self.graph().selected_node() {
- let dir = self.current_dir();
- if slot_idx < dir.children.len() && (dir.children[slot_idx].node_type == "node" || dir.children[slot_idx].node_type == "opencl" || !dir.children[slot_idx].children.is_empty()) {
- self.current_path.push(slot_idx);
- self.on_path_changed();
- changed = true;
- }
- }
+ if let Some(slot_idx) = self.graph().selected_node() {
+ let dir = self.current_dir();
+ if slot_idx < dir.children.len() && (dir.children[slot_idx].node_type == "node" || dir.children[slot_idx].node_type == "utility" || !dir.children[slot_idx].children.is_empty()) {
+ self.current_path.push(slot_idx);
+ self.on_path_changed();
+ changed = true;
+ }
}
}
}
@@ -4991,6 +5202,56 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
}
_ => {}
}
+ } else if is_ctrl_only && self.focused_pane == LEFT_MENUBAR_IDX {
+ match s.as_str() {
+ "c" | "C" => {
+ if let Some(slot_idx) = self.graph().selected_node() {
+ let dir = self.current_dir();
+ if slot_idx < dir.children.len() {
+ self.node_clipboard = Some(dir.children[slot_idx].clone());
+ }
+ }
+ }
+ "x" | "X" => {
+ if let Some(slot_idx) = self.graph().selected_node() {
+ let dir = self.current_dir();
+ if slot_idx < dir.children.len() {
+ self.node_clipboard = Some(dir.children[slot_idx].clone());
+ self.delete_node(slot_idx);
+ changed = true;
+ }
+ }
+ }
+ "v" | "V" => {
+ if let Some(ref clipboard_node) = self.node_clipboard {
+ let mut node = clipboard_node.clone();
+ fn regenerate_ids(n: &mut FsNode) {
+ n.id = generate_node_id();
+ for child in &mut n.children {
+ regenerate_ids(child);
+ }
+ }
+ regenerate_ids(&mut node);
+ let start_x = self.grid_cursor_col as f32;
+ let start_y = self.grid_cursor_row as f32;
+ let (nx, ny) = self.find_empty_cell(start_x, start_y, None);
+ node.position = (nx, ny);
+ self.current_dir_mut().children.push(node);
+ self.grid_cursor_col = nx as i32;
+ self.grid_cursor_row = ny as i32;
+ self.sync_nodes();
+ self.sync_cursor_and_selection();
+ self.rebuild_positions();
+ self.apply_layout();
+ self.update_panel_bounds();
+ self.rebuild_scene_geometry();
+ self.upload_vertices();
+ self.viewport_dirty = true;
+ changed = true;
+ }
+ }
+ _ => {}
+ }
}
}
}
@@ -5043,6 +5304,7 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
self.last_config_mod_time = current_mod_time;
clear_ui::layout::reload_config();
self.update_inertial_settings();
+ self.update_graph_settings_from_config();
self.rebuild_positions();
self.apply_layout();
self.upload_vertices();
@@ -5054,28 +5316,24 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
if let Ok(mod_time) = m.modified() {
if Some(mod_time) != self.last_design_mod_time {
self.last_design_mod_time = Some(mod_time);
- let settings = DesignSettings::load();
- self.square_viewport = settings.square_viewport;
- self.grid_snap_enabled = settings.grid_snap_enabled;
- self.network_grid_visible = settings.network_grid_enabled;
- self.grid_size_x = settings.grid_size_x;
- self.grid_size_y = settings.grid_size_y;
- self.skipped_row_h = settings.skipped_row_h;
- self.skipped_col_w = settings.skipped_col_w;
- self.grid_thickness = settings.grid_thickness;
- self.show_grid = settings.show_grid_enabled;
- self.show_cube = settings.show_cube_enabled;
- self.show_origin = settings.show_origin_enabled;
- self.show_camera_pivot = settings.show_camera_pivot_enabled;
- self.viewport_bg_color = settings.viewport_bg_color;
- self.node_color = settings.node_color;
- self.grid_color = settings.grid_color;
- self.origin_size = settings.origin_size;
- self.camera_pivot_size = settings.camera_pivot_size;
- self.uniform_background = settings.uniform_background;
- self.network_opacity = settings.network_opacity;
- self.cell_color = settings.cell_color;
- self.gap_color = settings.gap_color;
+ let settings = DesignSettings::load();
+ self.square_viewport = settings.square_viewport;
+ self.grid_size_x = settings.grid_size_x;
+ self.grid_size_y = settings.grid_size_y;
+ self.skipped_row_h = settings.skipped_row_h;
+ self.skipped_col_w = settings.skipped_col_w;
+ self.grid_thickness = settings.grid_thickness;
+ self.show_grid = settings.show_grid_enabled;
+ self.show_cube = settings.show_cube_enabled;
+ self.show_origin = settings.show_origin_enabled;
+ self.show_camera_pivot = settings.show_camera_pivot_enabled;
+ self.viewport_bg_color = settings.viewport_bg_color;
+ self.node_color = settings.node_color;
+ self.grid_color = settings.grid_color;
+ self.origin_size = settings.origin_size;
+ self.camera_pivot_size = settings.camera_pivot_size;
+ self.cell_color = settings.cell_color;
+ self.gap_color = settings.gap_color;
colors::set_node_color([self.node_color[0], self.node_color[1], self.node_color[2], 1.0]);
diff --git a/src/geometry.rs b/src/geometry.rs
index 976c17a..03584b1 100644
--- a/src/geometry.rs
+++ b/src/geometry.rs
@@ -10,6 +10,63 @@ use opencl3::memory::{Buffer as ClBuffer, CL_MEM_READ_WRITE};
use opencl3::types::{cl_float, cl_int, CL_TRUE};
use glam::Vec3;
+struct SimpleRng {
+ state: u32,
+}
+
+impl SimpleRng {
+ fn new(seed: u32) -> Self {
+ Self { state: if seed == 0 { 1 } else { seed } }
+ }
+
+ fn next_u32(&mut self) -> u32 {
+ let mut x = self.state;
+ x ^= x << 13;
+ x ^= x >> 17;
+ x ^= x << 5;
+ self.state = x;
+ x
+ }
+
+ fn next_f32(&mut self) -> f32 {
+ (self.next_u32() as f32) / (u32::MAX as f32)
+ }
+}
+
+#[allow(dead_code)]
+fn ray_triangle_intersect(
+ origin: Vec3,
+ dir: Vec3,
+ v0: Vec3,
+ v1: Vec3,
+ v2: Vec3,
+) -> Option<f32> {
+ let edge1 = v1 - v0;
+ let edge2 = v2 - v0;
+ let h = dir.cross(edge2);
+ let a = edge1.dot(h);
+ if a.abs() < 1e-6 {
+ return None;
+ }
+ let f = 1.0 / a;
+ let s = origin - v0;
+ let u = f * s.dot(h);
+ if u < 0.0 || u > 1.0 {
+ return None;
+ }
+ let q = s.cross(edge1);
+ let v = f * dir.dot(q);
+ if v < 0.0 || u + v > 1.0 {
+ return None;
+ }
+ let t = f * edge2.dot(q);
+ if t > 1e-5 {
+ Some(t)
+ } else {
+ None
+ }
+}
+
#[derive(Clone, Debug, PartialEq)]
pub enum GAttribute {
Float(f32),
@@ -85,9 +142,7 @@ pub fn cube_vertices() -> Vec<Vertex3D> {
data.iter().map(|&(p, c)| Vertex3D { position: p, color: c }).collect()
}
-pub fn sphere_vertices(center: Vec3, radius: f32) -> Geometry {
- let lat_steps = 16;
- let lon_steps = 24;
+pub fn sphere_vertices_res(center: Vec3, radius: f32, lat_steps: usize, lon_steps: usize) -> Geometry {
let mut vertices = Vec::new();
for lat in 0..lat_steps {
@@ -112,6 +167,10 @@ pub fn sphere_vertices(center: Vec3, radius: f32) -> Geometry {
Geometry { vertices }
}
+pub fn sphere_vertices(center: Vec3, radius: f32) -> Geometry {
+ sphere_vertices_res(center, radius, 16, 24)
+}
+
fn sphere_point(center: Vec3, radius: f32, theta: f32, phi: f32) -> Vec3 {
center + Vec3::new(
radius * theta.sin() * phi.cos(),
@@ -255,6 +314,21 @@ pub fn find_node_by_name<'a>(root: &'a FsNode, name: &str) -> Option<&'a FsNode>
None
}
+pub fn find_parent_node<'a>(root: &'a FsNode, child_id: &str) -> Option<&'a FsNode> {
+ fn visit<'a>(node: &'a FsNode, child_id: &str) -> Option<&'a FsNode> {
+ for child in &node.children {
+ if child.id == child_id {
+ return Some(node);
+ }
+ if let Some(res) = visit(child, child_id) {
+ return Some(res);
+ }
+ }
+ None
+ }
+ visit(root, child_id)
+}
+
pub fn generate_single_node_geometry(root: &FsNode, target: &FsNode, visited: &mut Vec<String>) -> Option<Geometry> {
let mut err = None;
generate_single_node_geometry_with_errors(root, target, visited, &mut err)
@@ -295,13 +369,54 @@ pub fn generate_single_node_geometry_with_errors(
let py = center.y + t * 0.5 - 0.25;
let pz = center.z + r * angle.sin();
let pt_center = Vec3::new(px, py, pz);
- geom.merge(sphere_vertices(pt_center, 0.02));
+ geom.merge(sphere_vertices_res(pt_center, 0.02, 6, 8));
}
Some(geom)
} else if target.node_type.eq_ignore_ascii_case("transform") {
resolve_transform_geometry_with_errors(root, target, visited, ocl_error)
+ } else if target.node_type.eq_ignore_ascii_case("scatter") {
+ resolve_scatter_geometry_with_errors(root, target, visited, ocl_error)
} else if target.node_type.eq_ignore_ascii_case("opencl") {
resolve_opencl_geometry_with_errors(root, target, visited, ocl_error)
+ } else if target.node_type.eq_ignore_ascii_case("node") {
+ if let Some(output_node) = target.children.iter().find(|c| c.node_type.eq_ignore_ascii_case("output")) {
+ generate_single_node_geometry_with_errors(root, output_node, visited, ocl_error)
+ } else {
+ None
+ }
+ } else if target.node_type.eq_ignore_ascii_case("output") {
+ let input_name = node_param_str(target, "Input", "");
+ if input_name.is_empty() {
+ None
+ } else {
+ let parent_node = find_parent_node(root, &target.id);
+ let input_node = if let Some(parent) = parent_node {
+ parent.children.iter().find(|c| c.name == input_name || c.id == input_name)
+ } else {
+ None
+ };
+ let input_node = input_node.or_else(|| find_node_by_name(root, &input_name));
+ if let Some(node) = input_node {
+ generate_single_node_geometry_with_errors(root, node, visited, ocl_error)
+ } else {
+ None
+ }
+ }
+ } else if target.node_type.eq_ignore_ascii_case("input") {
+ if let Some(parent) = find_parent_node(root, &target.id) {
+ let input_name = node_param_str(parent, "Input", "");
+ if !input_name.is_empty() {
+ if let Some(input_node) = find_node_by_name(root, &input_name) {
+ generate_single_node_geometry_with_errors(root, input_node, visited, ocl_error)
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+ } else {
+ None
+ }
} else {
None
};
@@ -336,6 +451,337 @@ pub fn resolve_transform_geometry_with_errors(
Some(geom)
}
+pub fn resolve_scatter_geometry(root: &FsNode, target: &FsNode, visited: &mut Vec<String>) -> Option<Geometry> {
+ let mut err = None;
+ resolve_scatter_geometry_with_errors(root, target, visited, &mut err)
+}
+
+struct PrecomputedTriangle {
+ v0: Vec3,
+ edge1: Vec3,
+ edge2: Vec3,
+ h: Vec3,
+ f: f32,
+}
+
+pub fn resolve_scatter_geometry_with_errors(
+ root: &FsNode,
+ target: &FsNode,
+ visited: &mut Vec<String>,
+ ocl_error: &mut Option<String>,
+) -> Option<Geometry> {
+ if visited.contains(&target.id) {
+ return None;
+ }
+ visited.push(target.id.clone());
+
+ let input_name = node_param_str(target, "Input", "");
+ if input_name.is_empty() {
+ visited.pop();
+ return None;
+ }
+ let input_node = match find_node_by_name(root, &input_name) {
+ Some(node) => node,
+ None => {
+ visited.pop();
+ return None;
+ }
+ };
+ let geom = match generate_single_node_geometry_with_errors(root, input_node, visited, ocl_error) {
+ Some(g) => g,
+ None => {
+ visited.pop();
+ return None;
+ }
+ };
+
+ let num_points = node_param_f32(target, "Points", 100.0) as usize;
+ let radius = node_param_f32(target, "Radius", 0.02);
+
+ let ray_dir = Vec3::new(0.19, 0.98, 0.05).normalize();
+ let mut triangles = Vec::new();
+ let mut min_pos = Vec3::splat(f32::MAX);
+ let mut max_pos = Vec3::splat(f32::MIN);
+
+ for chunk in geom.vertices.chunks_exact(3) {
+ let v0 = Vec3::from_array(chunk[0].pos);
+ let v1 = Vec3::from_array(chunk[1].pos);
+ let v2 = Vec3::from_array(chunk[2].pos);
+
+ min_pos = min_pos.min(v0).min(v1).min(v2);
+ max_pos = max_pos.max(v0).max(v1).max(v2);
+
+ let edge1 = v1 - v0;
+ let edge2 = v2 - v0;
+ let h = ray_dir.cross(edge2);
+ let a = edge1.dot(h);
+ if a.abs() >= 1e-6 {
+ let f = 1.0 / a;
+ triangles.push(PrecomputedTriangle {
+ v0,
+ edge1,
+ edge2,
+ h,
+ f,
+ });
+ }
+ }
+
+ let res = if triangles.is_empty() {
+ Geometry::new()
+ } else {
+ let mut rng = SimpleRng::new(1337);
+ let mut scattered_geom = Geometry::new();
+ let mut found_count = 0;
+ let max_attempts = (num_points * 100).max(10_000);
+
+ for _ in 0..max_attempts {
+ if found_count >= num_points {
+ break;
+ }
+ let rx = min_pos.x + rng.next_f32() * (max_pos.x - min_pos.x);
+ let ry = min_pos.y + rng.next_f32() * (max_pos.y - min_pos.y);
+ let rz = min_pos.z + rng.next_f32() * (max_pos.z - min_pos.z);
+ let candidate = Vec3::new(rx, ry, rz);
+
+ let mut intersection_count = 0;
+ for tri in &triangles {
+ let s = candidate - tri.v0;
+ let u = tri.f * s.dot(tri.h);
+ if u < 0.0 || u > 1.0 {
+ continue;
+ }
+ let q = s.cross(tri.edge1);
+ let v = tri.f * ray_dir.dot(q);
+ if v < 0.0 || u + v > 1.0 {
+ continue;
+ }
+ let t = tri.f * tri.edge2.dot(q);
+ if t > 1e-5 {
+ intersection_count += 1;
+ }
+ }
+
+ if intersection_count % 2 == 1 {
+ scattered_geom.merge(sphere_vertices_res(candidate, radius, 6, 8));
+ found_count += 1;
+ }
+ }
+ scattered_geom
+ };
+
+ visited.pop();
+ Some(res)
+}
+
+fn rewrite_kernel_signature(code: &str) -> String {
+ let bytes = code.as_bytes();
+ if let Some(process_idx) = code.find("process") {
+ let mut idx = process_idx + "process".len();
+ while idx < bytes.len() && (bytes[idx] as char).is_whitespace() {
+ idx += 1;
+ }
+ if idx < bytes.len() && bytes[idx] == b'(' {
+ let start_args = idx + 1;
+ let mut paren_count = 1;
+ let mut end_args = start_args;
+ while end_args < bytes.len() && paren_count > 0 {
+ if bytes[end_args] == b'(' {
+ paren_count += 1;
+ } else if bytes[end_args] == b')' {
+ paren_count -= 1;
+ }
+ end_args += 1;
+ }
+ if paren_count == 0 {
+ let closing_paren_idx = end_args - 1;
+ let before = &code[..closing_paren_idx];
+ let after = &code[closing_paren_idx..];
+ let args_str = &code[start_args..closing_paren_idx].trim();
+ let insertion = if args_str.is_empty() {
+ "__global const float* param_values"
+ } else {
+ ", __global const float* param_values"
+ };
+ return format!("{}{}{}", before, insertion, after);
+ }
+ }
+ }
+ code.to_string()
+}
+
+pub fn preprocess_opencl_code(code: &str) -> String {
+ let parsed_params = parse_dynamic_params(code);
+ if parsed_params.is_empty() {
+ return code.to_string();
+ }
+
+ let mut param_indices = std::collections::HashMap::new();
+ let mut flat_idx = 0;
+ for p in &parsed_params {
+ param_indices.insert(p.name.clone(), flat_idx);
+ if p.param_type == "float3" {
+ flat_idx += 3;
+ } else {
+ flat_idx += 1;
+ }
+ }
+
+ let mut processed = rewrite_kernel_signature(code);
+
+ let prefixes = [("chf", "slider"), ("chi", "spinbox"), ("chv", "float3"), ("chb", "toggle")];
+ for &(prefix, _) in &prefixes {
+ let pattern = format!("{}(", prefix);
+ while let Some(pos) = processed.find(&pattern) {
+ let start_idx = pos + pattern.len();
+ let mut paren_count = 1;
+ let mut end_pos = start_idx;
+ let bytes = processed.as_bytes();
+ while end_pos < bytes.len() && paren_count > 0 {
+ if bytes[end_pos] == b'(' {
+ paren_count += 1;
+ } else if bytes[end_pos] == b')' {
+ paren_count -= 1;
+ }
+ end_pos += 1;
+ }
+ if paren_count == 0 {
+ let full_match = &processed[pos..end_pos];
+ let args_str = &processed[start_idx..end_pos - 1];
+ let mut replacement = None;
+ if let Some(first_quote_pos) = args_str.find(|c| c == '"' || c == '\'') {
+ let quote_char = args_str.chars().nth(first_quote_pos).unwrap();
+ if let Some(second_quote_pos) = args_str[first_quote_pos + 1..].find(quote_char) {
+ let name = &args_str[first_quote_pos + 1..first_quote_pos + 1 + second_quote_pos];
+ if !name.is_empty() {
+ if let Some(&flat_idx) = param_indices.get(name) {
+ match prefix {
+ "chf" => {
+ replacement = Some(format!("param_values[{}]", flat_idx));
+ }
+ "chi" | "chb" => {
+ replacement = Some(format!("((int)param_values[{}])", flat_idx));
+ }
+ "chv" => {
+ replacement = Some(format!(
+ "(float3)(param_values[{}], param_values[{}], param_values[{}])",
+ flat_idx, flat_idx + 1, flat_idx + 2
+ ));
+ }
+ _ => {}
+ }
+ }
+ }
+ }
+ }
+ if let Some(rep) = replacement {
+ processed = processed.replace(full_match, &rep);
+ } else {
+ processed = processed.replace(full_match, "0");
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ processed
+}
+
+pub fn parse_dynamic_params(code: &str) -> Vec<ParamDef> {
+ let mut parsed = Vec::new();
+ let prefixes = [("chf", "slider"), ("chi", "spinbox"), ("chv", "float3"), ("chb", "toggle")];
+ for &(prefix, ptype) in &prefixes {
+ let pattern = format!("{}(", prefix);
+ let mut start_idx = 0;
+ while let Some(pos) = code[start_idx..].find(&pattern) {
+ let actual_pos = start_idx + pos;
+ start_idx = actual_pos + pattern.len();
+ let mut paren_count = 1;
+ let mut end_pos = start_idx;
+ let code_bytes = code.as_bytes();
+ while end_pos < code_bytes.len() && paren_count > 0 {
+ if code_bytes[end_pos] == b'(' {
+ paren_count += 1;
+ } else if code_bytes[end_pos] == b')' {
+ paren_count -= 1;
+ }
+ end_pos += 1;
+ }
+ if paren_count == 0 {
+ let args_str = &code[start_idx..end_pos - 1];
+ if let Some(first_quote_pos) = args_str.find(|c| c == '"' || c == '\'') {
+ let quote_char = args_str.chars().nth(first_quote_pos).unwrap();
+ if let Some(second_quote_pos) = args_str[first_quote_pos + 1..].find(quote_char) {
+ let name = &args_str[first_quote_pos + 1..first_quote_pos + 1 + second_quote_pos];
+ if !name.is_empty() {
+ let mut default_val = match prefix {
+ "chf" => "0.5".to_string(),
+ "chi" => "0".to_string(),
+ "chb" => "false".to_string(),
+ "chv" => "0.00:0.00:0.00".to_string(),
+ _ => "".to_string(),
+ };
+ let rest = &args_str[first_quote_pos + 1 + second_quote_pos + 1..];
+ if let Some(comma_pos) = rest.find(',') {
+ let val_part = rest[comma_pos + 1..].trim();
+ if !val_part.is_empty() {
+ let mut clean_val = val_part.to_string();
+ if clean_val.ends_with('f') {
+ clean_val.pop();
+ }
+ if clean_val.ends_with("f32") {
+ clean_val.truncate(clean_val.len() - 3);
+ }
+ let clean_val = clean_val.trim();
+ if prefix == "chv" {
+ let parts: Vec<String> = val_part.split(',')
+ .map(|p| {
+ let mut s = p.trim().to_string();
+ if s.ends_with('f') { s.pop(); }
+ if s.ends_with("f32") { s.truncate(s.len() - 3); }
+ s.trim().to_string()
+ })
+ .collect();
+ if parts.len() >= 3 {
+ if let (Ok(x), Ok(y), Ok(z)) = (parts[0].parse::<f32>(), parts[1].parse::<f32>(), parts[2].parse::<f32>()) {
+ default_val = format!("{:.2}:{:.2}:{:.2}", x, y, z);
+ }
+ } else {
+ if let Ok(val) = clean_val.parse::<f32>() {
+ default_val = format!("{:.2}:{:.2}:{:.2}", val, val, val);
+ }
+ }
+ } else {
+ default_val = clean_val.to_string();
+ }
+ }
+ }
+ if !parsed.iter().any(|p: &ParamDef| p.name == name) {
+ let (min, max, step) = match prefix {
+ "chf" => (Some(0.0), Some(2.0), Some(0.01)),
+ "chi" => (Some(0.0), Some(1000.0), Some(1.0)),
+ _ => (None, None, None),
+ };
+ parsed.push(ParamDef {
+ name: name.to_string(),
+ label: String::new(),
+ param_type: ptype.to_string(),
+ default: default_val,
+ options: Vec::new(),
+ min,
+ max,
+ step,
+ });
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ parsed
+}
+
pub fn resolve_opencl_geometry_with_errors(
root: &FsNode,
target: &FsNode,
@@ -344,14 +790,49 @@ pub fn resolve_opencl_geometry_with_errors(
) -> Option<Geometry> {
let input_name = node_param_str(target, "Input", "");
let mut geom = if !input_name.is_empty() {
- let input_node = find_node_by_name(root, &input_name)?;
- generate_single_node_geometry_with_errors(root, input_node, visited, ocl_error)?
+ if let Some(input_node) = find_node_by_name(root, &input_name) {
+ generate_single_node_geometry_with_errors(root, input_node, visited, ocl_error).unwrap_or_default()
+ } else {
+ Geometry::default()
+ }
} else {
Geometry::default()
};
let code = node_param_str(target, "Code", "");
if !code.is_empty() {
- if let Err(e) = run_opencl_kernel(&code, &mut geom) {
+ let parsed_params = parse_dynamic_params(&code);
+ let mut flat_values = Vec::new();
+ for p in &parsed_params {
+ let mut val_str = node_param_str(target, &p.name, &p.default);
+ if !target.params.iter().any(|p_def| p_def.name.eq_ignore_ascii_case(&p.name)) {
+ if let Some(parent) = find_parent_node(root, &target.id) {
+ val_str = node_param_str(parent, &p.name, &val_str);
+ }
+ }
+ if p.param_type == "float3" {
+ let parts: Vec<&str> = val_str.split(':').collect();
+ let (x, y, z) = if parts.len() >= 3 {
+ (parts[0].parse::<f32>().unwrap_or(0.0), parts[1].parse::<f32>().unwrap_or(0.0), parts[2].parse::<f32>().unwrap_or(0.0))
+ } else {
+ (0.0, 0.0, 0.0)
+ };
+ flat_values.push(x);
+ flat_values.push(y);
+ flat_values.push(z);
+ } else {
+ let val = if val_str.eq_ignore_ascii_case("true") {
+ 1.0
+ } else if val_str.eq_ignore_ascii_case("false") {
+ 0.0
+ } else {
+ val_str.parse::<f32>().unwrap_or(0.0)
+ };
+ flat_values.push(val);
+ }
+ }
+
+ let processed_code = preprocess_opencl_code(&code);
+ if let Err(e) = run_opencl_kernel_with_params(&processed_code, &mut geom, &flat_values) {
if ocl_error.is_none() {
*ocl_error = Some(e);
}
@@ -407,6 +888,10 @@ fn init_opencl() -> Option<OpenClCache> {
}
pub fn run_opencl_kernel(code: &str, geom: &mut Geometry) -> Result<(), String> {
+ run_opencl_kernel_with_params(code, geom, &[])
+}
+
+pub fn run_opencl_kernel_with_params(code: &str, geom: &mut Geometry, params: &[f32]) -> Result<(), String> {
let is_generator = code.contains("out_count");
if geom.vertices.is_empty() && !is_generator {
return Ok(());
@@ -434,6 +919,20 @@ pub fn run_opencl_kernel(code: &str, geom: &mut Geometry) -> Result<(), String>
let context = &cache.context;
let queue = &cache.queue;
+ // Prepare parameter values buffer
+ let mut param_values_data = params.to_vec();
+ if param_values_data.is_empty() {
+ param_values_data.push(0.0);
+ }
+ let mut param_values_buf = unsafe {
+ ClBuffer::<cl_float>::create(&context, CL_MEM_READ_WRITE, param_values_data.len(), std::ptr::null_mut())
+ .map_err(|e| format!("Failed to create param_values buffer: {:?}", e))?
+ };
+ let _write_param_event = unsafe {
+ queue.enqueue_write_buffer(&mut param_values_buf, CL_TRUE, 0, ¶m_values_data, &[])
+ .map_err(|e| format!("Failed to write param_values buffer: {:?}", e))?
+ };
+
if is_generator {
let in_count = geom.vertices.len();
let max_vertices = 200_000;
@@ -448,21 +947,23 @@ pub fn run_opencl_kernel(code: &str, geom: &mut Geometry) -> Result<(), String>
// Create GPU buffers for inputs
let mut in_pos_buf = unsafe {
- ClBuffer::<cl_float>::create(&context, CL_MEM_READ_WRITE, in_count * 3, std::ptr::null_mut())
+ ClBuffer::<cl_float>::create(&context, CL_MEM_READ_WRITE, (in_count * 3).max(1), std::ptr::null_mut())
.map_err(|e| format!("Failed to create input positions buffer: {:?}", e))?
};
let mut in_col_buf = unsafe {
- ClBuffer::<cl_float>::create(&context, CL_MEM_READ_WRITE, in_count * 3, std::ptr::null_mut())
+ ClBuffer::<cl_float>::create(&context, CL_MEM_READ_WRITE, (in_count * 3).max(1), std::ptr::null_mut())
.map_err(|e| format!("Failed to create input colors buffer: {:?}", e))?
};
// Write input data to GPU
let _write_pos_event = unsafe {
- queue.enqueue_write_buffer(&mut in_pos_buf, CL_TRUE, 0, &in_pos_data, &[])
+ let write_data = if in_pos_data.is_empty() { &[0.0f32] } else { &in_pos_data[..] };
+ queue.enqueue_write_buffer(&mut in_pos_buf, CL_TRUE, 0, write_data, &[])
.map_err(|e| format!("Failed to write input positions buffer: {:?}", e))?
};
let _write_col_event = unsafe {
- queue.enqueue_write_buffer(&mut in_col_buf, CL_TRUE, 0, &in_col_data, &[])
+ let write_data = if in_col_data.is_empty() { &[0.0f32] } else { &in_col_data[..] };
+ queue.enqueue_write_buffer(&mut in_col_buf, CL_TRUE, 0, write_data, &[])
.map_err(|e| format!("Failed to write input colors buffer: {:?}", e))?
};
@@ -489,16 +990,22 @@ pub fn run_opencl_kernel(code: &str, geom: &mut Geometry) -> Result<(), String>
// Execute kernel
let global_work_size = if in_count == 0 { 1 } else { in_count };
+ let mut exec = ExecuteKernel::new(kernel);
let kernel_event = unsafe {
- ExecuteKernel::new(kernel)
- .set_arg(&in_pos_buf)
+ exec.set_arg(&in_pos_buf)
.set_arg(&in_col_buf)
.set_arg(&(in_count as cl_int))
.set_arg(&out_pos_buf)
.set_arg(&out_col_buf)
.set_arg(&out_count_buf)
- .set_arg(&(max_vertices as cl_int))
- .set_global_work_size(global_work_size)
+ .set_arg(&(max_vertices as cl_int));
+
+ let num_args = kernel.num_args().unwrap_or(0);
+ if num_args >= 8 {
+ exec.set_arg(¶m_values_buf);
+ }
+
+ exec.set_global_work_size(global_work_size)
.enqueue_nd_range(&queue)
.map_err(|e| format!("Failed to enqueue kernel: {:?}", e))?
};
@@ -571,12 +1078,18 @@ pub fn run_opencl_kernel(code: &str, geom: &mut Geometry) -> Result<(), String>
};
// Execute kernel
+ let mut exec = ExecuteKernel::new(kernel);
let kernel_event = unsafe {
- ExecuteKernel::new(kernel)
- .set_arg(&pos_buf)
+ exec.set_arg(&pos_buf)
.set_arg(&col_buf)
- .set_arg(&(count as cl_int))
- .set_global_work_size(count)
+ .set_arg(&(count as cl_int));
+
+ let num_args = kernel.num_args().unwrap_or(0);
+ if num_args >= 4 {
+ exec.set_arg(¶m_values_buf);
+ }
+
+ exec.set_global_work_size(count)
.enqueue_nd_range(&queue)
.map_err(|e| format!("Failed to enqueue kernel: {:?}", e))?
};
@@ -603,24 +1116,38 @@ pub fn run_opencl_kernel(code: &str, geom: &mut Geometry) -> Result<(), String>
Ok(())
}
+pub fn is_geometry_node_type(node_type: &str) -> bool {
+ let nt = node_type.to_lowercase();
+ nt == "sphere"
+ || nt == "line"
+ || nt == "add"
+ || nt == "transform"
+ || nt == "opencl"
+ || nt == "box"
+ || nt == "input"
+ || nt == "output"
+ || nt == "scatter"
+}
+
pub fn network_sphere_vertices(root: &FsNode) -> Geometry {
let mut err = None;
network_sphere_vertices_with_errors(root, &mut err)
}
pub fn network_sphere_vertices_with_errors(root: &FsNode, ocl_error: &mut Option<String>) -> Geometry {
- fn visit(root: &FsNode, node: &FsNode, count: &mut usize, out: &mut Geometry, ocl_error: &mut Option<String>) {
+ fn visit(root: &FsNode, node: &FsNode, parent_visible: bool, count: &mut usize, out: &mut Geometry, ocl_error: &mut Option<String>) {
+ let is_visible = parent_visible && node.geometry_visible;
if node.node_type.eq_ignore_ascii_case("sphere") {
let idx = *count;
*count += 1;
- if node.geometry_visible {
+ if is_visible {
let center = Vec3::new((idx % 4) as f32 * 1.25 - 1.875, 0.55, -((idx / 4) as f32) * 1.25);
out.merge(sphere_vertices(center, node_param_f32(node, "Radius", 0.5).max(0.05)));
}
} else if node.node_type.eq_ignore_ascii_case("line") {
let idx = *count;
*count += 1;
- if node.geometry_visible {
+ if is_visible {
let start = Vec3::new((idx % 4) as f32 * 1.25 - 1.875, 0.55, -((idx / 4) as f32) * 1.25);
let length = node_param_f32(node, "Length", 1.0);
let thickness = node_param_f32(node, "Thickness", 0.02);
@@ -630,7 +1157,7 @@ pub fn network_sphere_vertices_with_errors(root: &FsNode, ocl_error: &mut Option
} else if node.node_type.eq_ignore_ascii_case("add") {
let idx = *count;
*count += 1;
- if node.geometry_visible {
+ if is_visible {
let center = Vec3::new((idx % 4) as f32 * 1.25 - 1.875, 0.55, -((idx / 4) as f32) * 1.25);
let num_points = node_param_f32(node, "Points", 100.0) as i32;
for i in 0..num_points {
@@ -641,22 +1168,31 @@ pub fn network_sphere_vertices_with_errors(root: &FsNode, ocl_error: &mut Option
let py = center.y + t * 0.5 - 0.25;
let pz = center.z + r * angle.sin();
let pt_center = Vec3::new(px, py, pz);
- out.merge(sphere_vertices(pt_center, 0.02));
+ out.merge(sphere_vertices_res(pt_center, 0.02, 6, 8));
}
}
} else if node.node_type.eq_ignore_ascii_case("transform") {
- let idx = *count;
+ let _idx = *count;
*count += 1;
- if node.geometry_visible {
+ if is_visible {
let mut visited = Vec::new();
if let Some(geom) = resolve_transform_geometry_with_errors(root, node, &mut visited, ocl_error) {
out.merge(geom);
}
}
+ } else if node.node_type.eq_ignore_ascii_case("scatter") {
+ let _idx = *count;
+ *count += 1;
+ if is_visible {
+ let mut visited = Vec::new();
+ if let Some(geom) = resolve_scatter_geometry_with_errors(root, node, &mut visited, ocl_error) {
+ out.merge(geom);
+ }
+ }
} else if node.node_type.eq_ignore_ascii_case("opencl") {
- let idx = *count;
+ let _idx = *count;
*count += 1;
- if node.geometry_visible {
+ if is_visible {
let mut visited = Vec::new();
if let Some(geom) = resolve_opencl_geometry_with_errors(root, node, &mut visited, ocl_error) {
out.merge(geom);
@@ -664,14 +1200,14 @@ pub fn network_sphere_vertices_with_errors(root: &FsNode, ocl_error: &mut Option
}
}
for child in &node.children {
- visit(root, child, count, out, ocl_error);
+ visit(root, child, is_visible, count, out, ocl_error);
}
}
let mut out = Geometry::new();
let mut count = 0;
for child in &root.children {
- visit(root, child, &mut count, &mut out, ocl_error);
+ visit(root, child, true, &mut count, &mut out, ocl_error);
}
out
}
@@ -683,7 +1219,8 @@ pub fn find_sphere_index(root: &FsNode, target: &FsNode) -> Option<usize> {
|| node.node_type.eq_ignore_ascii_case("line")
|| node.node_type.eq_ignore_ascii_case("add")
|| node.node_type.eq_ignore_ascii_case("transform")
- || node.node_type.eq_ignore_ascii_case("opencl") {
+ || node.node_type.eq_ignore_ascii_case("opencl")
+ || node.node_type.eq_ignore_ascii_case("scatter") {
let idx = *count;
*count += 1;
if is_target {
@@ -1007,6 +1544,9 @@ mod tests {
#[test]
fn test_add_node_points() {
let add_node = FsNode {
+ id: String::new(),
+ inputs: 1,
+ outputs: 1,
name: "Add points test".to_string(),
node_type: "add".to_string(),
children: vec![],
@@ -1026,6 +1566,9 @@ mod tests {
position: (0.0, 0.0),
};
let root = FsNode {
+ id: String::new(),
+ inputs: 1,
+ outputs: 1,
name: "root".to_string(),
node_type: "node".to_string(),
children: vec![add_node],
@@ -1034,12 +1577,15 @@ mod tests {
position: (0.0, 0.0),
};
let geom = network_sphere_vertices(&root);
- assert_eq!(geom.vertices.len(), 5 * 2304);
+ assert_eq!(geom.vertices.len(), 5 * 288);
}
#[test]
fn test_transform_node() {
let sphere = FsNode {
+ id: String::new(),
+ inputs: 1,
+ outputs: 1,
name: "Sphere 1".to_string(),
node_type: "sphere".to_string(),
children: vec![],
@@ -1059,6 +1605,9 @@ mod tests {
position: (0.0, 0.0),
};
let transform1 = FsNode {
+ id: String::new(),
+ inputs: 1,
+ outputs: 1,
name: "Transform 1".to_string(),
node_type: "transform".to_string(),
children: vec![],
@@ -1088,6 +1637,9 @@ mod tests {
position: (0.0, 0.0),
};
let root = FsNode {
+ id: String::new(),
+ inputs: 1,
+ outputs: 1,
name: "root".to_string(),
node_type: "node".to_string(),
children: vec![sphere.clone(), transform1.clone()],
@@ -1109,6 +1661,9 @@ mod tests {
// Test chained transform
let transform2 = FsNode {
+ id: String::new(),
+ inputs: 1,
+ outputs: 1,
name: "Transform 2".to_string(),
node_type: "transform".to_string(),
children: vec![],
@@ -1138,6 +1693,9 @@ mod tests {
position: (0.0, 0.0),
};
let root_chained = FsNode {
+ id: String::new(),
+ inputs: 1,
+ outputs: 1,
name: "root".to_string(),
node_type: "node".to_string(),
children: vec![sphere, transform1, transform2.clone()],
@@ -1156,6 +1714,9 @@ mod tests {
// Test loop detection
let transform_loop = FsNode {
+ id: String::new(),
+ inputs: 1,
+ outputs: 1,
name: "Transform Loop".to_string(),
node_type: "transform".to_string(),
children: vec![],
@@ -1185,6 +1746,9 @@ mod tests {
position: (0.0, 0.0),
};
let root_loop = FsNode {
+ id: String::new(),
+ inputs: 1,
+ outputs: 1,
name: "root".to_string(),
node_type: "node".to_string(),
children: vec![transform_loop.clone()],
@@ -1205,6 +1769,9 @@ mod tests {
}
let sphere = FsNode {
+ id: String::new(),
+ inputs: 1,
+ outputs: 1,
name: "Sphere 1".to_string(),
node_type: "sphere".to_string(),
children: vec![],
@@ -1225,6 +1792,9 @@ mod tests {
};
let opencl_node = FsNode {
+ id: String::new(),
+ inputs: 1,
+ outputs: 1,
name: "OpenCL 1".to_string(),
node_type: "opencl".to_string(),
children: vec![],
@@ -1262,6 +1832,9 @@ mod tests {
};
let root = FsNode {
+ id: String::new(),
+ inputs: 1,
+ outputs: 1,
name: "root".to_string(),
node_type: "node".to_string(),
children: vec![sphere, opencl_node.clone()],
@@ -1280,6 +1853,108 @@ mod tests {
let avg_y = geom.vertices.iter().map(|v| v.pos[1]).sum::<f32>() / geom.vertices.len() as f32;
assert!((avg_y - 2.55).abs() < 0.01);
}
+
+ #[test]
+ fn test_scatter_node() {
+ let sphere = FsNode {
+ id: "sphere1".to_string(),
+ inputs: 1,
+ outputs: 1,
+ name: "Sphere 1".to_string(),
+ node_type: "sphere".to_string(),
+ children: vec![],
+ params: vec![
+ ParamDef {
+ name: "Radius".to_string(),
+ label: String::new(),
+ param_type: "slider".to_string(),
+ default: "0.5".to_string(),
+ options: vec![],
+ min: None,
+ max: None,
+ step: None,
+ }
+ ],
+ geometry_visible: true,
+ position: (0.0, 0.0),
+ };
+
+ let scatter = FsNode {
+ id: "scatter1".to_string(),
+ inputs: 1,
+ outputs: 1,
+ name: "Scatter 1".to_string(),
+ node_type: "scatter".to_string(),
+ children: vec![],
+ params: vec![
+ ParamDef {
+ name: "Input".to_string(),
+ label: String::new(),
+ param_type: "text".to_string(),
+ default: "Sphere 1".to_string(),
+ options: vec![],
+ min: None,
+ max: None,
+ step: None,
+ },
+ ParamDef {
+ name: "Points".to_string(),
+ label: String::new(),
+ param_type: "spinbox".to_string(),
+ default: "15".to_string(),
+ options: vec![],
+ min: None,
+ max: None,
+ step: None,
+ },
+ ParamDef {
+ name: "Radius".to_string(),
+ label: String::new(),
+ param_type: "slider".to_string(),
+ default: "0.02".to_string(),
+ options: vec![],
+ min: None,
+ max: None,
+ step: None,
+ }
+ ],
+ geometry_visible: true,
+ position: (0.0, 0.0),
+ };
+
+ let root = FsNode {
+ id: String::new(),
+ inputs: 1,
+ outputs: 1,
+ name: "root".to_string(),
+ node_type: "node".to_string(),
+ children: vec![sphere, scatter.clone()],
+ params: vec![],
+ geometry_visible: true,
+ position: (0.0, 0.0),
+ };
+
+ let mut visited = Vec::new();
+ let geom = resolve_scatter_geometry(&root, &scatter, &mut visited).unwrap();
+
+ // 15 scattered spheres. Each sphere with lat_steps=6, lon_steps=8 has:
+ // 6 * 8 = 48 quads. Each quad has 6 vertices. 48 * 6 = 288 vertices.
+ // 15 * 288 = 4320 vertices.
+ assert_eq!(geom.vertices.len(), 15 * 288);
+
+ // Center of sphere at idx 0 is Vec3::new(-1.875, 0.55, 0.0). Radius = 0.5.
+ // Let's check that each scattered sphere's center is indeed inside the parent sphere.
+ let center = Vec3::new(-1.875, 0.55, 0.0);
+ for chunk in geom.vertices.chunks_exact(288) {
+ let mut sum = Vec3::ZERO;
+ for v in chunk {
+ sum += Vec3::from_array(v.pos);
+ }
+ let avg = sum / 288.0;
+ let dist = avg.distance(center);
+ assert!(dist <= 0.5, "Scattered point center {:?} (distance {}) is outside the sphere of radius 0.5", avg, dist);
+ }
+ }
}
diff --git a/src/main.rs b/src/main.rs
index 8fdad18..c6389d6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -50,7 +50,7 @@ pub mod project;
pub mod render;
pub mod shortcut;
-use app::{State, CustomEvent, HttpAction, ModifiersState, TouchPhase, DesignSettings, param_display};
+use app::{State, CustomEvent, HttpAction, ModifiersState, TouchPhase, DesignSettings, param_display, FsNode, Project, ProjectViewState, ParamDef};
use window::{AppState, WindowEvent};
use api::start_http_server;
use geometry::*;
@@ -266,7 +266,11 @@ fn main() {
const KEY_REPEAT_INTERVAL: std::time::Duration = std::time::Duration::from_millis(50);
loop {
- let timeout = std::time::Duration::from_millis(16);
+ let timeout = if app.redraw {
+ std::time::Duration::ZERO
+ } else {
+ std::time::Duration::from_millis(16)
+ };
event_loop.dispatch(timeout, &mut app).unwrap();
if app.exit || app.state.as_ref().map(|s| s.exit_requested).unwrap_or(false) {
@@ -390,6 +394,8 @@ fn create_memfd_with_data(name: &str, data: &[u8]) -> std::io::Result<std::os::u
#[cfg(test)]
mod tests {
use super::*;
+ use crate::app::{get_next_visible_pane, LEFT_MENUBAR_IDX, RIGHT_MENUBAR_IDX, PARAM_MENUBAR_IDX, SPREADSHEET_MENUBAR_IDX};
+ use crate::shortcut::{Shortcut, ShortcutManager, Action};
#[test]
fn test_load_default_project() {
@@ -399,33 +405,230 @@ mod tests {
assert_eq!(proj.name, "Default Project");
assert_eq!(proj.view_state.active_camera, "Camera 1");
assert_eq!(proj.root.name, "root");
- assert_eq!(proj.root.children.len(), 3);
+ assert_eq!(proj.root.children.len(), 2);
assert_eq!(proj.root.children[0].name, "Camera 1");
assert_eq!(proj.root.children[0].position, (1.0, 1.0));
assert_eq!(proj.root.children[1].name, "Sphere 1");
assert_eq!(proj.root.children[1].position, (4.0, 2.0));
- assert_eq!(proj.root.children[2].name, "Transform 1");
- assert_eq!(proj.root.children[2].position, (4.0, 3.0));
+ }
+
+ #[test]
+ fn test_node_template_names() {
+ let templates_root = crate::app::load_fs_tree();
+ let templates = crate::app::flatten_node_templates(&templates_root);
+ assert!(!templates.is_empty(), "No templates loaded!");
+ for t in templates {
+ let last_char = t.label.chars().last().unwrap();
+ assert!(!last_char.is_ascii_digit(), "Template name '{}' ends with a digit, but templates should not have numeric suffixes in the add node popup.", t.label);
+ }
+ }
+
+ #[test]
+ fn test_subnet_template_child_resolution() {
+ let templates_root = crate::app::load_fs_tree();
+ let box_template = templates_root
+ .children
+ .iter()
+ .find(|t| t.name == "Box")
+ .expect("Box template should be loaded");
+
+ assert_eq!(box_template.children.len(), 3);
+
+ let input1 = box_template.children.iter().find(|c| c.name == "input1").unwrap();
+ assert_eq!(input1.node_type, "input");
+
+ let opencl1 = box_template.children.iter().find(|c| c.name == "opencl1").unwrap();
+ assert_eq!(opencl1.node_type, "opencl");
+
+ let input_param = opencl1.params.iter().find(|p| p.name == "Input").unwrap();
+ assert_eq!(input_param.default, "input1");
+
+ let update_param = opencl1.params.iter().find(|p| p.name == "Update Parameters").unwrap();
+ assert_eq!(update_param.param_type, "button");
+
+ let output1 = box_template.children.iter().find(|c| c.name == "output1").unwrap();
+ assert_eq!(output1.node_type, "output");
+ let output_input = output1.params.iter().find(|p| p.name == "Input").unwrap();
+ assert_eq!(output_input.default, "opencl1");
+ }
+
+ #[test]
+ fn test_sphere_subnet_geometry_generation() {
+ let templates_root = crate::app::load_fs_tree();
+ let sphere_template = templates_root
+ .children
+ .iter()
+ .find(|t| t.name == "Sphere")
+ .expect("Sphere template should be loaded");
+
+ assert_eq!(sphere_template.children.len(), 2);
+
+ let opencl1 = sphere_template.children.iter().find(|c| c.name == "opencl1").unwrap();
+ assert_eq!(opencl1.node_type, "opencl");
+
+ let output1 = sphere_template.children.iter().find(|c| c.name == "output1").unwrap();
+ assert_eq!(output1.node_type, "output");
+
+ let mut sphere_instance = sphere_template.clone();
+ sphere_instance.id = "sphere_inst".to_string();
+ for child in &mut sphere_instance.children {
+ child.id = format!("{}_{}", sphere_instance.id, child.name);
+ }
+
+ let root = FsNode {
+ id: "root".to_string(),
+ name: "root".to_string(),
+ node_type: "node".to_string(),
+ children: vec![sphere_instance],
+ params: vec![],
+ geometry_visible: true,
+ position: (0.0, 0.0),
+ inputs: 0,
+ outputs: 0,
+ };
+
+ let mut visited = Vec::new();
+ let mut ocl_err = None;
+ let geom = crate::geometry::generate_single_node_geometry_with_errors(
+ &root,
+ &root.children[0],
+ &mut visited,
+ &mut ocl_err,
+ ).expect("Geometry generation failed");
+
+ assert!(ocl_err.is_none(), "OpenCL compilation error: {:?}", ocl_err);
+ assert_eq!(geom.vertices.len(), 2304);
+
+ let mut max_dist: f32 = 0.0;
+ for v in &geom.vertices {
+ let dx = v.pos[0] - 0.0;
+ let dy = v.pos[1] - 0.55;
+ let dz = v.pos[2] - 0.0;
+ let dist = (dx*dx + dy*dy + dz*dz).sqrt();
+ if dist > max_dist {
+ max_dist = dist;
+ }
+ }
+ assert!((max_dist - 0.5).abs() < 0.01, "Expected radius around 0.5, got {}", max_dist);
+
+ let mut sphere_instance_2 = sphere_template.clone();
+ sphere_instance_2.id = "sphere_inst_2".to_string();
+ for child in &mut sphere_instance_2.children {
+ child.id = format!("{}_{}", sphere_instance_2.id, child.name);
+ }
+ if let Some(radius_param) = sphere_instance_2.params.iter_mut().find(|p| p.name == "Radius") {
+ radius_param.default = "1.0".to_string();
+ }
+
+ let root_2 = FsNode {
+ id: "root".to_string(),
+ name: "root".to_string(),
+ node_type: "node".to_string(),
+ children: vec![sphere_instance_2],
+ params: vec![],
+ geometry_visible: true,
+ position: (0.0, 0.0),
+ inputs: 0,
+ outputs: 0,
+ };
+
+ let mut visited_2 = Vec::new();
+ let mut ocl_err_2 = None;
+ let geom_2 = crate::geometry::generate_single_node_geometry_with_errors(
+ &root_2,
+ &root_2.children[0],
+ &mut visited_2,
+ &mut ocl_err_2,
+ ).expect("Geometry generation failed");
+
+ assert!(ocl_err_2.is_none(), "OpenCL compilation error: {:?}", ocl_err_2);
+ assert_eq!(geom_2.vertices.len(), 2304);
+
+ let mut max_dist_2: f32 = 0.0;
+ for v in &geom_2.vertices {
+ let dx = v.pos[0] - 0.0;
+ let dy = v.pos[1] - 0.55;
+ let dz = v.pos[2] - 0.0;
+ let dist = (dx*dx + dy*dy + dz*dz).sqrt();
+ if dist > max_dist_2 {
+ max_dist_2 = dist;
+ }
+ }
+ assert!((max_dist_2 - 1.0).abs() < 0.01, "Expected radius around 1.0, got {}", max_dist_2);
+ }
+
+ #[test]
+ fn test_dynamic_parameters_parsing_and_preprocessing() {
+ let code = r#"
+ float freq = chf("freq", 4.0f);
+ int count = chi("count", 15);
+ float3 col = chv("col", 0.8f, 0.2f, 0.2f);
+ float scale = chf("scale");
+ "#;
+
+ let parsed = crate::geometry::parse_dynamic_params(code);
+ assert_eq!(parsed.len(), 4);
+
+ assert_eq!(parsed[0].name, "freq");
+ assert_eq!(parsed[0].param_type, "slider");
+ assert_eq!(parsed[0].default, "4.0");
+
+ assert_eq!(parsed[1].name, "scale");
+ assert_eq!(parsed[1].param_type, "slider");
+ assert_eq!(parsed[1].default, "0.5");
+
+ assert_eq!(parsed[2].name, "count");
+ assert_eq!(parsed[2].param_type, "spinbox");
+ assert_eq!(parsed[2].default, "15");
+
+ assert_eq!(parsed[3].name, "col");
+ assert_eq!(parsed[3].param_type, "float3");
+ assert_eq!(parsed[3].default, "0.80:0.20:0.20");
+
+ let mut target = FsNode {
+ id: "node1".to_string(),
+ name: "OpenCL Node".to_string(),
+ node_type: "opencl".to_string(),
+ children: Vec::new(),
+ params: parsed,
+ geometry_visible: true,
+ position: (0.0, 0.0),
+ inputs: 1,
+ outputs: 1,
+ };
+
+ target.params[1].default = "1.25".to_string(); // "scale"
+ let preprocessed = crate::geometry::preprocess_opencl_code(code);
+ assert!(preprocessed.contains("param_values[0]"));
+ assert!(preprocessed.contains("((int)param_values[2])"));
+ assert!(preprocessed.contains("(float3)(param_values[3], param_values[4], param_values[5])"));
+ assert!(preprocessed.contains("param_values[1]"));
}
#[test]
fn test_project_serialization_roundtrip() {
let root = FsNode {
+ id: "root".to_string(),
name: "test_root".to_string(),
node_type: "node".to_string(),
children: vec![
FsNode {
+ id: "child1".to_string(),
name: "child1".to_string(),
node_type: "sphere".to_string(),
children: vec![],
params: vec![],
geometry_visible: true,
position: (5.0, 6.0),
+ inputs: 0,
+ outputs: 1,
}
],
params: vec![],
geometry_visible: true,
position: (0.0, 0.0),
+ inputs: 0,
+ outputs: 0,
};
let view_state = ProjectViewState {
active_camera: "child1".to_string(),
@@ -567,8 +770,6 @@ mod tests {
assert_eq!(settings.show_camera_pivot_enabled, false);
assert_eq!(settings.camera_pivot_size, 1.0);
assert_eq!(settings.grid_color, [0.35, 0.35, 0.40]);
- assert_eq!(settings.uniform_background, false);
- assert_eq!(settings.network_opacity, 0.95);
assert_eq!(settings.cell_color, [0.13, 0.13, 0.16]);
assert_eq!(settings.gap_color, [0.07, 0.07, 0.09]);
@@ -577,8 +778,6 @@ mod tests {
assert_eq!(settings_roundtrip.show_camera_pivot_enabled, false);
assert_eq!(settings_roundtrip.camera_pivot_size, 1.0);
assert_eq!(settings_roundtrip.grid_color, [0.35, 0.35, 0.40]);
- assert_eq!(settings_roundtrip.uniform_background, false);
- assert_eq!(settings_roundtrip.network_opacity, 0.95);
assert_eq!(settings_roundtrip.cell_color, [0.13, 0.13, 0.16]);
assert_eq!(settings_roundtrip.gap_color, [0.07, 0.07, 0.09]);
}
diff --git a/src/project.rs b/src/project.rs
index 59a7cd2..8a56a9f 100644
--- a/src/project.rs
+++ b/src/project.rs
@@ -71,6 +71,7 @@ impl State {
self.recent_files.truncate(10);
Self::save_recent_files(&self.recent_files);
self.rebuild_recent_buttons();
+ self.ensure_menubar_subnets();
self.update_paginator();
}
@@ -162,18 +163,9 @@ impl State {
self.sync_grid_settings();
self.sync_nodes();
-
- let params = if !self.is_detached_network {
- self.graph().selected_node().and_then(|sel_idx| {
- let dir = self.current_dir();
- if sel_idx < dir.children.len() {
- Some(param_display(&dir.children[sel_idx].params))
- } else { None }
- }).unwrap_or_default()
- } else {
- vec![]
- };
- self.param_mut().set_display_params(¶ms);
+ self.sync_cursor_and_selection_from_loaded();
+ self.sync_cursor_and_selection();
+ self.sync_parameters_pane();
self.rebuild_scene_geometry();
self.rebuild_positions();
@@ -225,19 +217,10 @@ impl State {
self.sync_grid_settings();
self.sync_nodes();
-
- // Sync Parameters pane with selected node
- let params = if !self.is_detached_network {
- self.graph().selected_node().and_then(|sel_idx| {
- let dir = self.current_dir();
- if sel_idx < dir.children.len() {
- Some(param_display(&dir.children[sel_idx].params))
- } else { None }
- }).unwrap_or_default()
- } else {
- vec![]
- };
- self.param_mut().set_display_params(¶ms);
+ self.sync_cursor_and_selection_from_loaded();
+ self.sync_cursor_and_selection();
+ self.add_recent_file(project_dir.clone());
+ self.sync_parameters_pane();
self.rebuild_scene_geometry();
self.rebuild_positions();
@@ -260,6 +243,8 @@ impl State {
params: vec![],
geometry_visible: true,
position: (0.0, 0.0),
+ inputs: 0,
+ outputs: 0,
};
self.ensure_menubar_subnets();
self.apply_settings_from_menubar_subnets();
@@ -283,6 +268,8 @@ impl State {
self.sync_grid_settings();
self.sync_nodes();
+ self.sync_cursor_and_selection();
+ self.sync_parameters_pane();
self.rebuild_scene_geometry();
self.rebuild_positions();
self.apply_layout();
@@ -302,9 +289,16 @@ impl State {
camera_options.extend(camera_nodes);
let camera_options_refs: Vec<&str> = camera_options.iter().map(|s| s.as_str()).collect();
- fn find_or_create_subnet<'a>(parent: &'a mut FsNode, name: &str, pos: (f32, f32)) -> &'a mut FsNode {
+ let mut recent_options = vec!["- Select -".to_string()];
+ for path in &self.recent_files {
+ recent_options.push(path.to_string_lossy().to_string());
+ }
+ let recent_options_refs: Vec<&str> = recent_options.iter().map(|s| s.as_str()).collect();
+
+ fn find_or_create_subnet<'a>(parent: &'a mut FsNode, name: &str, node_type: &str, pos: (f32, f32)) -> &'a mut FsNode {
if let Some(idx) = parent.children.iter().position(|c| c.name == name) {
let node = &mut parent.children[idx];
+ node.node_type = node_type.to_string();
if node.position == (0.0, 0.0) {
node.position = pos;
}
@@ -313,11 +307,13 @@ impl State {
let new_node = FsNode {
id: crate::app::generate_node_id(),
name: name.to_string(),
- node_type: "node".to_string(),
+ node_type: node_type.to_string(),
children: vec![],
params: vec![],
geometry_visible: true,
position: pos,
+ inputs: 1,
+ outputs: 1,
};
parent.children.push(new_node);
parent.children.last_mut().unwrap()
@@ -340,216 +336,172 @@ impl State {
}
// 1. Main subnet
- let main_node = find_or_create_subnet(&mut self.fs_root, "Main", (0.0, 0.0));
-
- let file_node = find_or_create_subnet(main_node, "File", (0.0, 0.0));
- ensure_param(file_node, "New Project", "button", "", &[], None, None, None);
- ensure_param(file_node, "Open", "button", "", &[], None, None, None);
- ensure_param(file_node, "Save", "button", "", &[], None, None, None);
- ensure_param(file_node, "Save As", "button", "", &[], None, None, None);
- ensure_param(file_node, "Exit", "button", "", &[], None, None, None);
-
- let edit_node = find_or_create_subnet(main_node, "Edit", (2.0, 0.0));
- ensure_param(edit_node, "Undo", "button", "", &[], None, None, None);
- ensure_param(edit_node, "Redo", "button", "", &[], None, None, None);
-
- let view_node = find_or_create_subnet(main_node, "View", (4.0, 0.0));
- ensure_param(view_node, "Zoom In", "button", "", &[], None, None, None);
- ensure_param(view_node, "Zoom Out", "button", "", &[], None, None, None);
- ensure_param(view_node, "Reset Zoom", "button", "", &[], None, None, None);
- ensure_param(view_node, "Detach Circular Window", "button", "", &[], None, None, None);
- ensure_param(view_node, "Show Network Pane", "button", "", &[], None, None, None);
- ensure_param(view_node, "Show Viewport Pane", "button", "", &[], None, None, None);
- ensure_param(view_node, "Show Parameters Pane", "button", "", &[], None, None, None);
- ensure_param(view_node, "Show Spreadsheet Pane", "button", "", &[], None, None, None);
-
- let help_node = find_or_create_subnet(main_node, "Help", (6.0, 0.0));
- ensure_param(help_node, "About", "button", "", &[], None, None, None);
+ let main_node = find_or_create_subnet(&mut self.fs_root, "Main", "utility", (0.0, 0.0));
+ main_node.children.clear();
+
+ ensure_param(main_node, "File", "section", "", &[], None, None, None);
+ ensure_param(main_node, "New Project", "button", "", &[], None, None, None);
+ ensure_param(main_node, "Open", "button", "", &[], None, None, None);
+
+ if let Some(p) = main_node.params.iter_mut().find(|p| p.name == "Open Recent") {
+ p.options = recent_options.clone();
+ if !p.options.contains(&p.default) {
+ p.default = "- Select -".to_string();
+ }
+ } else {
+ ensure_param(main_node, "Open Recent", "choice", "- Select -", &recent_options_refs, None, None, None);
+ }
+
+ ensure_param(main_node, "Save", "button", "", &[], None, None, None);
+ ensure_param(main_node, "Save As", "button", "", &[], None, None, None);
+ ensure_param(main_node, "Exit", "button", "", &[], None, None, None);
+
+ ensure_param(main_node, "Edit", "section", "", &[], None, None, None);
+ ensure_param(main_node, "Undo", "button", "", &[], None, None, None);
+ ensure_param(main_node, "Redo", "button", "", &[], None, None, None);
+
+ ensure_param(main_node, "View", "section", "", &[], None, None, None);
+ ensure_param(main_node, "Zoom In", "button", "", &[], None, None, None);
+ ensure_param(main_node, "Zoom Out", "button", "", &[], None, None, None);
+ ensure_param(main_node, "Reset Zoom", "button", "", &[], None, None, None);
+ ensure_param(main_node, "Detach Circular Window", "button", "", &[], None, None, None);
+ ensure_param(main_node, "Show Network Pane", "button", "", &[], None, None, None);
+ ensure_param(main_node, "Show Viewport Pane", "button", "", &[], None, None, None);
+ ensure_param(main_node, "Show Parameters Pane", "button", "", &[], None, None, None);
+ ensure_param(main_node, "Show Spreadsheet Pane", "button", "", &[], None, None, None);
+
+ ensure_param(main_node, "Help", "section", "", &[], None, None, None);
+ ensure_param(main_node, "About", "button", "", &[], None, None, None);
// 2. Network subnet
- let net_node = find_or_create_subnet(&mut self.fs_root, "Network", (2.0, 0.0));
-
- let net_file_node = find_or_create_subnet(net_node, "File", (0.0, 0.0));
- ensure_param(net_file_node, "New", "button", "", &[], None, None, None);
- ensure_param(net_file_node, "Open", "button", "", &[], None, None, None);
- ensure_param(net_file_node, "Save", "button", "", &[], None, None, None);
- ensure_param(net_file_node, "Save As", "button", "", &[], None, None, None);
-
- let net_edit_node = find_or_create_subnet(net_node, "Edit", (2.0, 0.0));
- ensure_param(net_edit_node, "Undo", "button", "", &[], None, None, None);
- ensure_param(net_edit_node, "Redo", "button", "", &[], None, None, None);
-
- let net_view_node = find_or_create_subnet(net_node, "View", (4.0, 0.0));
- ensure_param(net_view_node, "Zoom In", "button", "", &[], None, None, None);
- ensure_param(net_view_node, "Zoom Out", "button", "", &[], None, None, None);
- ensure_param(net_view_node, "Circular Pane", "choice", if self.circular_network_pane { "true" } else { "false" }, &["false", "true"], None, None, None);
- ensure_param(net_view_node, "Detach Pane", "button", "", &[], None, None, None);
- ensure_param(net_view_node, "Close Pane", "button", "", &[], None, None, None);
-
- let net_settings_node = find_or_create_subnet(net_node, "Settings", (6.0, 0.0));
- ensure_param(net_settings_node, "Snap to Grid", "choice", if self.grid_snap_enabled { "true" } else { "false" }, &["false", "true"], None, None, None);
- ensure_param(net_settings_node, "Grid Visible", "choice", if self.network_grid_visible { "true" } else { "false" }, &["false", "true"], None, None, None);
- ensure_param(net_settings_node, "Uniform Background", "choice", if self.uniform_background { "true" } else { "false" }, &["false", "true"], None, None, None);
- ensure_param(net_settings_node, "Opacity", "slider", &format!("{:.2}", self.network_opacity), &[], Some(0.0), Some(1.0), None);
- ensure_param(net_settings_node, "Node Color R", "spinbox", &((self.node_color[0] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
- ensure_param(net_settings_node, "Node Color G", "spinbox", &((self.node_color[1] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
- ensure_param(net_settings_node, "Node Color B", "spinbox", &((self.node_color[2] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
- ensure_param(net_settings_node, "Cell Color R", "spinbox", &((self.cell_color[0] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
- ensure_param(net_settings_node, "Cell Color G", "spinbox", &((self.cell_color[1] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
- ensure_param(net_settings_node, "Cell Color B", "spinbox", &((self.cell_color[2] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
- ensure_param(net_settings_node, "Gap Color R", "spinbox", &((self.gap_color[0] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
- ensure_param(net_settings_node, "Gap Color G", "spinbox", &((self.gap_color[1] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
- ensure_param(net_settings_node, "Gap Color B", "spinbox", &((self.gap_color[2] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
+ let net_node = find_or_create_subnet(&mut self.fs_root, "Network", "utility", (2.0, 0.0));
+ net_node.children.clear();
+
+ ensure_param(net_node, "File", "section", "", &[], None, None, None);
+ ensure_param(net_node, "New", "button", "", &[], None, None, None);
+ ensure_param(net_node, "Open", "button", "", &[], None, None, None);
+ ensure_param(net_node, "Save", "button", "", &[], None, None, None);
+ ensure_param(net_node, "Save As", "button", "", &[], None, None, None);
+
+ ensure_param(net_node, "Edit", "section", "", &[], None, None, None);
+ ensure_param(net_node, "Undo", "button", "", &[], None, None, None);
+ ensure_param(net_node, "Redo", "button", "", &[], None, None, None);
+
+ ensure_param(net_node, "View", "section", "", &[], None, None, None);
+ ensure_param(net_node, "Zoom In", "button", "", &[], None, None, None);
+ ensure_param(net_node, "Zoom Out", "button", "", &[], None, None, None);
+ ensure_param(net_node, "Circular Pane", "choice", if self.circular_network_pane { "true" } else { "false" }, &["false", "true"], None, None, None);
+ ensure_param(net_node, "Detach Pane", "button", "", &[], None, None, None);
+ ensure_param(net_node, "Close Pane", "button", "", &[], None, None, None);
+
+ ensure_param(net_node, "Settings", "section", "", &[], None, None, None);
+ ensure_param(net_node, "Node Color R", "spinbox", &((self.node_color[0] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
+ ensure_param(net_node, "Node Color G", "spinbox", &((self.node_color[1] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
+ ensure_param(net_node, "Node Color B", "spinbox", &((self.node_color[2] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
// 3. Viewport subnet
- let vp_node = find_or_create_subnet(&mut self.fs_root, "Viewport", (4.0, 0.0));
+ let vp_node = find_or_create_subnet(&mut self.fs_root, "Viewport", "utility", (4.0, 0.0));
+ vp_node.children.clear();
- // Camera node - dynamically build options
- let camera_node = find_or_create_subnet(vp_node, "Camera", (0.0, 0.0));
- if let Some(p) = camera_node.params.iter_mut().find(|p| p.name == "Active Camera") {
+ ensure_param(vp_node, "Camera", "section", "", &[], None, None, None);
+ if let Some(p) = vp_node.params.iter_mut().find(|p| p.name == "Active Camera") {
p.options = camera_options.clone();
if !p.options.contains(&p.default) {
p.default = "Default Camera".to_string();
}
} else {
- ensure_param(camera_node, "Active Camera", "choice", &self.active_camera, &camera_options_refs, None, None, None);
+ ensure_param(vp_node, "Active Camera", "choice", &self.active_camera, &camera_options_refs, None, None, None);
}
- let display_node = find_or_create_subnet(vp_node, "Display", (2.0, 0.0));
- ensure_param(display_node, "Square Aspect", "choice", if self.square_viewport { "true" } else { "false" }, &["false", "true"], None, None, None);
-
- let guides_node = find_or_create_subnet(vp_node, "Guides", (4.0, 0.0));
- ensure_param(guides_node, "Show Grid", "choice", if self.show_grid { "true" } else { "false" }, &["false", "true"], None, None, None);
- ensure_param(guides_node, "Cube", "choice", if self.show_cube { "true" } else { "false" }, &["false", "true"], None, None, None);
- ensure_param(guides_node, "Origin", "choice", if self.show_origin { "true" } else { "false" }, &["false", "true"], None, None, None);
- ensure_param(guides_node, "Camera Pivot", "choice", if self.show_camera_pivot { "true" } else { "false" }, &["false", "true"], None, None, None);
-
- let vp_view_node = find_or_create_subnet(vp_node, "View", (6.0, 0.0));
- ensure_param(vp_view_node, "Close Pane", "button", "", &[], None, None, None);
-
- let vp_settings_node = find_or_create_subnet(vp_node, "Settings", (8.0, 0.0));
- ensure_param(vp_settings_node, "Show Grid Guide", "choice", if self.show_grid { "true" } else { "false" }, &["false", "true"], None, None, None);
- ensure_param(vp_settings_node, "Show Reference Cube", "choice", if self.show_cube { "true" } else { "false" }, &["false", "true"], None, None, None);
- ensure_param(vp_settings_node, "Show Origin Axes", "choice", if self.show_origin { "true" } else { "false" }, &["false", "true"], None, None, None);
- ensure_param(vp_settings_node, "Show Camera Pivot", "choice", if self.show_camera_pivot { "true" } else { "false" }, &["false", "true"], None, None, None);
- ensure_param(vp_settings_node, "Grid Thickness", "spinbox", &((self.grid_thickness * 1000.0) as i32).to_string(), &[], Some(2.0), Some(200.0), Some(1.0));
- ensure_param(vp_settings_node, "Origin Guide Size", "spinbox", &((self.origin_size * 10.0) as i32).to_string(), &[], Some(1.0), Some(50.0), Some(1.0));
- ensure_param(vp_settings_node, "Camera Pivot Size", "spinbox", &((self.camera_pivot_size * 10.0) as i32).to_string(), &[], Some(1.0), Some(50.0), Some(1.0));
- ensure_param(vp_settings_node, "BG Color R", "spinbox", &((self.viewport_bg_color[0] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
- ensure_param(vp_settings_node, "BG Color G", "spinbox", &((self.viewport_bg_color[1] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
- ensure_param(vp_settings_node, "BG Color B", "spinbox", &((self.viewport_bg_color[2] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
- ensure_param(vp_settings_node, "Grid Color R", "spinbox", &((self.grid_color[0] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
- ensure_param(vp_settings_node, "Grid Color G", "spinbox", &((self.grid_color[1] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
- ensure_param(vp_settings_node, "Grid Color B", "spinbox", &((self.grid_color[2] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
+ ensure_param(vp_node, "Display", "section", "", &[], None, None, None);
+ ensure_param(vp_node, "Square Aspect", "choice", if self.square_viewport { "true" } else { "false" }, &["false", "true"], None, None, None);
+
+ ensure_param(vp_node, "Guides", "section", "", &[], None, None, None);
+ ensure_param(vp_node, "Show Grid", "choice", if self.show_grid { "true" } else { "false" }, &["false", "true"], None, None, None);
+ ensure_param(vp_node, "Cube", "choice", if self.show_cube { "true" } else { "false" }, &["false", "true"], None, None, None);
+ ensure_param(vp_node, "Origin", "choice", if self.show_origin { "true" } else { "false" }, &["false", "true"], None, None, None);
+ ensure_param(vp_node, "Camera Pivot", "choice", if self.show_camera_pivot { "true" } else { "false" }, &["false", "true"], None, None, None);
+
+ ensure_param(vp_node, "View", "section", "", &[], None, None, None);
+ ensure_param(vp_node, "Close Pane", "button", "", &[], None, None, None);
+
+ ensure_param(vp_node, "Settings", "section", "", &[], None, None, None);
+ ensure_param(vp_node, "Show Grid Guide", "choice", if self.show_grid { "true" } else { "false" }, &["false", "true"], None, None, None);
+ ensure_param(vp_node, "Show Reference Cube", "choice", if self.show_cube { "true" } else { "false" }, &["false", "true"], None, None, None);
+ ensure_param(vp_node, "Show Origin Axes", "choice", if self.show_origin { "true" } else { "false" }, &["false", "true"], None, None, None);
+ ensure_param(vp_node, "Show Camera Pivot", "choice", if self.show_camera_pivot { "true" } else { "false" }, &["false", "true"], None, None, None);
+ ensure_param(vp_node, "Grid Thickness", "spinbox", &((self.grid_thickness * 1000.0) as i32).to_string(), &[], Some(2.0), Some(200.0), Some(1.0));
+ ensure_param(vp_node, "Origin Guide Size", "spinbox", &((self.origin_size * 10.0) as i32).to_string(), &[], Some(1.0), Some(50.0), Some(1.0));
+ ensure_param(vp_node, "Camera Pivot Size", "spinbox", &((self.camera_pivot_size * 10.0) as i32).to_string(), &[], Some(1.0), Some(50.0), Some(1.0));
+ ensure_param(vp_node, "BG Color R", "spinbox", &((self.viewport_bg_color[0] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
+ ensure_param(vp_node, "BG Color G", "spinbox", &((self.viewport_bg_color[1] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
+ ensure_param(vp_node, "BG Color B", "spinbox", &((self.viewport_bg_color[2] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
+ ensure_param(vp_node, "Grid Color R", "spinbox", &((self.grid_color[0] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
+ ensure_param(vp_node, "Grid Color G", "spinbox", &((self.grid_color[1] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
+ ensure_param(vp_node, "Grid Color B", "spinbox", &((self.grid_color[2] * 255.0) as i32).to_string(), &[], Some(0.0), Some(255.0), Some(1.0));
// 4. Parameters subnet
- let param_node = find_or_create_subnet(&mut self.fs_root, "Parameters", (6.0, 0.0));
+ let param_node = find_or_create_subnet(&mut self.fs_root, "Parameters", "utility", (6.0, 0.0));
+ param_node.children.clear();
- let preset_node = find_or_create_subnet(param_node, "Preset", (0.0, 0.0));
- ensure_param(preset_node, "Default", "button", "", &[], None, None, None);
- ensure_param(preset_node, "Custom", "button", "", &[], None, None, None);
+ ensure_param(param_node, "Preset", "section", "", &[], None, None, None);
+ ensure_param(param_node, "Default", "button", "", &[], None, None, None);
+ ensure_param(param_node, "Custom", "button", "", &[], None, None, None);
- let reset_node = find_or_create_subnet(param_node, "Reset", (2.0, 0.0));
- ensure_param(reset_node, "All", "button", "", &[], None, None, None);
+ ensure_param(param_node, "Reset", "section", "", &[], None, None, None);
+ ensure_param(param_node, "All", "button", "", &[], None, None, None);
- let param_view_node = find_or_create_subnet(param_node, "View", (4.0, 0.0));
- ensure_param(param_view_node, "Close Pane", "button", "", &[], None, None, None);
+ ensure_param(param_node, "View", "section", "", &[], None, None, None);
+ ensure_param(param_node, "Close Pane", "button", "", &[], None, None, None);
// 5. Spreadsheet subnet
- let ss_node = find_or_create_subnet(&mut self.fs_root, "Spreadsheet", (8.0, 0.0));
+ let ss_node = find_or_create_subnet(&mut self.fs_root, "Spreadsheet", "utility", (8.0, 0.0));
+ ss_node.children.clear();
- let ss_view_node = find_or_create_subnet(ss_node, "View", (0.0, 0.0));
- ensure_param(ss_view_node, "Close Pane", "button", "", &[], None, None, None);
+ ensure_param(ss_node, "View", "section", "", &[], None, None, None);
+ ensure_param(ss_node, "Close Pane", "button", "", &[], None, None, None);
}
pub(crate) fn apply_settings_from_menubar_subnets(&mut self) {
// Read Settings from Network subnet
if let Some(net_idx) = self.fs_root.children.iter().position(|c| c.name == "Network") {
- let subnet = &self.fs_root.children[net_idx];
- if let Some(settings_idx) = subnet.children.iter().position(|c| c.name == "Settings") {
- let node = &subnet.children[settings_idx];
- for p in &node.params {
- match p.name.as_str() {
- "Snap to Grid" => if let Ok(val) = p.default.parse::<bool>() { self.grid_snap_enabled = val; }
- "Grid Visible" => if let Ok(val) = p.default.parse::<bool>() { self.network_grid_visible = val; }
- "Uniform Background" => if let Ok(val) = p.default.parse::<bool>() { self.uniform_background = val; }
- "Opacity" => if let Ok(val) = p.default.parse::<f32>() { self.network_opacity = val; }
- "Node Color R" => if let Ok(val) = p.default.parse::<f32>() { self.node_color[0] = val / 255.0; }
- "Node Color G" => if let Ok(val) = p.default.parse::<f32>() { self.node_color[1] = val / 255.0; }
- "Node Color B" => if let Ok(val) = p.default.parse::<f32>() { self.node_color[2] = val / 255.0; }
- "Cell Color R" => if let Ok(val) = p.default.parse::<f32>() { self.cell_color[0] = val / 255.0; }
- "Cell Color G" => if let Ok(val) = p.default.parse::<f32>() { self.cell_color[1] = val / 255.0; }
- "Cell Color B" => if let Ok(val) = p.default.parse::<f32>() { self.cell_color[2] = val / 255.0; }
- "Gap Color R" => if let Ok(val) = p.default.parse::<f32>() { self.gap_color[0] = val / 255.0; }
- "Gap Color G" => if let Ok(val) = p.default.parse::<f32>() { self.gap_color[1] = val / 255.0; }
- "Gap Color B" => if let Ok(val) = p.default.parse::<f32>() { self.gap_color[2] = val / 255.0; }
- _ => {}
- }
- }
- }
- if let Some(view_idx) = subnet.children.iter().position(|c| c.name == "View") {
- let node = &subnet.children[view_idx];
- for p in &node.params {
- match p.name.as_str() {
- "Circular Pane" => if let Ok(val) = p.default.parse::<bool>() { self.circular_network_pane = val; }
- _ => {}
- }
+ let node = &self.fs_root.children[net_idx];
+ for p in &node.params {
+ match p.name.as_str() {
+ "Node Color R" => if let Ok(val) = p.default.parse::<f32>() { self.node_color[0] = val / 255.0; }
+ "Node Color G" => if let Ok(val) = p.default.parse::<f32>() { self.node_color[1] = val / 255.0; }
+ "Node Color B" => if let Ok(val) = p.default.parse::<f32>() { self.node_color[2] = val / 255.0; }
+ "Circular Pane" => if let Ok(val) = p.default.parse::<bool>() { self.circular_network_pane = val; }
+ _ => {}
}
}
}
// Read Settings from Viewport subnet
if let Some(vp_idx) = self.fs_root.children.iter().position(|c| c.name == "Viewport") {
- let subnet = &self.fs_root.children[vp_idx];
- if let Some(settings_idx) = subnet.children.iter().position(|c| c.name == "Settings") {
- let node = &subnet.children[settings_idx];
- for p in &node.params {
- match p.name.as_str() {
- "Show Grid Guide" => if let Ok(val) = p.default.parse::<bool>() { self.show_grid = val; }
- "Show Reference Cube" => if let Ok(val) = p.default.parse::<bool>() { self.show_cube = val; }
- "Show Origin Axes" => if let Ok(val) = p.default.parse::<bool>() { self.show_origin = val; }
- "Show Camera Pivot" => if let Ok(val) = p.default.parse::<bool>() { self.show_camera_pivot = val; }
- "Grid Thickness" => if let Ok(val) = p.default.parse::<f32>() { self.grid_thickness = val / 1000.0; }
- "Origin Guide Size" => if let Ok(val) = p.default.parse::<f32>() { self.origin_size = val / 10.0; }
- "Camera Pivot Size" => if let Ok(val) = p.default.parse::<f32>() { self.camera_pivot_size = val / 10.0; }
- "BG Color R" => if let Ok(val) = p.default.parse::<f32>() { self.viewport_bg_color[0] = val / 255.0; }
- "BG Color G" => if let Ok(val) = p.default.parse::<f32>() { self.viewport_bg_color[1] = val / 255.0; }
- "BG Color B" => if let Ok(val) = p.default.parse::<f32>() { self.viewport_bg_color[2] = val / 255.0; }
- "Grid Color R" => if let Ok(val) = p.default.parse::<f32>() { self.grid_color[0] = val / 255.0; }
- "Grid Color G" => if let Ok(val) = p.default.parse::<f32>() { self.grid_color[1] = val / 255.0; }
- "Grid Color B" => if let Ok(val) = p.default.parse::<f32>() { self.grid_color[2] = val / 255.0; }
- _ => {}
- }
- }
- }
- if let Some(disp_idx) = subnet.children.iter().position(|c| c.name == "Display") {
- let node = &subnet.children[disp_idx];
- for p in &node.params {
- match p.name.as_str() {
- "Square Aspect" => if let Ok(val) = p.default.parse::<bool>() { self.square_viewport = val; }
- _ => {}
- }
- }
- }
- if let Some(guides_idx) = subnet.children.iter().position(|c| c.name == "Guides") {
- let node = &subnet.children[guides_idx];
- for p in &node.params {
- match p.name.as_str() {
- "Show Grid" => if let Ok(val) = p.default.parse::<bool>() { self.show_grid = val; }
- "Cube" => if let Ok(val) = p.default.parse::<bool>() { self.show_cube = val; }
- "Origin" => if let Ok(val) = p.default.parse::<bool>() { self.show_origin = val; }
- "Camera Pivot" => if let Ok(val) = p.default.parse::<bool>() { self.show_camera_pivot = val; }
- _ => {}
- }
- }
- }
- if let Some(camera_idx) = subnet.children.iter().position(|c| c.name == "Camera") {
- let node = &subnet.children[camera_idx];
- for p in &node.params {
- match p.name.as_str() {
- "Active Camera" => self.active_camera = p.default.clone(),
- _ => {}
- }
+ let node = &self.fs_root.children[vp_idx];
+ for p in &node.params {
+ match p.name.as_str() {
+ "Show Grid Guide" => if let Ok(val) = p.default.parse::<bool>() { self.show_grid = val; }
+ "Show Reference Cube" => if let Ok(val) = p.default.parse::<bool>() { self.show_cube = val; }
+ "Show Origin Axes" => if let Ok(val) = p.default.parse::<bool>() { self.show_origin = val; }
+ "Show Camera Pivot" => if let Ok(val) = p.default.parse::<bool>() { self.show_camera_pivot = val; }
+ "Grid Thickness" => if let Ok(val) = p.default.parse::<f32>() { self.grid_thickness = val / 1000.0; }
+ "Origin Guide Size" => if let Ok(val) = p.default.parse::<f32>() { self.origin_size = val / 10.0; }
+ "Camera Pivot Size" => if let Ok(val) = p.default.parse::<f32>() { self.camera_pivot_size = val / 10.0; }
+ "BG Color R" => if let Ok(val) = p.default.parse::<f32>() { self.viewport_bg_color[0] = val / 255.0; }
+ "BG Color G" => if let Ok(val) = p.default.parse::<f32>() { self.viewport_bg_color[1] = val / 255.0; }
+ "BG Color B" => if let Ok(val) = p.default.parse::<f32>() { self.viewport_bg_color[2] = val / 255.0; }
+ "Grid Color R" => if let Ok(val) = p.default.parse::<f32>() { self.grid_color[0] = val / 255.0; }
+ "Grid Color G" => if let Ok(val) = p.default.parse::<f32>() { self.grid_color[1] = val / 255.0; }
+ "Grid Color B" => if let Ok(val) = p.default.parse::<f32>() { self.grid_color[2] = val / 255.0; }
+ "Square Aspect" => if let Ok(val) = p.default.parse::<bool>() { self.square_viewport = val; }
+ "Show Grid" => if let Ok(val) = p.default.parse::<bool>() { self.show_grid = val; }
+ "Cube" => if let Ok(val) = p.default.parse::<bool>() { self.show_cube = val; }
+ "Origin" => if let Ok(val) = p.default.parse::<bool>() { self.show_origin = val; }
+ "Camera Pivot" => if let Ok(val) = p.default.parse::<bool>() { self.show_camera_pivot = val; }
+ "Active Camera" => self.active_camera = p.default.clone(),
+ _ => {}
}
}
}
diff --git a/src/render.rs b/src/render.rs
index 4bc57df..c756c2f 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -63,9 +63,7 @@ impl State {
let sh = self.height;
let node_area_y = self.positions[CONTENT_IDX].1;
- let dialog_open = self.node_palette_visible;
- let show_cursor = self.drag_widget.is_none()
- && !dialog_open;
+ let show_cursor = self.drag_widget.is_none();
let clip = if self.circular_network_pane {
(
@@ -103,7 +101,6 @@ impl State {
};
(self.has_any_open_menu(i), base_key)
});
- println!("DEBUG_DRAW_ORDER: {:?}", draw_order.iter().map(|&i| (i, self.widgets[i].visible(), self.positions[i])).collect::<Vec<_>>());
let mut visited = vec![false; self.widgets.len()];
for &i in &draw_order {
@@ -266,9 +263,27 @@ impl State {
}
// Dropdown popover
- if w.visible() && self.focused_widget == Some(idx) {
- if let Some((px, py, pw, ph)) = w.popover_rect() {
- push_widget_popover_vertices(px, py, pw, ph, sw, sh, verts);
+ if w.visible() && (self.focused_widget == Some(idx) || idx == PARAM_IDX) {
+ let mut popover_pc = clear_ui::layout::PopoverCollector::new();
+ w.render_popover(&mut popover_pc);
+ for (color, px, py, pw, ph) in popover_pc.rects {
+ let ndc_x = (px / sw) * 2.0 - 1.0;
+ let ndc_y = 1.0 - (py / sh) * 2.0;
+ let ndc_w = (pw / sw) * 2.0;
+ let ndc_h = (ph / sh) * 2.0;
+
+ let v_tl = Vertex { position: [ndc_x, ndc_y], color, clip_circle: [0.0, 0.0, 0.0] };
+ let v_tr = Vertex { position: [ndc_x + ndc_w, ndc_y], color, clip_circle: [0.0, 0.0, 0.0] };
+ let v_bl = Vertex { position: [ndc_x, ndc_y - ndc_h], color, clip_circle: [0.0, 0.0, 0.0] };
+ let v_br = Vertex { position: [ndc_x + ndc_w, ndc_y - ndc_h], color, clip_circle: [0.0, 0.0, 0.0] };
+
+ verts.push(v_tl);
+ verts.push(v_tr);
+ verts.push(v_bl);
+
+ verts.push(v_tr);
+ verts.push(v_br);
+ verts.push(v_bl);
}
}
}
@@ -299,6 +314,7 @@ impl State {
}
pub(crate) fn upload_vertices(&mut self) {
+ self.text_dirty = true;
let mut verts = std::mem::take(&mut self.vertex_data);
self.collect_vertices(&mut verts);
self.vertex_count = verts.len() as u32;
@@ -317,31 +333,24 @@ impl State {
}
pub(crate) fn rebuild_scene_geometry(&mut self) {
- let mut geom = crate::geometry::network_sphere_vertices(&self.fs_root);
+ let mut ocl_error = None;
+ let geom = network_sphere_vertices_with_errors(&self.fs_root, &mut ocl_error);
- fn collect_opencl_codes(node: &FsNode, codes: &mut Vec<String>) {
+ fn has_visible_opencl(node: &FsNode) -> bool {
if node.node_type.eq_ignore_ascii_case("opencl") && node.geometry_visible {
- let code = node.params.iter()
- .find(|p| p.name.eq_ignore_ascii_case("code"))
- .map(|p| p.default.clone())
- .unwrap_or_else(String::new);
- codes.push(code);
+ return true;
}
for child in &node.children {
- collect_opencl_codes(child, codes);
- }
- }
- let mut opencl_codes = Vec::new();
- collect_opencl_codes(&self.fs_root, &mut opencl_codes);
-
- for code in &opencl_codes {
- if !code.is_empty() {
- if let Err(e) = crate::geometry::run_opencl_kernel(code, &mut geom) {
- self.update_status_text(&format!("OpenCL Error: {}", e));
+ if has_visible_opencl(child) {
+ return true;
}
}
+ false
}
- if !opencl_codes.is_empty() {
+
+ if let Some(e) = ocl_error {
+ self.update_status_text(&format!("OpenCL Error: {}", e));
+ } else if has_visible_opencl(&self.fs_root) {
self.update_status_text("OpenCL kernel executed successfully.");
} else {
self.update_status_text("Geometry updated successfully.");
@@ -360,13 +369,54 @@ impl State {
});
}
self.wgpu_adapter.queue.write_buffer(&self.vertex_buffer_spheres, 0, data);
+ self.viewport_dirty = true;
}
pub(crate) fn update_status_text(&mut self, text: &str) {
- self.widgets[STATUS_IDX].set_text(text);
+ if self.last_status_text != text {
+ self.last_status_text = text.to_string();
+ self.widgets[STATUS_IDX].set_text(text);
+ self.text_dirty = true;
+ }
}
pub(crate) fn prepare_text(&mut self) {
+ let mut current_popovers = Vec::new();
+ {
+ fn collect_popovers(
+ w: &dyn Element,
+ popovers: &mut Vec<(f32, f32, f32, f32)>,
+ ctx: &clear_ui::context::UiContext,
+ ) {
+ if let Some(rect) = w.popover_rect() {
+ popovers.push(rect);
+ }
+ for child_ptr in w.children(ctx) {
+ unsafe {
+ if let Some(child) = child_ptr.as_ref() {
+ collect_popovers(child, popovers, ctx);
+ }
+ }
+ }
+ }
+
+ for w in &self.widgets {
+ if w.visible() {
+ collect_popovers(w.as_ref(), &mut current_popovers, &self.ui_context);
+ }
+ }
+ }
+
+ if current_popovers != self.last_popover_rects {
+ self.last_popover_rects = current_popovers;
+ self.text_dirty = true;
+ }
+
+ if !self.text_dirty {
+ return;
+ }
+ self.text_dirty = false;
+
// 1. Prepare text on all widgets using self.wgpu_adapter.font_system
for (i, w) in self.widgets.iter_mut().enumerate() {
let is_menubar = i == HEADER_IDX || i == LEFT_MENUBAR_IDX || i == RIGHT_MENUBAR_IDX || i == PARAM_MENUBAR_IDX || i == SPREADSHEET_MENUBAR_IDX;
@@ -457,10 +507,56 @@ impl State {
}
}
+ // Pass 1b: Populate text_buffer_cache with popover texts
+ for (i, w) in widgets.iter().enumerate() {
+ if !w.visible() {
+ continue;
+ }
+ let is_menubar = i == HEADER_IDX || i == LEFT_MENUBAR_IDX || i == RIGHT_MENUBAR_IDX || i == PARAM_MENUBAR_IDX || i == SPREADSHEET_MENUBAR_IDX;
+ if is_menubar {
+ continue;
+ }
+ if self.focused_widget == Some(i) || i == PARAM_IDX {
+ let mut popover_pc = clear_ui::layout::PopoverCollector::new();
+ w.render_popover(&mut popover_pc);
+ for (t, size, _x, _y, _tc, font_opt, _bounds) in popover_pc.texts {
+ let key = (t.clone(), (size * 100.0) as u32, font_opt.clone());
+ if !text_buffer_cache.contains_key(&key) {
+ let buf = make_text_buffer_with_font(font_system, &t, size, font_opt.as_deref());
+ text_buffer_cache.insert(key, buf);
+ }
+ }
+ }
+ }
+
let viewport = Resolution { width: *physical_width, height: *physical_height };
text_viewport.update(queue, viewport);
let s = *scale as f32;
+ let mut popovers = Vec::new();
+ fn collect_popovers(
+ w: &dyn Element,
+ popovers: &mut Vec<(f32, f32, f32, f32)>,
+ ctx: &clear_ui::context::UiContext,
+ ) {
+ if let Some(rect) = w.popover_rect() {
+ popovers.push(rect);
+ }
+ for child_ptr in w.children(ctx) {
+ unsafe {
+ if let Some(child) = child_ptr.as_ref() {
+ collect_popovers(child, popovers, ctx);
+ }
+ }
+ }
+ }
+
+ for w in widgets {
+ if w.visible() {
+ collect_popovers(w.as_ref(), &mut popovers, ui_context);
+ }
+ }
+
let mut areas: Vec<TextArea> = Vec::new();
// Temporary storage for legacy buffers generated during this frame
@@ -548,6 +644,21 @@ impl State {
continue;
}
}
+ // Overlap check
+ let mut overlaps = false;
+ let tw = buf.layout_runs().next().map(|r| r.line_w).unwrap_or(0.0) / s;
+ let th = (buf.layout_runs().count() as f32 * 14.0).max(14.0);
+ for &(px, py, pw, ph) in &popovers {
+ let x_overlap = x <= px + pw && (x + tw) >= px;
+ let y_overlap = y <= py + ph && (y + th) >= py;
+ if x_overlap && y_overlap {
+ overlaps = true;
+ break;
+ }
+ }
+ if overlaps {
+ continue;
+ }
areas.push(TextArea {
buffer: buf,
left: (x * s).round(),
@@ -598,6 +709,26 @@ impl State {
}
let key = (label.text.clone(), (label.font_size * 100.0) as u32, font_opt.clone());
let buf_ref = text_buffer_cache.get(&key).unwrap();
+
+ // Overlap check
+ let mut overlaps = false;
+ let tw = buf_ref.layout_runs().next().map(|r| r.line_w).unwrap_or(0.0) / s;
+ let th = label.font_size * 1.4;
+ for &(px, py, pw, ph) in &popovers {
+ let x_overlap = label.x <= px + pw && (label.x + tw) >= px;
+ let y_overlap = label.y <= py + ph && (label.y + th) >= py;
+ if x_overlap && y_overlap {
+ overlaps = true;
+ break;
+ }
+ }
+ if label.text.contains("Save") {
+ println!("DEBUG SAVE OVERLAP: i={}, label_text={:?}, label.x={}, label.y={}, tw={}, th={}, popovers={:?}, overlaps={}", i, label.text, label.x, label.y, tw, th, popovers, overlaps);
+ }
+ if overlaps {
+ continue;
+ }
+
legacy_buffers.push(buf_ref);
legacy_labels.push(label);
legacy_bounds.push(item_bounds);
@@ -619,6 +750,65 @@ impl State {
});
}
+ // Add popover text areas
+ for (i, w) in widgets.iter().enumerate() {
+ if !w.visible() {
+ continue;
+ }
+ let is_menubar = i == HEADER_IDX || i == LEFT_MENUBAR_IDX || i == RIGHT_MENUBAR_IDX || i == PARAM_MENUBAR_IDX || i == SPREADSHEET_MENUBAR_IDX;
+ if is_menubar {
+ continue;
+ }
+ if self.focused_widget == Some(i) || i == PARAM_IDX {
+ let mut popover_pc = clear_ui::layout::PopoverCollector::new();
+ w.render_popover(&mut popover_pc);
+ for (t, size, x, y, tc, font_opt, label_bounds) in popover_pc.texts {
+ let key = (t.clone(), (size * 100.0) as u32, font_opt.clone());
+ if let Some(buf_ref) = text_buffer_cache.get(&key) {
+ let mut item_bounds = TextBounds {
+ left: 0,
+ top: 0,
+ right: *physical_width as i32,
+ bottom: *physical_height as i32,
+ };
+ if let Some([l, t_bound, r, b]) = label_bounds {
+ let pl = (l * s).round() as i32;
+ let pt = (t_bound * s).round() as i32;
+ let pr = (r * s).round() as i32;
+ let pb = (b * s).round() as i32;
+ item_bounds = TextBounds {
+ left: item_bounds.left.max(pl),
+ top: item_bounds.top.max(pt),
+ right: item_bounds.right.min(pr),
+ bottom: item_bounds.bottom.min(pb),
+ };
+ }
+ areas.push(TextArea {
+ buffer: buf_ref,
+ left: (x * s).round(),
+ top: (y * s).round(),
+ scale: s,
+ bounds: item_bounds,
+ default_color: glyphon::Color::rgb(
+ (tc[0] * 255.0) as u8,
+ (tc[1] * 255.0) as u8,
+ (tc[2] * 255.0) as u8,
+ ),
+ custom_glyphs: &[],
+ });
+ }
+ }
+ }
+ }
+
+ if !popovers.is_empty() {
+ for (idx, area) in areas.iter().enumerate() {
+ for run in area.buffer.layout_runs() {
+ println!("DEBUG: Area {}, text={:?}, x={}, y={}", idx, run.text, area.left, area.top);
+ }
+ }
+ }
+
text_renderer.prepare(device, queue, font_system, text_atlas, text_viewport, areas, swash_cache).unwrap();
// Process curved labels
diff --git a/src/window.rs b/src/window.rs
index 6d57818..f9dd0e9 100644
--- a/src/window.rs
+++ b/src/window.rs
@@ -659,6 +659,9 @@ impl AppState {
xkeysym::Keysym::k | xkeysym::Keysym::K => Key::Character("k".into()),
xkeysym::Keysym::l | xkeysym::Keysym::L => Key::Character("l".into()),
xkeysym::Keysym::s | xkeysym::Keysym::S => Key::Character("s".into()),
+ xkeysym::Keysym::c | xkeysym::Keysym::C => Key::Character("c".into()),
+ xkeysym::Keysym::x | xkeysym::Keysym::X => Key::Character("x".into()),
+ xkeysym::Keysym::v | xkeysym::Keysym::V => Key::Character("v".into()),
xkeysym::Keysym::grave => Key::Character("`".into()),
_ => {
if let Some(ref text) = event.utf8 {
@@ -726,12 +729,28 @@ impl AppState {
pub fn process_event(&mut self, ev: WindowEvent) {
if let Some(state) = &mut self.state {
let mut changed = state.handle_event(&ev);
- state.sync_settings_from_paginator();
if let Some(seg) = state.path_mut().path_click() {
if seg < state.current_path.len() {
+ let exited_idx = state.current_path.get(seg).copied();
state.current_path.truncate(seg);
state.on_path_changed();
+ if let Some(idx) = exited_idx {
+ let pos = {
+ let dir = state.current_dir();
+ if idx < dir.children.len() {
+ Some(dir.children[idx].position)
+ } else {
+ None
+ }
+ };
+ if let Some((pos_x, pos_y)) = pos {
+ state.grid_cursor_col = pos_x as i32;
+ state.grid_cursor_row = pos_y as i32;
+ state.sync_cursor_and_selection();
+ state.upload_vertices();
+ }
+ }
changed = true;
}
}
@@ -1309,9 +1328,7 @@ impl AppState {
CustomEvent::PostAction(action, tx) => {
let res = match action {
HttpAction::Up => {
- if !state.current_path.is_empty() {
- state.current_path.pop();
- state.on_path_changed();
+ if state.move_up() {
needs_redraw = true;
Ok("Moved up".to_string())
} else {
@@ -1320,7 +1337,7 @@ impl AppState {
}
HttpAction::Enter { slot } => {
let dir = state.current_dir();
- if slot < dir.children.len() && (dir.children[slot].node_type == "node" || dir.children[slot].node_type == "opencl" || !dir.children[slot].children.is_empty()) {
+ if slot < dir.children.len() && (dir.children[slot].node_type == "node" || dir.children[slot].node_type == "utility" || !dir.children[slot].children.is_empty()) {
state.current_path.push(slot);
state.on_path_changed();
needs_redraw = true;
@@ -1385,12 +1402,16 @@ impl AppState {
HttpAction::ToggleGeometry { slot } => {
let active_nodes = state.current_dir().children.len();
if slot < active_nodes {
- let visible = !state.current_dir().children[slot].geometry_visible;
- state.current_dir_mut().children[slot].geometry_visible = visible;
- state.sync_nodes();
- state.rebuild_scene_geometry();
- needs_redraw = true;
- Ok(format!("Geometry visible: {}", visible))
+ if state.current_dir().children[slot].node_type == "utility" {
+ Err("Cannot toggle geometry visibility on utility nodes".to_string())
+ } else {
+ let visible = !state.current_dir().children[slot].geometry_visible;
+ state.current_dir_mut().children[slot].geometry_visible = visible;
+ state.sync_nodes();
+ state.rebuild_scene_geometry();
+ needs_redraw = true;
+ Ok(format!("Geometry visible: {}", visible))
+ }
} else {
Err("Slot out of bounds".to_string())
}
@@ -1402,43 +1423,39 @@ impl AppState {
});
if let Some(idx) = template_idx {
let mut node = state.node_templates[idx].node.clone();
- let (nx, ny) = state.find_empty_cell(x, y, None);
- node.position = (nx, ny);
- if let Some(n) = name {
- node.name = n;
+ let mut allowed = true;
+ let is_in_utility = !state.current_path.is_empty() && state.fs_root.children[state.current_path[0]].node_type == "utility";
+ if is_in_utility {
+ if crate::geometry::is_geometry_node_type(&node.node_type) {
+ allowed = false;
+ }
+ }
+ if !allowed {
+ Err("Utility nodes cannot contain geometry.".to_string())
+ } else {
+ let (nx, ny) = state.find_empty_cell(x, y, None);
+ node.position = (nx, ny);
+ if let Some(n) = name {
+ node.name = n;
+ } else {
+ node.name = state.get_lowest_unused_name(&node.name);
+ }
+ state.current_dir_mut().children.push(node);
+ state.sync_nodes();
+ state.rebuild_positions();
+ state.apply_layout();
+ state.update_panel_bounds();
+ state.rebuild_scene_geometry();
+ state.upload_vertices();
+ needs_redraw = true;
+ Ok("Node added".to_string())
}
- state.current_dir_mut().children.push(node);
- state.sync_nodes();
- state.rebuild_positions();
- state.apply_layout();
- state.update_panel_bounds();
- state.upload_vertices();
- needs_redraw = true;
- Ok("Node added".to_string())
} else {
Err(format!("Template '{}' not found", template_name))
}
}
HttpAction::DeleteNode { slot } => {
- let len = state.current_dir().children.len();
- if slot < len {
- state.current_dir_mut().children.remove(slot);
- if let Some(focused) = state.focused_widget {
- if focused == CONTENT_IDX {
- if let Some(sel_idx) = state.graph().selected_node() {
- if sel_idx == slot {
- state.graph_mut().set_selected_node(None);
- } else if sel_idx > slot {
- state.graph_mut().set_selected_node(Some(sel_idx - 1));
- }
- }
- }
- }
- state.sync_nodes();
- state.rebuild_positions();
- state.apply_layout();
- state.update_panel_bounds();
- state.upload_vertices();
+ if state.delete_node(slot) {
needs_redraw = true;
Ok("Node deleted".to_string())
} else {