diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2024-03-05 13:32:57 +0400 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2024-03-05 13:32:57 +0400 |
| commit | f9127616b02eb12b462aca3e48fa27bf0479a4b0 (patch) | |
| tree | f7552abcb80253f29b01ce37583d960e9561ad85 /src | |
| parent | ae89b2e514fd4c22a38c2dce258707c369ca944a (diff) | |
| download | niri-f9127616b02eb12b462aca3e48fa27bf0479a4b0.tar.gz niri-f9127616b02eb12b462aca3e48fa27bf0479a4b0.tar.bz2 niri-f9127616b02eb12b462aca3e48fa27bf0479a4b0.zip | |
Implement rubber banding for the vertical gesture
Diffstat (limited to 'src')
| -rw-r--r-- | src/layout/monitor.rs | 22 | ||||
| -rw-r--r-- | src/lib.rs | 1 | ||||
| -rw-r--r-- | src/rubber_band.rs | 39 |
3 files changed, 59 insertions, 3 deletions
diff --git a/src/layout/monitor.rs b/src/layout/monitor.rs index f2e4c30e..f817ef1a 100644 --- a/src/layout/monitor.rs +++ b/src/layout/monitor.rs @@ -15,12 +15,18 @@ use super::workspace::{ use super::{LayoutElement, Options}; use crate::animation::Animation; use crate::render_helpers::renderer::NiriRenderer; +use crate::rubber_band::RubberBand; use crate::swipe_tracker::SwipeTracker; use crate::utils::output_size; /// Amount of touchpad movement to scroll the height of one workspace. const WORKSPACE_GESTURE_MOVEMENT: f64 = 300.; +const WORKSPACE_GESTURE_RUBBER_BAND: RubberBand = RubberBand { + stiffness: 0.5, + limit: 0.05, +}; + #[derive(Debug)] pub struct Monitor<W: LayoutElement> { /// Output for this monitor. @@ -762,7 +768,8 @@ impl<W: LayoutElement> Monitor<W> { let min = gesture.center_idx.saturating_sub(1) as f64; let max = (gesture.center_idx + 1).min(self.workspaces.len() - 1) as f64; - let new_idx = (gesture.center_idx as f64 + pos).clamp(min, max); + let new_idx = gesture.center_idx as f64 + pos; + let new_idx = WORKSPACE_GESTURE_RUBBER_BAND.clamp(min, max, new_idx); if gesture.current_idx == new_idx { return Some(false); @@ -783,14 +790,23 @@ impl<W: LayoutElement> Monitor<W> { return true; } - let velocity = gesture.tracker.velocity() / WORKSPACE_GESTURE_MOVEMENT; + let mut velocity = gesture.tracker.velocity() / WORKSPACE_GESTURE_MOVEMENT; + let current_pos = gesture.tracker.pos() / WORKSPACE_GESTURE_MOVEMENT; let pos = gesture.tracker.projected_end_pos() / WORKSPACE_GESTURE_MOVEMENT; let min = gesture.center_idx.saturating_sub(1) as f64; let max = (gesture.center_idx + 1).min(self.workspaces.len() - 1) as f64; - let new_idx = (gesture.center_idx as f64 + pos).clamp(min, max); + let new_idx = gesture.center_idx as f64 + pos; + + let new_idx = WORKSPACE_GESTURE_RUBBER_BAND.clamp(min, max, new_idx); let new_idx = new_idx.round() as usize; + velocity *= WORKSPACE_GESTURE_RUBBER_BAND.clamp_derivative( + min, + max, + gesture.center_idx as f64 + current_pos, + ); + self.active_workspace_idx = new_idx; self.workspace_switch = Some(WorkspaceSwitch::Animation(Animation::new( gesture.current_idx, @@ -15,6 +15,7 @@ pub mod layout; pub mod niri; pub mod protocols; pub mod render_helpers; +pub mod rubber_band; pub mod swipe_tracker; pub mod ui; pub mod utils; diff --git a/src/rubber_band.rs b/src/rubber_band.rs new file mode 100644 index 00000000..1543d71e --- /dev/null +++ b/src/rubber_band.rs @@ -0,0 +1,39 @@ +#[derive(Debug, Clone, Copy)] +pub struct RubberBand { + pub stiffness: f64, + pub limit: f64, +} + +impl RubberBand { + pub fn band(&self, x: f64) -> f64 { + let c = self.stiffness; + let d = self.limit; + + (1. - (1. / (x * c / d + 1.))) * d + } + + pub fn derivative(&self, x: f64) -> f64 { + let c = self.stiffness; + let d = self.limit; + + c * d * d / (c * x + d).powi(2) + } + + pub fn clamp(&self, min: f64, max: f64, x: f64) -> f64 { + let clamped = x.clamp(min, max); + let sign = if x < clamped { -1. } else { 1. }; + let diff = (x - clamped).abs(); + + clamped + sign * self.band(diff) + } + + pub fn clamp_derivative(&self, min: f64, max: f64, x: f64) -> f64 { + if min <= x && x <= max { + return 1.; + } + + let clamped = x.clamp(min, max); + let diff = (x - clamped).abs(); + self.derivative(diff) + } +} |
