Drag very granularly with Shift+Drag
This commit is contained in:
parent
5dd3cab305
commit
7eb8622736
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -533,6 +533,7 @@ dependencies = [
|
||||||
"crossbeam",
|
"crossbeam",
|
||||||
"egui",
|
"egui",
|
||||||
"egui-baseview",
|
"egui-baseview",
|
||||||
|
"lazy_static",
|
||||||
"nih_plug",
|
"nih_plug",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
]
|
]
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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(¶ms.some_int, setter));
|
ui.add(widgets::ParamSlider::for_param(¶ms.some_int, setter));
|
||||||
|
ui.add(widgets::ParamSlider::for_param(¶ms.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
|
||||||
|
|
Loading…
Reference in a new issue