1
0
Fork 0

Drag very granularly with Shift+Drag

This commit is contained in:
Robbert van der Helm 2022-02-09 12:24:01 +01:00
parent 5dd3cab305
commit 7eb8622736
4 changed files with 66 additions and 2 deletions

1
Cargo.lock generated
View file

@ -533,6 +533,7 @@ dependencies = [
"crossbeam", "crossbeam",
"egui", "egui",
"egui-baseview", "egui-baseview",
"lazy_static",
"nih_plug", "nih_plug",
"parking_lot", "parking_lot",
] ]

View file

@ -15,4 +15,5 @@ crossbeam = "0.8"
egui = "0.16" egui = "0.16"
# Upstream doesn't work with the current baseview and egui versions # Upstream doesn't work with the current baseview and egui versions
egui-baseview = { git = "https://github.com/robbert-vdh/egui-baseview.git", branch = "fix/update-dependencies" } egui-baseview = { git = "https://github.com/robbert-vdh/egui-baseview.git", branch = "fix/update-dependencies" }
lazy_static = "1.4"
parking_lot = "0.12" parking_lot = "0.12"

View file

@ -1,13 +1,25 @@
use egui::{vec2, Response, Sense, Stroke, TextStyle, Ui, Widget}; use egui::{vec2, Response, Sense, Stroke, TextStyle, Ui, Vec2, Widget};
use lazy_static::lazy_static;
use super::util; use super::util;
use nih_plug::{Param, ParamSetter}; use nih_plug::{Param, ParamSetter};
/// When shift+dragging a parameter, one pixel dragged corresponds to this much change in the
/// noramlized parameter.
const GRANULAR_DRAG_MULTIPLIER: f32 = 0.0015;
lazy_static! {
static ref DRAG_NORMALIZED_START_VALUE_MEMORY_ID: egui::Id = egui::Id::new((file!(), 0));
static ref DRAG_AMOUNT_MEMORY_ID: egui::Id = egui::Id::new((file!(), 1));
}
/// A slider widget similar to [egui::widgets::Slider] that knows about NIH-plug parameters ranges /// A slider widget similar to [egui::widgets::Slider] that knows about NIH-plug parameters ranges
/// and can get values for it. /// and can get values for it.
/// ///
/// TODO: Vertical orientation /// TODO: Vertical orientation
/// TODO: Check below for more input methods that should be added /// TODO: Check below for more input methods that should be added
/// TODO: Decouple the logic from the drawing so we can also do things like nobs without having to
/// repeat everything
pub struct ParamSlider<'a, P: Param> { pub struct ParamSlider<'a, P: Param> {
param: &'a P, param: &'a P,
setter: &'a ParamSetter<'a>, setter: &'a ParamSetter<'a>,
@ -46,9 +58,51 @@ impl<'a, P: Param> ParamSlider<'a, P> {
.set_parameter_normalized(self.param, normalized_default); .set_parameter_normalized(self.param, normalized_default);
} }
fn granular_drag(&self, ui: &Ui, drag_delta: Vec2) {
// Remember the intial position when we started with the granular drag. This value gets
// reset whenever we have a normal itneraction with the slider.
let start_value = if Self::get_drag_amount_memory(ui) == 0.0 {
Self::set_drag_normalized_start_value_memory(ui, self.normalized_value());
self.normalized_value()
} else {
Self::get_drag_normalized_start_value_memory(ui)
};
let total_drag_distance = drag_delta.x + Self::get_drag_amount_memory(ui);
Self::set_drag_amount_memory(ui, total_drag_distance);
self.set_normalized_value(
(start_value + (total_drag_distance * GRANULAR_DRAG_MULTIPLIER)).clamp(0.0, 1.0),
);
}
fn end_drag(&self) { fn end_drag(&self) {
self.setter.end_set_parameter(self.param); self.setter.end_set_parameter(self.param);
} }
fn get_drag_normalized_start_value_memory(ui: &Ui) -> f32 {
ui.memory()
.data
.get_temp(*DRAG_NORMALIZED_START_VALUE_MEMORY_ID)
.unwrap_or(0.0)
}
fn set_drag_normalized_start_value_memory(ui: &Ui, amount: f32) {
ui.memory()
.data
.insert_temp(*DRAG_NORMALIZED_START_VALUE_MEMORY_ID, amount);
}
fn get_drag_amount_memory(ui: &Ui) -> f32 {
ui.memory()
.data
.get_temp(*DRAG_AMOUNT_MEMORY_ID)
.unwrap_or(0.0)
}
fn set_drag_amount_memory(ui: &Ui, amount: f32) {
ui.memory().data.insert_temp(*DRAG_AMOUNT_MEMORY_ID, amount);
}
} }
impl<P: Param> Widget for ParamSlider<'_, P> { impl<P: Param> Widget for ParamSlider<'_, P> {
@ -82,17 +136,24 @@ impl<P: Param> Widget for ParamSlider<'_, P> {
// TODO: Optionally add alt+click for value entry? // TODO: Optionally add alt+click for value entry?
// TODO: Handle shift+drag being more granular // TODO: Handle shift+drag being more granular
if response.drag_started() { if response.drag_started() {
// When beginning a drag or dragging normally, reset the memory used to keep track of
// our granular drag
self.begin_drag(); self.begin_drag();
Self::set_drag_amount_memory(ui, 0.0);
} }
if let Some(click_pos) = response.interact_pointer_pos() { if let Some(click_pos) = response.interact_pointer_pos() {
// Like double clicking, Ctrl+Click should reset the parameter
if ui.input().modifiers.command { if ui.input().modifiers.command {
// Like double clicking, Ctrl+Click should reset the parameter
self.reset_param(); self.reset_param();
} else if ui.input().modifiers.shift {
// And shift dragging should switch to a more granulra input method
self.granular_drag(ui, response.drag_delta());
} else { } else {
let proportion = let proportion =
egui::emath::remap_clamp(click_pos.x, response.rect.x_range(), 0.0..=1.0) egui::emath::remap_clamp(click_pos.x, response.rect.x_range(), 0.0..=1.0)
as f64; as f64;
self.set_normalized_value(proportion as f32); self.set_normalized_value(proportion as f32);
Self::set_drag_amount_memory(ui, 0.0);
} }
} }
if response.double_clicked() { if response.double_clicked() {

View file

@ -125,6 +125,7 @@ impl Plugin for Gain {
// It's not yet fully implemented, as the text is missing. // It's not yet fully implemented, as the text is missing.
ui.label("Gain (now linked to some random skewed int)"); ui.label("Gain (now linked to some random skewed int)");
ui.add(widgets::ParamSlider::for_param(&params.some_int, setter)); ui.add(widgets::ParamSlider::for_param(&params.some_int, setter));
ui.add(widgets::ParamSlider::for_param(&params.gain, setter));
// This is a simple naieve version of a parameter slider that's not aware of how // This is a simple naieve version of a parameter slider that's not aware of how
// the parmaeters work // the parmaeters work