graphic design tool
git clone https://git.lucas.co/cce-designer.git
feat: the wheel turns the palette's zoom slider
Over the Zoom row's control a wheel notch moves the slider by 2% of its
range, up meaning in — the toolkit slider's rate and the viewport zoom
wheel's sign; over the rest of the list the wheel scrolls as before.
`dialog_mouse_wheel` drains the change the way a click's is drained.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
CLAUDE.md | 7 +++++--
src/dialog.rs | 34 ++++++++++++++++++++++++++++++++--
src/main.rs | 16 ++++++++++++++++
3 files changed, 53 insertions(+), 4 deletions(-)
diff --git a/CLAUDE.md b/CLAUDE.md
index e00456b..2799f14 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -934,8 +934,11 @@ on the band jumps to it and arms the app's widget-drag protocol on
half's sliders arm it on `DIALOG_PARAMS_IDX`), so the value follows the
pointer off the plate; the drained value lands through
`State::set_zoom_percent`, which zooms about the cursor cell and re-reads
-the row, since `zoom` clamps. Left/Right nudge it by a Zoom In / Out step
-while it is selected; Enter on it runs nothing. The dialog stays up
+the row, since `zoom` clamps. The wheel over the control turns it (2% of
+the range a notch, up meaning in — the viewport zoom wheel's sign) where
+over the rest of the list it scrolls; `dialog_mouse_wheel` drains the
+change like a click. Left/Right nudge it by a Zoom In / Out step while it
+is selected; Enter on it runs nothing. The dialog stays up
throughout, as it does for the toggle rows. Ranked like a row labelled
"Zoom", so a query still finds or drops it.
diff --git a/src/dialog.rs b/src/dialog.rs
index 42dab74..c029a15 100644
--- a/src/dialog.rs
+++ b/src/dialog.rs
@@ -526,6 +526,21 @@ impl Dialog {
(b.x, b.width)
}
+ /// Step the slider by wheel notches: 2% of the range each, the toolkit
+ /// slider's own rate, up meaning more — the sign the viewport's zoom
+ /// wheel has, since this IS a zoom.
+ fn scroll_slider(&mut self, notches: f32) -> bool {
+ let (min, max) = self.slider_stamp.range();
+ let Some(cur) = self.slider_row().and_then(|i| self.rows[i].slider) else { return false };
+ let v = (cur + notches * 0.02 * (max - min)).clamp(min.min(max), max.max(min));
+ if (v - cur).abs() < 1e-6 {
+ return false;
+ }
+ self.set_slider_value(v);
+ self.slider_change = Some(v);
+ true
+ }
+
/// Put the slider where the pointer is along the captured band. Jumps,
/// rather than dragging relative to a grab: the band has no thumb to
/// grab, and a click on a zoom scale should mean "this much".
@@ -955,10 +970,22 @@ impl Input for Dialog {
self.hover_tab = tab;
changed
}
- Event::MouseWheel { delta, .. } => {
+ Event::MouseWheel { delta, x, y, .. } => {
if !self.shows_list() || self.rows.is_empty() {
return false;
}
+ // Over the slider row's control the wheel turns the slider,
+ // not the list — the rest of the row still scrolls.
+ if let Some(i) = self.row_at(rect, *x, *y) {
+ if self.rows[i].slider.is_some() {
+ if let Some(r) = self.row_rect(rect, i) {
+ let s = Self::slider_rect(r);
+ if *x >= s.x && *x < s.x + s.width {
+ return self.scroll_slider(delta.notches_y());
+ }
+ }
+ }
+ }
// The DE scroll model: a notch is one row and glides there, a
// trackpad tracks 1:1 and coasts on the lift (`tick` advances).
let max = self.max_scroll_px();
@@ -1916,7 +1943,10 @@ impl State {
return self.dispatch_uncovered(DIALOG_PARAMS_IDX, &ev);
}
if self.in_dialog_slot(DIALOG_IDX, x, y) {
- return self.dispatch_uncovered(DIALOG_IDX, &ev);
+ let taken = self.dispatch_uncovered(DIALOG_IDX, &ev);
+ // A wheel over the zoom slider row moved it: land the value.
+ self.drain_dialog_clicks();
+ return taken;
}
false
}
diff --git a/src/main.rs b/src/main.rs
index b4035f5..8bfca24 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -8463,6 +8463,22 @@ mod tests {
// An ordinary row still picks.
assert!(d.mouse_input(MouseButton::Left, ElementState::Pressed, 30.0, row_y + 24.0, &mut ctx));
assert_eq!(d.take_activated().as_deref(), Some("c1"));
+
+ // The wheel over the control turns the slider — a notch up is 2% of
+ // the range more, as on the toolkit's slider — and over the label
+ // end it scrolls the list instead, reporting nothing.
+ let before = d.rows[0].slider.unwrap();
+ let wheel = |x: f32, y: f32| cce_ui::widget::Event::MouseWheel {
+ delta: cce_ui::widget::MouseScrollDelta::LineDelta(0.0, 1.0),
+ x, y, local_x: x, local_y: y,
+ };
+ ctx.note_scroll_event();
+ assert!(d.handle_event(&wheel(band_x + 10.0, row_y), &mut ctx));
+ let v = d.take_slider_change().expect("a wheel over the band reports a value");
+ assert!((v - (before + 0.02 * 300.0)).abs() < 1e-3, "notch up: {before} -> {v}");
+ ctx.note_scroll_event();
+ d.handle_event(&wheel(30.0, row_y), &mut ctx);
+ assert_eq!(d.take_slider_change(), None, "over the label the wheel is the list's");
}
/// Backspace walks the query back, and the ranking follows it.