From 304b9bdfeb8db17796fc20b95ed91a45f2ce9ca7 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Mon, 14 Mar 2022 15:00:40 +0100 Subject: [PATCH] Fix granular drag when clicking with shift held --- nih_plug_iced/src/widgets/param_slider.rs | 55 +++++++++++++++++++---- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/nih_plug_iced/src/widgets/param_slider.rs b/nih_plug_iced/src/widgets/param_slider.rs index ec2d0152..1f114bc2 100644 --- a/nih_plug_iced/src/widgets/param_slider.rs +++ b/nih_plug_iced/src/widgets/param_slider.rs @@ -9,10 +9,13 @@ use nih_plug::prelude::{GuiContext, Param, ParamSetter}; use super::util; use super::ParamMessage; +/// When shift+dragging a parameter, one pixel dragged corresponds to this much change in the +/// noramlized parameter. +const GRANULAR_DRAG_MULTIPLIER: f32 = 0.1; + /// A slider that integrates with NIH-plug's [`Param`] types. /// /// TODO: There are currently no styling options at all -/// TODO: Handle Shift+drag for granular drag /// TODO: Handle Alt+click for text entry pub struct ParamSlider<'a, P: Param, Renderer: text::Renderer> { state: &'a mut State, @@ -31,8 +34,11 @@ pub struct ParamSlider<'a, P: Param, Renderer: text::Renderer> { /// State for a [`ParamSlider`]. #[derive(Debug, Default)] pub struct State { - drag_active: bool, keyboard_modifiers: keyboard::Modifiers, + drag_active: bool, + /// We keep track of the start coordinate holding down Shift while dragging for higher precision + /// dragging. This is a `None` value when granular dragging is not active. + granular_drag_start_x: Option, /// Track clicks for double clicks. last_click: Option, /// Will be set to `true` if we just reset the parameter since you could otherwise reset the @@ -117,25 +123,35 @@ impl<'a, P: Param, Renderer: text::Renderer> Widget | Event::Touch(touch::Event::FingerPressed { .. }) => { if bounds.contains(cursor_position) { shell.publish(ParamMessage::BeginSetParameter(self.param.as_ptr())); + self.state.drag_active = true; - // Immediately trigger a parameter update if the value would be different, or - // reset the parameter if Ctrl is held or the parameter is being double clicked let click = mouse::Click::new(cursor_position, self.state.last_click); self.state.last_click = Some(click); - if self.state.keyboard_modifiers.control() + if self.state.keyboard_modifiers.command() || matches!(click.kind(), mouse::click::Kind::Double) { + // Immediately trigger a parameter update if the value would be different, or + // reset the parameter if Ctrl is held or the parameter is being double clicked self.set_normalized_value( shell, self.setter.default_normalized_param_value(self.param), ); self.state.ignore_changes = true; + } else if self.state.keyboard_modifiers.shift() { + // When holding down shift while clicking on a parameter we want to + // granuarly edit the parameter without jumping to a new value + self.state.granular_drag_start_x = + Some(util::remap_rect_x_t(&bounds, self.param.normalized_value())); + + self.state.ignore_changes = false; } else { self.set_normalized_value( shell, util::remap_rect_x_coordinate(&bounds, cursor_position.x), ); + self.state.granular_drag_start_x = None; + self.state.ignore_changes = false; } @@ -146,6 +162,7 @@ impl<'a, P: Param, Renderer: text::Renderer> Widget | Event::Touch(touch::Event::FingerLifted { .. } | touch::Event::FingerLost { .. }) => { if bounds.contains(cursor_position) { shell.publish(ParamMessage::EndSetParameter(self.param.as_ptr())); + self.state.drag_active = false; return event::Status::Captured; @@ -158,10 +175,30 @@ impl<'a, P: Param, Renderer: text::Renderer> Widget && self.state.drag_active && bounds.contains(cursor_position) { - self.set_normalized_value( - shell, - util::remap_rect_x_coordinate(&bounds, cursor_position.x), - ); + // If shift is being held then the drag should be more granular instead of + // absolute + if self.state.keyboard_modifiers.shift() { + let drag_start_x = *self + .state + .granular_drag_start_x + .get_or_insert(cursor_position.x); + + self.set_normalized_value( + shell, + util::remap_rect_x_coordinate( + &bounds, + drag_start_x + + (cursor_position.x - drag_start_x) * GRANULAR_DRAG_MULTIPLIER, + ), + ); + } else { + self.state.granular_drag_start_x = None; + + self.set_normalized_value( + shell, + util::remap_rect_x_coordinate(&bounds, cursor_position.x), + ); + } return event::Status::Captured; }