graphic design tool
git clone https://git.lucas.co/cce-designer.git
Add sphere node parameter settings and slider controls
.gitignore | 1 +
nodes/node_1.json | 21 ++++
nodes/node_2.json | 7 ++
nodes/sphere.json | 7 ++
src/main.rs | 307 ++++++++++++++++++++++++++++++++++++++++++++++--------
5 files changed, 298 insertions(+), 45 deletions(-)
diff --git a/.gitignore b/.gitignore
index ea8c4bf..a1dfb85 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
/target
+.antigravitycli
diff --git a/nodes/node_1.json b/nodes/node_1.json
new file mode 100644
index 0000000..83a7ed1
--- /dev/null
+++ b/nodes/node_1.json
@@ -0,0 +1,21 @@
+{
+ "name": "Node 1",
+ "children": [
+ {
+ "name": "Child A",
+ "params": [
+ { "name": "Type", "default": "Subnet" }
+ ]
+ },
+ {
+ "name": "Child B",
+ "params": [
+ { "name": "Type", "default": "Subnet" }
+ ]
+ }
+ ],
+ "params": [
+ { "name": "IP", "label": "IP Address", "type": "string", "default": "192.168.1.1" },
+ { "name": "Port", "type": "string", "default": "8080" }
+ ]
+}
diff --git a/nodes/node_2.json b/nodes/node_2.json
new file mode 100644
index 0000000..dc66872
--- /dev/null
+++ b/nodes/node_2.json
@@ -0,0 +1,7 @@
+{
+ "name": "Node 2",
+ "params": [
+ { "name": "IP", "default": "10.0.0.1" },
+ { "name": "Port", "default": "443" }
+ ]
+}
diff --git a/nodes/sphere.json b/nodes/sphere.json
new file mode 100644
index 0000000..be69a94
--- /dev/null
+++ b/nodes/sphere.json
@@ -0,0 +1,7 @@
+{
+ "name": "Sphere 1",
+ "type": "sphere",
+ "params": [
+ { "name": "Radius", "default": "0.5", "type": "slider" }
+ ]
+}
diff --git a/src/main.rs b/src/main.rs
index bc5f065..91ad65b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -88,7 +88,7 @@ struct NodeTemplate {
node: FsNode,
}
-fn param_display(params: &[ParamDef]) -> Vec<(String, String)> {
+fn param_display(params: &[ParamDef]) -> Vec<(String, String, String)> {
params.iter().map(|p| {
let key = if p.label.is_empty() { &p.name } else { &p.label };
let value = if p.param_type == "choice" && !p.options.is_empty() && p.default.is_empty() {
@@ -96,7 +96,7 @@ fn param_display(params: &[ParamDef]) -> Vec<(String, String)> {
} else {
p.default.clone()
};
- (key.clone(), value)
+ (key.clone(), value, p.param_type.clone())
}).collect()
}
@@ -166,6 +166,8 @@ struct ConfigDialog {
network_grid_hovered: bool,
grid_size_x: f32,
grid_size_y: f32,
+ skipped_row_h: f32,
+ skipped_col_w: f32,
grid_size_pending: Option<(usize, f32)>,
hovered_spin_btn: Option<usize>,
}
@@ -185,8 +187,10 @@ impl ConfigDialog {
network_grid_enabled: true,
network_grid_pending: None,
network_grid_hovered: false,
- grid_size_x: 75.0,
+ grid_size_x: 150.0,
grid_size_y: 75.0,
+ skipped_row_h: 37.5,
+ skipped_col_w: 37.5,
grid_size_pending: None,
hovered_spin_btn: None,
}
@@ -202,14 +206,20 @@ impl ConfigDialog {
(px + pw - 28.0, py + 8.0, 20.0, 20.0)
}
- fn spin_btns(&self, px: f32, py: f32) -> [(usize, f32, f32); 4] {
+ fn spin_btns(&self, px: f32, py: f32) -> Vec<(usize, f32, f32)> {
let row0_y = py + 156.0; // Grid X
let row1_y = py + 178.0; // Grid Y
- [
+ let row2_y = py + 200.0; // Skipped Row H
+ let row3_y = py + 222.0; // Skipped Col W
+ vec![
(0, px + 160.0, row0_y - 2.0),
(1, px + 210.0, row0_y - 2.0),
(2, px + 160.0, row1_y - 2.0),
(3, px + 210.0, row1_y - 2.0),
+ (4, px + 160.0, row2_y - 2.0),
+ (5, px + 210.0, row2_y - 2.0),
+ (6, px + 160.0, row3_y - 2.0),
+ (7, px + 210.0, row3_y - 2.0),
]
}
@@ -259,6 +269,8 @@ impl Widget for ConfigDialog {
match id {
0 => self.grid_size_x = val,
1 => self.grid_size_y = val,
+ 2 => self.skipped_row_h = val,
+ 3 => self.skipped_col_w = val,
_ => {}
}
}
@@ -351,18 +363,21 @@ impl Widget for ConfigDialog {
}
for &(btn_id, bx, by) in &self.spin_btns(ppx, ppy) {
if px >= bx && px < bx + 22.0 && py >= by && py < by + 20.0 {
- let (val, delta) = match btn_id {
- 0 => (&mut self.grid_size_x, -5.0),
- 1 => (&mut self.grid_size_x, 5.0),
- 2 => (&mut self.grid_size_y, -5.0),
- 3 => (&mut self.grid_size_y, 5.0),
+ let (val, delta, min_val, max_val, pending_id) = match btn_id {
+ 0 => (&mut self.grid_size_x, -5.0, 10.0, 200.0, 0),
+ 1 => (&mut self.grid_size_x, 5.0, 10.0, 200.0, 0),
+ 2 => (&mut self.grid_size_y, -5.0, 5.0, 100.0, 1),
+ 3 => (&mut self.grid_size_y, 5.0, 5.0, 100.0, 1),
+ 4 => (&mut self.skipped_row_h, -5.0, 0.0, 150.0, 2),
+ 5 => (&mut self.skipped_row_h, 5.0, 0.0, 150.0, 2),
+ 6 => (&mut self.skipped_col_w, -5.0, 0.0, 150.0, 3),
+ 7 => (&mut self.skipped_col_w, 5.0, 0.0, 150.0, 3),
_ => unreachable!(),
};
- let new = (*val + delta).clamp(5.0, 100.0);
+ let new = (*val + delta).clamp(min_val, max_val);
if (new - *val).abs() > 0.01 {
*val = new;
- let id = if btn_id < 2 { 0 } else { 1 };
- self.grid_size_pending = Some((id, *val));
+ self.grid_size_pending = Some((pending_id, *val));
}
return true;
}
@@ -478,6 +493,18 @@ impl Widget for ConfigDialog {
labels.push(TextLabel { text: "\u{2212}".into(), x: px + 170.0, y: content_y + 122.0, font_size: 12.0, color: [0xcc, 0xcc, 0xd4] });
labels.push(TextLabel { text: format!("{}", gy), x: px + 188.0, y: content_y + 122.0, font_size: 12.0, color: [0xdd, 0xdd, 0x88] });
labels.push(TextLabel { text: "+".into(), x: px + 220.0, y: content_y + 122.0, font_size: 12.0, color: [0xcc, 0xcc, 0xd4] });
+
+ let srh = self.skipped_row_h as i32;
+ labels.push(TextLabel { text: "Skipped Row H:".into(), x: px + 16.0, y: content_y + 144.0, font_size: 12.0, color: [0xbb, 0xbb, 0xcc] });
+ labels.push(TextLabel { text: "\u{2212}".into(), x: px + 170.0, y: content_y + 144.0, font_size: 12.0, color: [0xcc, 0xcc, 0xd4] });
+ labels.push(TextLabel { text: format!("{}", srh), x: px + 188.0, y: content_y + 144.0, font_size: 12.0, color: [0xdd, 0xdd, 0x88] });
+ labels.push(TextLabel { text: "+".into(), x: px + 220.0, y: content_y + 144.0, font_size: 12.0, color: [0xcc, 0xcc, 0xd4] });
+
+ let scw = self.skipped_col_w as i32;
+ labels.push(TextLabel { text: "Skipped Col W:".into(), x: px + 16.0, y: content_y + 166.0, font_size: 12.0, color: [0xbb, 0xbb, 0xcc] });
+ labels.push(TextLabel { text: "\u{2212}".into(), x: px + 170.0, y: content_y + 166.0, font_size: 12.0, color: [0xcc, 0xcc, 0xd4] });
+ labels.push(TextLabel { text: format!("{}", scw), x: px + 188.0, y: content_y + 166.0, font_size: 12.0, color: [0xdd, 0xdd, 0x88] });
+ labels.push(TextLabel { text: "+".into(), x: px + 220.0, y: content_y + 166.0, font_size: 12.0, color: [0xcc, 0xcc, 0xd4] });
} else {
labels.push(TextLabel { text: "Keyboard Shortcuts".into(), x: px + 16.0, y: content_y, font_size: 13.0, color: [0xcc, 0xcc, 0xd4] });
let shortcuts = [
@@ -916,6 +943,8 @@ struct State {
network_grid_visible: bool,
grid_size_x: f32,
grid_size_y: f32,
+ skipped_row_h: f32,
+ skipped_col_w: f32,
pan_x: f32,
pan_y: f32,
@@ -1088,7 +1117,7 @@ impl State {
dir.children.get(i).map(|c| c.name.clone())
}).collect()
};
- let params_list: Vec<Vec<(String, String)>> = {
+ let params_list: Vec<Vec<(String, String, String)>> = {
let dir = self.current_dir();
self.node_slots.iter().enumerate().map(|(i, _)| {
dir.children.get(i).map(|c| param_display(&c.params)).unwrap_or_default()
@@ -1358,10 +1387,9 @@ impl State {
let fs_root = load_fs_tree();
let node_templates = flatten_node_templates(&fs_root);
let node_slots: Vec<usize> = (NODE_SLOT_START..NODE_SLOT_START + NODE_SLOT_COUNT).collect();
- let mut left_offsets: Vec<(f32, f32)> = (0..NODE_SLOT_COUNT)
- .map(|i| (0.0, i as f32 * 2.0))
+ let left_offsets: Vec<(f32, f32)> = (0..NODE_SLOT_COUNT)
+ .map(|i| (0.0, i as f32))
.collect();
- if left_offsets.len() > 1 { left_offsets[1] = (0.0, 3.0); }
let mut widgets: Vec<Box<dyn Widget>> = vec![
Box::new(MenuBar::new(0.0, 0.0, 0.0, HEADER_H).with_title("Clear Designer").with_item("File", &["New Project", "Open", "Save", "Configure", "Exit"]).with_item("Edit", &["Undo", "Redo"]).with_item("View", &["Zoom In", "Zoom Out", "Reset Zoom"]).with_item("Help", &["About"])),
@@ -1454,8 +1482,10 @@ impl State {
square_viewport: false,
grid_snap_enabled: true,
network_grid_visible: true,
- grid_size_x: 75.0,
+ grid_size_x: 150.0,
grid_size_y: 75.0,
+ skipped_row_h: 37.5,
+ skipped_col_w: 37.5,
pan_x: 0.0,
pan_y: 0.0,
@@ -1484,48 +1514,67 @@ impl State {
let node_area_y = HEADER_H + MENUBAR_H + BREADCRUMB_H;
self.widgets[CONTENT_IDX].set_show_network_grid(self.network_grid_visible);
self.widgets[CONTENT_IDX].set_grid_sizes(self.grid_size_x, self.grid_size_y);
+ self.widgets[CONTENT_IDX].set_skipped_sizes(self.skipped_row_h, self.skipped_col_w);
self.widgets[CONTENT_IDX].set_grid_origin(self.pan_x, node_area_y + self.pan_y);
for &i in &self.node_slots {
- let gx = if self.grid_snap_enabled { self.grid_size_x } else { 0.0 };
- let gy = if self.grid_snap_enabled { self.grid_size_y } else { 0.0 };
+ let gx = if self.grid_snap_enabled { self.grid_size_x + self.skipped_col_w } else { 0.0 };
+ let gy = if self.grid_snap_enabled { self.grid_size_y + self.skipped_row_h } else { 0.0 };
self.widgets[i].set_grid_snap(gx, gy);
self.widgets[i].set_grid_origin(self.pan_x, node_area_y + self.pan_y);
}
}
- fn zoom(&mut self, factor: f32) {
+ fn zoom(&mut self, factor: f32, center: Option<(f32, f32)>) {
let old_gx = self.grid_size_x;
let old_gy = self.grid_size_y;
- let new_gx = (old_gx * factor).clamp(15.0, 250.0);
+ let new_gx = (old_gx * factor).clamp(30.0, 500.0);
let new_gy = (old_gy * factor).clamp(15.0, 250.0);
if (new_gx - old_gx).abs() < 0.01 {
return;
}
- let col = self.grid_cursor_col as f32;
- let row = self.grid_cursor_row as f32;
+ let old_row_h = self.skipped_row_h;
+ let old_col_w = self.skipped_col_w;
+
+ let node_area_y = HEADER_H + MENUBAR_H + BREADCRUMB_H;
+ let (cx, cy) = match center {
+ Some(pt) => pt,
+ None => {
+ let col = self.grid_cursor_col as f32;
+ let row = self.grid_cursor_row as f32;
+ (
+ col * (old_gx + old_col_w) + 0.5 * old_gx + self.pan_x,
+ row * (old_gy + old_row_h) + 0.5 * old_gy + self.pan_y + node_area_y,
+ )
+ }
+ };
+
+ let col_f = (cx - self.pan_x) / old_gx;
+ let row_f = (cy - self.pan_y - node_area_y) / old_gy;
- self.pan_x += (col + 1.0) * (old_gx - new_gx);
- self.pan_y += (row + 0.5) * (old_gy - new_gy);
+ self.pan_x = cx - col_f * new_gx;
+ self.pan_y = cy - row_f * new_gy - node_area_y;
self.grid_size_x = new_gx;
self.grid_size_y = new_gy;
+ self.skipped_row_h = old_row_h * (new_gy / old_gy);
+ self.skipped_col_w = old_col_w * (new_gx / old_gx);
self.sync_grid_settings();
for &i in &self.node_slots {
let (x, y, _, _) = self.widgets[i].rect();
- self.widgets[i].set_rect(x, y, self.grid_size_x * 2.0, self.grid_size_y);
+ self.widgets[i].set_rect(x, y, self.grid_size_x, self.grid_size_y);
}
}
fn keep_cursor_in_view(&mut self) {
let node_area_y = HEADER_H + MENUBAR_H + BREADCRUMB_H;
- let cx = self.grid_cursor_col as f32 * self.grid_size_x + self.pan_x;
- let cy = node_area_y + self.grid_cursor_row as f32 * self.grid_size_y + self.pan_y;
- let cw = self.grid_size_x * 2.0;
+ let cx = self.grid_cursor_col as f32 * (self.grid_size_x + self.skipped_col_w) + self.pan_x;
+ let cy = node_area_y + self.grid_cursor_row as f32 * (self.grid_size_y + self.skipped_row_h) + self.pan_y;
+ let cw = self.grid_size_x;
let ch = self.grid_size_y;
let clw = self.content_left_w();
let max_y = self.height - STATUS_H;
@@ -1571,7 +1620,12 @@ impl State {
let active_nodes = self.current_dir().children.len().min(self.node_slots.len());
for (slot_idx, (&idx, off)) in self.node_slots.iter().zip(self.left_offsets.iter()).enumerate() {
if slot_idx < active_nodes {
- self.positions[idx] = (off.0 * self.grid_size_x + self.pan_x, node_area_y + BREADCRUMB_H + off.1 * self.grid_size_y + self.pan_y, self.grid_size_x * 2.0, self.grid_size_y);
+ self.positions[idx] = (
+ off.0 * (self.grid_size_x + self.skipped_col_w) + self.pan_x,
+ node_area_y + BREADCRUMB_H + off.1 * (self.grid_size_y + self.skipped_row_h) + self.pan_y,
+ self.grid_size_x,
+ self.grid_size_y,
+ );
} else {
self.positions[idx] = (0.0, 0.0, 0.0, 0.0);
}
@@ -1617,6 +1671,8 @@ impl State {
self.widgets[CONFIG_DIALOG_IDX].set_config_toggle(1, self.network_grid_visible);
self.widgets[CONFIG_DIALOG_IDX].set_config_spin(0, self.grid_size_x);
self.widgets[CONFIG_DIALOG_IDX].set_config_spin(1, self.grid_size_y);
+ self.widgets[CONFIG_DIALOG_IDX].set_config_spin(2, self.skipped_row_h);
+ self.widgets[CONFIG_DIALOG_IDX].set_config_spin(3, self.skipped_col_w);
self.widgets[CONFIG_DIALOG_IDX].set_visible(true);
}
self.upload_vertices();
@@ -1631,9 +1687,43 @@ impl State {
let active_nodes = self.current_dir().children.len().min(self.node_slots.len());
for (&idx, off) in self.node_slots.iter().take(active_nodes).zip(self.left_offsets.iter_mut()) {
let (px, py, _, _) = self.widgets[idx].rect();
- let c = ((px - self.pan_x) / self.grid_size_x).round();
- let r = (((py - node_area_y) - self.pan_y) / self.grid_size_y).round();
+ let c = ((px - self.pan_x) / (self.grid_size_x + self.skipped_col_w)).round();
+ let r = ((py - node_area_y - self.pan_y) / (self.grid_size_y + self.skipped_row_h)).round();
*off = (c, r);
+ if Some(idx) == self.focused_widget && self.drag_widget == Some(idx) {
+ self.grid_cursor_col = c as i32;
+ self.grid_cursor_row = r as i32;
+ }
+ }
+ }
+
+ fn sync_cursor_and_selection(&mut self) {
+ let active_nodes = self.current_dir().children.len().min(self.node_slots.len());
+ let mut node_at_cursor = None;
+ for (slot_idx, (&idx, off)) in self.node_slots.iter().zip(self.left_offsets.iter()).enumerate() {
+ if slot_idx < active_nodes {
+ if off.0 as i32 == self.grid_cursor_col && off.1 as i32 == self.grid_cursor_row {
+ node_at_cursor = Some(idx);
+ break;
+ }
+ }
+ }
+
+ if let Some(idx) = node_at_cursor {
+ if self.focused_widget != Some(idx) {
+ if let Some(old) = self.focused_widget {
+ self.widgets[old].unfocus();
+ }
+ self.widgets[idx].focus();
+ self.focused_widget = Some(idx);
+ }
+ } else {
+ if let Some(old) = self.focused_widget {
+ if self.node_slots.contains(&old) {
+ self.widgets[old].unfocus();
+ self.focused_widget = None;
+ }
+ }
}
}
@@ -1672,9 +1762,9 @@ impl State {
}
if i == CONTENT_IDX && show_cursor {
- let cx = self.grid_cursor_col as f32 * self.grid_size_x + self.pan_x;
- let cy = node_area_y + self.grid_cursor_row as f32 * self.grid_size_y + self.pan_y;
- let cw = self.grid_size_x * 2.0;
+ let cx = self.grid_cursor_col as f32 * (self.grid_size_x + self.skipped_col_w) + self.pan_x;
+ let cy = node_area_y + self.grid_cursor_row as f32 * (self.grid_size_y + self.skipped_row_h) + self.pan_y;
+ let cw = self.grid_size_x;
let ch = self.grid_size_y;
let bg_color = [0.15, 0.25, 0.45, 0.15];
@@ -1831,6 +1921,69 @@ impl State {
fn handle_event(&mut self, event: &WindowEvent) -> bool {
match event {
+ WindowEvent::MouseWheel { delta, .. } => {
+ let node_area_y = HEADER_H + MENUBAR_H + BREADCRUMB_H;
+ let dialog_open = self.widgets[CONFIG_DIALOG_IDX].visible() || self.node_palette_visible;
+ let in_network_pane = self.cursor_x >= 0.0
+ && self.cursor_x < self.content_left_w()
+ && self.cursor_y >= node_area_y
+ && self.cursor_y < self.height - STATUS_H;
+
+ if !dialog_open && in_network_pane {
+ if self.modifiers.control_key() {
+ let factor = match delta {
+ winit::event::MouseScrollDelta::LineDelta(_x, y) => {
+ if *y > 0.0 { 1.15 } else if *y < 0.0 { 1.0 / 1.15 } else { 1.0 }
+ }
+ winit::event::MouseScrollDelta::PixelDelta(pos) => {
+ let dy = pos.y as f32;
+ (1.0 + dy / 100.0).clamp(0.8, 1.25)
+ }
+ };
+ if factor != 1.0 {
+ self.zoom(factor, Some((self.cursor_x, self.cursor_y)));
+ true
+ } else {
+ false
+ }
+ } else {
+ let (dx, dy) = match delta {
+ winit::event::MouseScrollDelta::LineDelta(x, y) => {
+ (*x * 30.0, *y * 30.0)
+ }
+ winit::event::MouseScrollDelta::PixelDelta(pos) => {
+ (pos.x as f32 / self.scale as f32, pos.y as f32 / self.scale as f32)
+ }
+ };
+ self.pan_x -= dx;
+ self.pan_y -= dy;
+ self.sync_grid_settings();
+ true
+ }
+ } else {
+ false
+ }
+ }
+ WindowEvent::PinchGesture { delta, .. } => {
+ let node_area_y = HEADER_H + MENUBAR_H + BREADCRUMB_H;
+ let dialog_open = self.widgets[CONFIG_DIALOG_IDX].visible() || self.node_palette_visible;
+ let in_network_pane = self.cursor_x >= 0.0
+ && self.cursor_x < self.content_left_w()
+ && self.cursor_y >= node_area_y
+ && self.cursor_y < self.height - STATUS_H;
+
+ if !dialog_open && in_network_pane {
+ if delta.is_finite() && *delta != 0.0 {
+ let factor = (1.0 + *delta as f32).clamp(0.8, 1.25);
+ self.zoom(factor, Some((self.cursor_x, self.cursor_y)));
+ true
+ } else {
+ false
+ }
+ } else {
+ false
+ }
+ }
WindowEvent::CursorMoved { position, .. } => {
let old_cx = self.cursor_x;
let old_cy = self.cursor_y;
@@ -1936,11 +2089,18 @@ impl State {
let click_target = (0..self.widgets.len()).rev()
.find(|&i| self.widgets[i].hit_test(self.cursor_x, self.cursor_y));
if let Some(old) = self.focused_widget {
- if click_target != Some(old) {
+ if click_target != Some(old) && click_target != Some(PARAM_IDX) {
self.widgets[old].unfocus();
self.focused_widget = None;
}
}
+ if click_target.is_none() && !dialog_open && in_network_pane {
+ let col = ((self.cursor_x - self.pan_x) / (self.grid_size_x + self.skipped_col_w)).floor() as i32;
+ let row = ((self.cursor_y - node_area_y - self.pan_y) / (self.grid_size_y + self.skipped_row_h)).floor() as i32;
+ self.grid_cursor_col = col;
+ self.grid_cursor_row = row;
+ changed = true;
+ }
if let Some(i) = click_target {
if self.widgets[i].mouse_input(*button, *btn_state, self.cursor_x, self.cursor_y) {
changed = true;
@@ -1949,8 +2109,17 @@ impl State {
self.widgets[i].drag_begin(self.cursor_x, self.cursor_y);
self.drag_widget = Some(i);
}
- self.widgets[i].focus();
- self.focused_widget = Some(i);
+ if i != PARAM_IDX {
+ self.widgets[i].focus();
+ self.focused_widget = Some(i);
+ }
+ if self.node_slots.contains(&i) {
+ if let Some(slot_idx) = self.node_slots.iter().position(|&x| x == i) {
+ let off = self.left_offsets[slot_idx];
+ self.grid_cursor_col = off.0 as i32;
+ self.grid_cursor_row = off.1 as i32;
+ }
+ }
// Double-click detection for node directory entry
if self.node_slots.contains(&i) {
if let Some((t, prev)) = self.last_click {
@@ -2066,12 +2235,26 @@ impl State {
self.grid_cursor_col += 1;
changed = true;
}
+ "e" | "E" => {
+ if let Some(idx) = self.focused_widget {
+ if let Some(slot_idx) = self.node_slots.iter().position(|&x| x == idx) {
+ let active_nodes = self.current_dir().children.len().min(self.node_slots.len());
+ if slot_idx < active_nodes {
+ let visible = !self.widgets[idx].geom_visible();
+ self.widgets[idx].set_geom_visible(visible);
+ self.current_dir_mut().children[slot_idx].geometry_visible = visible;
+ self.rebuild_scene_geometry();
+ changed = true;
+ }
+ }
+ }
+ }
"-" => {
- self.zoom(1.0 / 1.15);
+ self.zoom(1.0 / 1.15, None);
changed = true;
}
"=" | "+" => {
- self.zoom(1.15);
+ self.zoom(1.15, None);
changed = true;
}
_ => {}
@@ -2322,6 +2505,30 @@ impl ApplicationHandler for App {
if let Some(state) = &mut self.state {
let mut changed = state.handle_event(&event);
if changed {
+ if let Some(focused) = state.focused_widget {
+ if state.node_slots.contains(&focused) {
+ if let Some(slot_idx) = state.node_slots.iter().position(|&x| x == focused) {
+ let updated_params = state.widgets[PARAM_IDX].node_params();
+ let dir = state.current_dir_mut();
+ if let Some(child) = dir.children.get_mut(slot_idx) {
+ let mut param_changed = false;
+ for (u_name, u_val, _type) 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 param_changed {
+ state.rebuild_scene_geometry();
+ state.sync_nodes();
+ }
+ }
+ }
+ }
+ }
+
state.sync_layout();
state.read_panel_offsets();
state.upload_vertices();
@@ -2345,6 +2552,7 @@ impl ApplicationHandler for App {
state.sync_layout();
state.read_panel_offsets();
state.upload_vertices();
+ changed = true;
}
}
@@ -2358,6 +2566,8 @@ impl ApplicationHandler for App {
state.widgets[CONFIG_DIALOG_IDX].set_config_toggle(1, state.network_grid_visible);
state.widgets[CONFIG_DIALOG_IDX].set_config_spin(0, state.grid_size_x);
state.widgets[CONFIG_DIALOG_IDX].set_config_spin(1, state.grid_size_y);
+ state.widgets[CONFIG_DIALOG_IDX].set_config_spin(2, state.skipped_row_h);
+ state.widgets[CONFIG_DIALOG_IDX].set_config_spin(3, state.skipped_col_w);
state.widgets[CONFIG_DIALOG_IDX].set_visible(true);
state.upload_vertices();
changed = true;
@@ -2382,8 +2592,8 @@ impl ApplicationHandler for App {
0 => {
state.grid_snap_enabled = val;
for &i in &state.node_slots {
- let gx = if val { state.grid_size_x } else { 0.0 };
- let gy = if val { state.grid_size_y } else { 0.0 };
+ let gx = if val { state.grid_size_x + state.skipped_col_w } else { 0.0 };
+ let gy = if val { state.grid_size_y + state.skipped_row_h } else { 0.0 };
state.widgets[i].set_grid_snap(gx, gy);
}
}
@@ -2400,19 +2610,26 @@ impl ApplicationHandler for App {
match id {
0 => state.grid_size_x = val,
1 => state.grid_size_y = val,
+ 2 => state.skipped_row_h = val,
+ 3 => state.skipped_col_w = val,
_ => {}
}
- state.widgets[CONTENT_IDX].set_grid_sizes(state.grid_size_x, state.grid_size_y);
for &i in &state.node_slots {
let (x, y, _, _) = state.widgets[i].rect();
- state.widgets[i].set_rect(x, y, state.grid_size_x * 2.0, state.grid_size_y);
+ state.widgets[i].set_rect(x, y, state.grid_size_x, state.grid_size_y);
}
state.sync_layout();
+ state.sync_grid_settings();
state.read_panel_offsets();
state.upload_vertices();
changed = true;
}
+ if changed {
+ state.sync_cursor_and_selection();
+ state.upload_vertices();
+ }
+
state.update_status_text(&format!(
"col: {:.0} vp: {:.0} params: {:.0}",
state.content_left_w(), state.viewport_w(), state.param_w(),