1
0
Fork 0

Fix granular drag when clicking with shift held

This commit is contained in:
Robbert van der Helm 2022-03-14 15:00:40 +01:00
parent 986e93b8d0
commit 304b9bdfeb

View file

@ -9,10 +9,13 @@ use nih_plug::prelude::{GuiContext, Param, ParamSetter};
use super::util; use super::util;
use super::ParamMessage; 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. /// A slider that integrates with NIH-plug's [`Param`] types.
/// ///
/// TODO: There are currently no styling options at all /// TODO: There are currently no styling options at all
/// TODO: Handle Shift+drag for granular drag
/// TODO: Handle Alt+click for text entry /// TODO: Handle Alt+click for text entry
pub struct ParamSlider<'a, P: Param, Renderer: text::Renderer> { pub struct ParamSlider<'a, P: Param, Renderer: text::Renderer> {
state: &'a mut State, state: &'a mut State,
@ -31,8 +34,11 @@ pub struct ParamSlider<'a, P: Param, Renderer: text::Renderer> {
/// State for a [`ParamSlider`]. /// State for a [`ParamSlider`].
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct State { pub struct State {
drag_active: bool,
keyboard_modifiers: keyboard::Modifiers, 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<f32>,
/// Track clicks for double clicks. /// Track clicks for double clicks.
last_click: Option<mouse::Click>, last_click: Option<mouse::Click>,
/// Will be set to `true` if we just reset the parameter since you could otherwise reset the /// 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<ParamMessage, Renderer>
| Event::Touch(touch::Event::FingerPressed { .. }) => { | Event::Touch(touch::Event::FingerPressed { .. }) => {
if bounds.contains(cursor_position) { if bounds.contains(cursor_position) {
shell.publish(ParamMessage::BeginSetParameter(self.param.as_ptr())); shell.publish(ParamMessage::BeginSetParameter(self.param.as_ptr()));
self.state.drag_active = true; 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); let click = mouse::Click::new(cursor_position, self.state.last_click);
self.state.last_click = Some(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) || 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( self.set_normalized_value(
shell, shell,
self.setter.default_normalized_param_value(self.param), self.setter.default_normalized_param_value(self.param),
); );
self.state.ignore_changes = true; 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 { } else {
self.set_normalized_value( self.set_normalized_value(
shell, shell,
util::remap_rect_x_coordinate(&bounds, cursor_position.x), util::remap_rect_x_coordinate(&bounds, cursor_position.x),
); );
self.state.granular_drag_start_x = None;
self.state.ignore_changes = false; self.state.ignore_changes = false;
} }
@ -146,6 +162,7 @@ impl<'a, P: Param, Renderer: text::Renderer> Widget<ParamMessage, Renderer>
| Event::Touch(touch::Event::FingerLifted { .. } | touch::Event::FingerLost { .. }) => { | Event::Touch(touch::Event::FingerLifted { .. } | touch::Event::FingerLost { .. }) => {
if bounds.contains(cursor_position) { if bounds.contains(cursor_position) {
shell.publish(ParamMessage::EndSetParameter(self.param.as_ptr())); shell.publish(ParamMessage::EndSetParameter(self.param.as_ptr()));
self.state.drag_active = false; self.state.drag_active = false;
return event::Status::Captured; return event::Status::Captured;
@ -158,10 +175,30 @@ impl<'a, P: Param, Renderer: text::Renderer> Widget<ParamMessage, Renderer>
&& self.state.drag_active && self.state.drag_active
&& bounds.contains(cursor_position) && bounds.contains(cursor_position)
{ {
self.set_normalized_value( // If shift is being held then the drag should be more granular instead of
shell, // absolute
util::remap_rect_x_coordinate(&bounds, cursor_position.x), 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; return event::Status::Captured;
} }