use crate::scale::Scale;
use gpui::*;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
#[derive(Clone, Copy, Debug)]
pub struct DragState {
pub start_pos: f32, pub start_value: f64, }
thread_local! {
static DRAG_STATES: RefCell<HashMap<String, DragState>> = RefCell::new(HashMap::new());
}
pub fn store_drag_state(element_key: &str, start_pos: f32, start_value: f64) {
DRAG_STATES.with(|states| {
states.borrow_mut().insert(
element_key.to_string(),
DragState {
start_pos,
start_value,
},
);
});
}
pub fn get_drag_state(element_key: &str) -> Option<DragState> {
DRAG_STATES.with(|states| states.borrow().get(element_key).copied())
}
pub fn clear_drag_state(element_key: &str) {
DRAG_STATES.with(|states| {
states.borrow_mut().remove(element_key);
});
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DragOrientation {
Vertical,
Horizontal,
Rotational,
}
#[derive(Clone)]
pub struct InteractionConfig {
pub min: f64,
pub max: f64,
pub scale: Scale,
pub orientation: DragOrientation,
pub track_size: f32, pub media_keys: bool,
}
impl InteractionConfig {
pub fn vertical(min: f64, max: f64, scale: Scale, track_height: f32) -> Self {
Self {
min,
max,
scale,
orientation: DragOrientation::Vertical,
track_size: track_height,
media_keys: false,
}
}
pub fn horizontal(min: f64, max: f64, scale: Scale, track_width: f32) -> Self {
Self {
min,
max,
scale,
orientation: DragOrientation::Horizontal,
track_size: track_width,
media_keys: false,
}
}
pub fn rotational(min: f64, max: f64, scale: Scale, drag_distance: f32) -> Self {
Self {
min,
max,
scale,
orientation: DragOrientation::Rotational,
track_size: drag_distance,
media_keys: false,
}
}
pub fn with_media_keys(mut self) -> Self {
self.media_keys = true;
self
}
}
pub type ValueTracker = Rc<std::cell::Cell<f64>>;
pub fn value_tracker(initial: f64) -> ValueTracker {
Rc::new(std::cell::Cell::new(initial))
}
pub fn handle_keyboard(
key: &str,
modifiers: &Modifiers,
current_value: f64,
config: &InteractionConfig,
) -> Option<f64> {
let scale = config.scale;
let min = config.min;
let max = config.max;
let step_size = if modifiers.shift {
0.01
} else if modifiers.control || modifiers.platform {
0.10
} else {
0.05
};
match key {
"up" | "right" => Some(scale.step_value(current_value, min, max, 1.0, step_size)),
"down" | "left" => Some(scale.step_value(current_value, min, max, -1.0, step_size)),
"pageup" => Some(scale.step_value(current_value, min, max, 1.0, 0.10)),
"pagedown" => Some(scale.step_value(current_value, min, max, -1.0, 0.10)),
"home" => Some(min),
"end" => Some(max),
_ => {
if config.media_keys {
match key {
"audiomute" => None, "audiolowervolume" => {
Some(scale.step_value(current_value, min, max, -1.0, 0.05))
}
"audioraisevolume" => {
Some(scale.step_value(current_value, min, max, 1.0, 0.05))
}
_ => None,
}
} else {
None
}
}
}
}
pub fn handle_scroll(
delta: &ScrollDelta,
modifiers: &Modifiers,
current_value: f64,
config: &InteractionConfig,
) -> Option<f64> {
let (delta_x, delta_y): (f32, f32) = match delta {
ScrollDelta::Pixels(point) => (point.x.into(), point.y.into()),
ScrollDelta::Lines(point) => (point.x, point.y),
};
let scroll_delta = match config.orientation {
DragOrientation::Vertical | DragOrientation::Rotational => {
if delta_y.abs() > 0.0001 {
delta_y
} else if delta_x.abs() > 0.0001 {
delta_x
} else {
return None;
}
}
DragOrientation::Horizontal => {
if delta_x.abs() > 0.0001 {
-delta_x } else if delta_y.abs() > 0.0001 {
delta_y
} else {
return None;
}
}
};
let direction = if scroll_delta < 0.0 { 1.0 } else { -1.0 };
let step_size = if modifiers.shift { 0.005 } else { 0.05 };
Some(
config
.scale
.step_value(current_value, config.min, config.max, direction, step_size),
)
}
pub fn handle_drag(
current_pos: f32,
drag_state: &DragState,
config: &InteractionConfig,
) -> Option<f64> {
let delta = match config.orientation {
DragOrientation::Vertical => {
drag_state.start_pos - current_pos
}
DragOrientation::Horizontal => {
current_pos - drag_state.start_pos
}
DragOrientation::Rotational => {
drag_state.start_pos - current_pos
}
};
if delta.abs() < 2.0 {
return None;
}
let delta_norm = (delta / config.track_size) as f64;
Some(config.scale.step_value(
drag_state.start_value,
config.min,
config.max,
delta_norm,
1.0,
))
}