Add configurable inactive dimming and viewport-paged scrolling

This commit is contained in:
Samuel Onoja committed 2026-09-26 09:41:55 +01:00
1 parent a0fd760737
commit 48ac963ffe
8 files changed
+607 -14

No files matched your search

+129
View File
@@ -0,0 +1,129 @@
use crate::config::InactiveDimSettings;
use ferese_layout::WindowId;
use std::time::Duration;
pub(crate) fn target(
settings: InactiveDimSettings,
focused: Option<WindowId>,
window: WindowId,
overview: bool,
) -> f64 {
if settings.enabled && !overview && focused.is_some() && focused != Some(window) {
settings.amount
} else {
0.0
}
}
/// A bounded, interruption-safe opacity transition; it never overshoots.
#[derive(Debug)]
pub(crate) struct DimAnimation {
pub current: f64,
start: f64,
target: f64,
elapsed: f64,
}
impl DimAnimation {
pub fn new(value: f64) -> Self {
Self {
current: value,
start: value,
target: value,
elapsed: 0.0,
}
}
pub fn advance(&mut self, target: f64, delta: Duration, duration_ms: f64) -> bool {
if self.target != target {
self.start = self.current;
self.target = target;
self.elapsed = 0.0;
}
if duration_ms == 0.0 {
self.current = target;
return false;
}
self.elapsed += delta.as_secs_f64() * 1000.0;
let progress = (self.elapsed / duration_ms).min(1.0);
if progress == 1.0 {
self.current = target;
return false;
}
let eased = progress * progress * (3.0 - 2.0 * progress);
self.current = self.start + (target - self.start) * eased;
self.current != target
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn only_unfocused_windows_dim_and_overview_disables_it() {
let settings = InactiveDimSettings {
enabled: true,
amount: 0.15,
duration_ms: 150.0,
};
let left = WindowId(1);
let right = WindowId(2);
assert_eq!(target(settings, Some(right), left, false), 0.15);
assert_eq!(target(settings, Some(right), right, false), 0.0);
assert_eq!(target(settings, Some(right), left, true), 0.0);
assert_eq!(target(settings, None, left, false), 0.0);
assert_eq!(
target(
InactiveDimSettings {
enabled: false,
..settings
},
Some(right),
left,
false
),
0.0
);
assert_eq!(
target(
InactiveDimSettings {
amount: 0.0,
..settings
},
Some(right),
left,
false
),
0.0
);
}
#[test]
fn focus_transition_is_smooth_bounded_and_settles() {
let mut dim = DimAnimation::new(0.0);
assert!(dim.advance(0.15, Duration::from_millis(75), 150.0));
assert!((dim.current - 0.075).abs() < 1e-9);
assert!(!dim.advance(0.15, Duration::from_millis(75), 150.0));
assert_eq!(dim.current, 0.15);
assert!(!dim.advance(0.15, Duration::from_secs(10), 150.0));
}
#[test]
fn reversing_focus_starts_from_current_opacity() {
let mut dim = DimAnimation::new(0.0);
dim.advance(0.15, Duration::from_millis(75), 150.0);
let before = dim.current;
assert!(dim.advance(0.0, Duration::ZERO, 150.0));
assert_eq!(dim.current, before);
assert!(!dim.advance(0.0, Duration::from_millis(150), 150.0));
assert_eq!(dim.current, 0.0);
}
#[test]
fn zero_duration_snaps_for_reduced_motion() {
let mut dim = DimAnimation::new(0.15);
assert!(!dim.advance(0.0, Duration::ZERO, 0.0));
assert_eq!(dim.current, 0.0);
}
}