git.lucas.co / cce-designer
graphic design tool
git clone https://git.lucas.co/cce-designer.git

commitf0d868085361030fb96be3100178cbccb5c53efd
parentb36aad1ee7
authorLucas Galante <lsgalante12@gmail.com>
date2026-09-28 23:36
test: a trackpad swipe over a float3 band turns that component

End to end through handle_event and the wheel's write-back, on the pull
node's Value row: a finger gesture on the Y band turns Y alone and the
node's Value follows. The fix is in cce-ui's ParametersBg and Float3.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

 src/main.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/src/main.rs b/src/main.rs
index e104a6b..9abdc05 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -12892,4 +12892,54 @@ mod tests {
         // The parameter itself never changed kind: it is text in the node.
         assert_eq!(state.fs_root.children[1].params.iter().find(|p| p.name == "Value").unwrap().kind(), crate::param::ParamKind::Text);
     }
+
+    /// A trackpad swipe over a band of the pull node's float3 Value row
+    /// turns that component, and the node's Value follows: Y alone, written
+    /// back as the `x:y:z` text the Attribute node parses. Until 2026-09-28
+    /// the params pane kept every finger gesture for its own scroll, so a
+    /// slider could be turned by a wheel notch and not by a trackpad — and a
+    /// float3 gave a scroll over its Y band to X, the first row in order.
+    #[test]
+    fn a_trackpad_swipe_over_a_float3_band_turns_that_component() {
+        use crate::window::{LocalPosition, WindowEvent};
+        use cce_ui::widget::{scroll_motion::set_scroll_phase, MouseScrollDelta, ParametersBg, Position, ScrollPhase};
+        let mut state = State::new(false);
+        state.resize(1600.0, 900.0, 1.0);
+        state.rebuild_positions();
+        state.apply_layout();
+        state.focused_pane = LEFT_MENUBAR_IDX;
+        state.param_editor = crate::slots::CONTENT_IDX;
+        let mut redraw = false;
+        state.apply_action(McpAction::AddNode { template_name: "Attribute".into(), name: Some("pull1".into()), x: 5.0, y: 8.0 }, &mut redraw).unwrap();
+        let pull = state.current_dir().children.iter().position(|c| c.name == "pull1").unwrap();
+        for (name, value) in [("Input", "sphere1"), ("Operation", "Modify"), ("Attribute Name", "Pos"), ("Value", "0.00:0.00:0.00")] {
+            state.apply_action(McpAction::SetParam { slot: pull, name: name.into(), value: value.into() }, &mut redraw).unwrap();
+        }
+        state.graph_mut().set_selected_node(Some(pull));
+        state.sync_parameters_pane();
+        state.rebuild_positions();
+        state.apply_layout();
+
+        // The Y band of the Value row, from the pane's own float3 group.
+        let (bx, by) = {
+            // The slot is statically an `Adapted<ParametersBg>`.
+            let pane: &ParametersBg = state.slots.param.inner();
+            let f = pane.float3s.iter().flatten().next().expect("the Value row is a float3");
+            let (rx, ry, rw, rh) = f.get_row_rects()[1];
+            (rx + (rw - 68.0) * 0.5, ry + rh * 0.5)
+        };
+        let value = |state: &State| -> Vec<f32> {
+            state.current_dir().children[pull].params.iter().find(|p| p.name == "Value").unwrap()
+                .text().split(':').map(|v| v.parse().unwrap()).collect()
+        };
+        state.handle_event(&WindowEvent::CursorMoved { position: LocalPosition { x: bx as f64, y: by as f64 } });
+        set_scroll_phase(ScrollPhase::Finger);
+        state.ui_context.scroll_gesture_new = true;
+        state.ui_context.scroll_initiate_widget_id = None;
+        assert!(state.handle_event(&WindowEvent::MouseWheel { delta: MouseScrollDelta::PixelDelta(Position { x: 0.0, y: -60.0 }) }));
+        set_scroll_phase(ScrollPhase::Wheel);
+        let v = value(&state);
+        assert_eq!((v[0], v[2]), (0.0, 0.0), "X and Z hold: {v:?}");
+        assert_ne!(v[1], 0.0, "Y turned, and the node's Value followed: {v:?}");
+    }
 }