Add basic dragging gestures to the X-Y pad
This commit is contained in:
parent
4f9a0eb117
commit
27d42797ee
|
@ -17,6 +17,7 @@
|
|||
use nih_plug::prelude::Param;
|
||||
use nih_plug_vizia::vizia::prelude::*;
|
||||
use nih_plug_vizia::widgets::param_base::ParamWidgetBase;
|
||||
use nih_plug_vizia::widgets::util::{self, ModifiersExt};
|
||||
|
||||
// TODO: Vizia doesn't let you do this -50% translation programmatically yet, so this is hardcoded
|
||||
// for now
|
||||
|
@ -25,9 +26,17 @@ const HANDLE_WIDTH_PX: f32 = 20.0;
|
|||
/// An X-Y pad that controlers two parameters at the same time by binding them to one of the two
|
||||
/// axes. This specific implementation has a tooltip for the X-axis parmaeter and allows
|
||||
/// Alt+clicking to enter a specific value.
|
||||
//
|
||||
// TODO: Text entry for the x-parameter
|
||||
// TODO: Tooltip
|
||||
// TODO: Granular dragging
|
||||
pub struct XyPad {
|
||||
x_param_base: ParamWidgetBase,
|
||||
y_param_base: ParamWidgetBase,
|
||||
|
||||
/// Will be set to `true` if we're dragging the parameter. Resetting the parameter or entering a
|
||||
/// text value should not initiate a drag.
|
||||
drag_active: bool,
|
||||
}
|
||||
|
||||
/// The [`XyPad`]'s handle. This is a separate eleemnt to allow easier positioning.
|
||||
|
@ -54,6 +63,8 @@ impl XyPad {
|
|||
Self {
|
||||
x_param_base: ParamWidgetBase::new(cx, params.clone(), params_to_x_param),
|
||||
y_param_base: ParamWidgetBase::new(cx, params.clone(), params_to_y_param),
|
||||
|
||||
drag_active: false,
|
||||
}
|
||||
.build(
|
||||
cx,
|
||||
|
@ -91,6 +102,33 @@ impl XyPad {
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
/// Should be called at the start of a drag operation.
|
||||
fn begin_set_parameters(&self, cx: &mut EventContext) {
|
||||
self.x_param_base.begin_set_parameter(cx);
|
||||
self.y_param_base.begin_set_parameter(cx);
|
||||
}
|
||||
|
||||
/// Resets both parameters. `begin_set_parameters()` needs to be called first.
|
||||
fn reset_parameters(&self, cx: &mut EventContext) {
|
||||
self.x_param_base
|
||||
.set_normalized_value(cx, self.x_param_base.default_normalized_value());
|
||||
self.y_param_base
|
||||
.set_normalized_value(cx, self.y_param_base.default_normalized_value());
|
||||
}
|
||||
|
||||
/// Set a normalized value for both parameters. `begin_set_parameters()` needs to be called
|
||||
/// first.
|
||||
fn set_normalized_values(&self, cx: &mut EventContext, (x_value, y_value): (f32, f32)) {
|
||||
self.x_param_base.set_normalized_value(cx, x_value);
|
||||
self.y_param_base.set_normalized_value(cx, y_value);
|
||||
}
|
||||
|
||||
/// Should be called at the end of a drag operation.
|
||||
fn end_set_parameters(&self, cx: &mut EventContext) {
|
||||
self.x_param_base.end_set_parameter(cx);
|
||||
self.y_param_base.end_set_parameter(cx);
|
||||
}
|
||||
}
|
||||
|
||||
impl XyPadHandle {
|
||||
|
@ -105,12 +143,128 @@ impl View for XyPad {
|
|||
fn element(&self) -> Option<&'static str> {
|
||||
Some("xy-pad")
|
||||
}
|
||||
|
||||
fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
|
||||
event.map(|window_event, meta| match window_event {
|
||||
WindowEvent::MouseDown(MouseButton::Left)
|
||||
| WindowEvent::MouseTripleClick(MouseButton::Left) => {
|
||||
// TODO: Alt+click text entry
|
||||
if cx.modifiers.command() {
|
||||
// Ctrl+Click and double click should reset the parameter instead of initiating
|
||||
// a drag operation
|
||||
self.begin_set_parameters(cx);
|
||||
self.reset_parameters(cx);
|
||||
self.end_set_parameters(cx);
|
||||
} else {
|
||||
self.drag_active = true;
|
||||
cx.capture();
|
||||
// NOTE: Otherwise we don't get key up events
|
||||
cx.focus();
|
||||
cx.set_active(true);
|
||||
|
||||
// When holding down shift while clicking on a parameter we want to granuarly
|
||||
// edit the parameter without jumping to a new value
|
||||
self.begin_set_parameters(cx);
|
||||
// TODO: Granular dragging
|
||||
// if cx.modifiers.shift() {
|
||||
// self.granular_drag_start_x_value = Some((
|
||||
// cx.mouse.cursorx,
|
||||
// self.param_base.unmodulated_normalized_value(),
|
||||
// ));
|
||||
// } else {
|
||||
// self.granular_drag_start_x_value = None;
|
||||
// self.set_normalized_value_drag(
|
||||
// cx,
|
||||
// util::remap_current_entity_x_coordinate(cx, cx.mouse.cursorx),
|
||||
// );
|
||||
// }
|
||||
}
|
||||
|
||||
meta.consume();
|
||||
}
|
||||
WindowEvent::MouseDoubleClick(MouseButton::Left) => {
|
||||
// Ctrl+Click and double click should reset the parameters instead of initiating a
|
||||
// drag operation
|
||||
self.begin_set_parameters(cx);
|
||||
self.reset_parameters(cx);
|
||||
self.end_set_parameters(cx);
|
||||
|
||||
meta.consume();
|
||||
}
|
||||
WindowEvent::MouseUp(MouseButton::Left) => {
|
||||
if self.drag_active {
|
||||
self.drag_active = false;
|
||||
cx.release();
|
||||
cx.set_active(false);
|
||||
|
||||
self.end_set_parameters(cx);
|
||||
|
||||
meta.consume();
|
||||
}
|
||||
}
|
||||
WindowEvent::MouseMove(x, y) => {
|
||||
if self.drag_active {
|
||||
// If shift is being held then the drag should be more granular instead of
|
||||
// absolute
|
||||
// TODO: Granular dragging
|
||||
// if cx.modifiers.shift() {
|
||||
// let (drag_start_x, drag_start_value) =
|
||||
// *self.granular_drag_start_x_value.get_or_insert_with(|| {
|
||||
// (
|
||||
// cx.mouse.cursorx,
|
||||
// self.param_base.unmodulated_normalized_value(),
|
||||
// )
|
||||
// });
|
||||
|
||||
// self.set_normalized_value_drag(
|
||||
// cx,
|
||||
// util::remap_current_entity_x_coordinate(
|
||||
// cx,
|
||||
// // This can be optimized a bit
|
||||
// util::remap_current_entity_x_t(cx, drag_start_value)
|
||||
// + (*x - drag_start_x) * GRANULAR_DRAG_MULTIPLIER,
|
||||
// ),
|
||||
// );
|
||||
// } else {
|
||||
// self.granular_drag_start_x_value = None;
|
||||
|
||||
// self.set_normalized_value_drag(
|
||||
// cx,
|
||||
// util::remap_current_entity_x_coordinate(cx, *x),
|
||||
// );
|
||||
// }
|
||||
|
||||
self.set_normalized_values(
|
||||
cx,
|
||||
(
|
||||
util::remap_current_entity_x_coordinate(cx, *x),
|
||||
// We want the top of the widget to be 1.0 and the bottom to be 0.0,
|
||||
// this is the opposite of how the y-coordinate works
|
||||
1.0 - util::remap_current_entity_y_coordinate(cx, *y),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
// TODO: Granular dragging
|
||||
// WindowEvent::KeyUp(_, Some(Key::Shift)) => {
|
||||
// // If this happens while dragging, snap back to reality uh I mean the current screen
|
||||
// // position
|
||||
// if self.drag_active && self.granular_drag_start_x_value.is_some() {
|
||||
// self.granular_drag_start_x_value = None;
|
||||
// self.param_base.set_normalized_value(
|
||||
// cx,
|
||||
// util::remap_current_entity_x_coordinate(cx, cx.mouse.cursorx),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// TODO: Scrolling, because why not. Could be useful on laptops/with touchpads.
|
||||
_ => {}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl View for XyPadHandle {
|
||||
fn element(&self) -> Option<&'static str> {
|
||||
Some("xy-pad__handle")
|
||||
}
|
||||
|
||||
// TODO: Add a draw() implementation that draws at a -50% translation
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue