diff options
| author | Bernardo Kuri <github.com@bkuri.com> | 2025-08-18 23:51:32 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-19 05:51:32 +0000 |
| commit | 5ea9092a4954f8c5cd795f0ebec910666b367d5f (patch) | |
| tree | c9a6aec2fe9a2f6f1692de3c66ec8ac82c890bbf /niri-config/src | |
| parent | 43a2648e579fc81366fc81b15f834c9c9dff119b (diff) | |
| download | niri-5ea9092a4954f8c5cd795f0ebec910666b367d5f.tar.gz niri-5ea9092a4954f8c5cd795f0ebec910666b367d5f.tar.bz2 niri-5ea9092a4954f8c5cd795f0ebec910666b367d5f.zip | |
Add per-axis scroll speed config for input devices (#2109)
* Add per-axis scroll speed config for input devices.
Accepts negative values to inverse scroll direction.
Properly complements/overrides global `scroll-direction` setting.
Includes docs and tests.
* Update per-axis scroll factor implementation after testing
- Refined configuration structure in niri-config
- Updated input handling to use per-axis scroll factors
- Added comprehensive test snapshots
- Updated documentation with per-axis examples
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Simplify per-axis scroll factor implementation per review feedback
- Make documentation concise and clear
- Remove unnecessary comments and test helper functions
- Use inline snapshots for tests
- Rename get_factors() to h_v_factors() for clarity
- Remove unnecessary .clone() calls (ScrollFactor is Copy)
- Reduce test count to essential cases only
- Fix comment about window factor override behavior
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Remove unnecessary ScrollFactor::new() helper function
The maintainer prefers minimal code, so removing this helper and
constructing ScrollFactor directly in tests.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Fix scroll factor behavior - all settings now multiply with window factor
Per maintainer feedback, both combined and per-axis scroll settings
should multiply with the window-specific scroll factor, not override it.
This ensures consistent behavior regardless of configuration method.
Also removed the now-unused has_per_axis_override() method.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Final cleanup: remove redundant comments and unused snapshot files
- Removed unused snapshot files (now using inline snapshots)
- Removed redundant inline comments in tests
- Simplified test descriptions to be more concise
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Convert scroll factor parsing tests to use assert_debug_snapshot
Updates parse_scroll_factor_combined, parse_scroll_factor_split, and
parse_scroll_factor_partial tests to use assert_debug_snapshot instead
of manual assert_eq comparisons, as requested in PR review.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Convert to inline snapshots as requested
- Convert all scroll factor parsing tests to use inline snapshots instead of external files
- Remove external snapshot files to keep test directory clean
- All tests still pass with inline snapshot assertions
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Fix missed assert_eq in parse_scroll_factor_mixed test
Converts the remaining assert_eq calls to assert_debug_snapshot
with inline snapshots in the mixed syntax test function.
Also fixes raw string delimiters from ### to #.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Convert scroll_factor_h_v_factors test to use assert_debug_snapshot
Makes all scroll factor tests consistent by using snapshots instead
of assert_eq for better maintainability.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fixes
---------
Co-authored-by: Bernardo Kuri <github@bkuri.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Ivan Molodetskikh <yalterz@gmail.com>
Diffstat (limited to 'niri-config/src')
| -rw-r--r-- | niri-config/src/lib.rs | 282 |
1 files changed, 272 insertions, 10 deletions
diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs index 16ec6dca..dbd6b5ea 100644 --- a/niri-config/src/lib.rs +++ b/niri-config/src/lib.rs @@ -191,6 +191,25 @@ pub enum TrackLayout { Window, } +#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)] +pub struct ScrollFactor { + #[knuffel(argument)] + pub base: Option<FloatOrInt<0, 100>>, + #[knuffel(property)] + pub horizontal: Option<FloatOrInt<-100, 100>>, + #[knuffel(property)] + pub vertical: Option<FloatOrInt<-100, 100>>, +} + +impl ScrollFactor { + pub fn h_v_factors(&self) -> (f64, f64) { + let base_value = self.base.map(|f| f.0).unwrap_or(1.0); + let h = self.horizontal.map(|f| f.0).unwrap_or(base_value); + let v = self.vertical.map(|f| f.0).unwrap_or(base_value); + (h, v) + } +} + #[derive(knuffel::Decode, Debug, Default, PartialEq)] pub struct Touchpad { #[knuffel(child)] @@ -227,8 +246,8 @@ pub struct Touchpad { pub disabled_on_external_mouse: bool, #[knuffel(child)] pub middle_emulation: bool, - #[knuffel(child, unwrap(argument))] - pub scroll_factor: Option<FloatOrInt<0, 100>>, + #[knuffel(child)] + pub scroll_factor: Option<ScrollFactor>, } #[derive(knuffel::Decode, Debug, Default, PartialEq)] @@ -251,8 +270,8 @@ pub struct Mouse { pub left_handed: bool, #[knuffel(child)] pub middle_emulation: bool, - #[knuffel(child, unwrap(argument))] - pub scroll_factor: Option<FloatOrInt<0, 100>>, + #[knuffel(child)] + pub scroll_factor: Option<ScrollFactor>, } #[derive(knuffel::Decode, Debug, Default, PartialEq)] @@ -4055,6 +4074,237 @@ mod tests { } #[test] + fn parse_scroll_factor_combined() { + // Test combined scroll-factor syntax + let parsed = do_parse( + r#" + input { + mouse { + scroll-factor 2.0 + } + touchpad { + scroll-factor 1.5 + } + } + "#, + ); + + assert_debug_snapshot!(parsed.input.mouse.scroll_factor, @r#" + Some( + ScrollFactor { + base: Some( + FloatOrInt( + 2.0, + ), + ), + horizontal: None, + vertical: None, + }, + ) + "#); + assert_debug_snapshot!(parsed.input.touchpad.scroll_factor, @r#" + Some( + ScrollFactor { + base: Some( + FloatOrInt( + 1.5, + ), + ), + horizontal: None, + vertical: None, + }, + ) + "#); + } + + #[test] + fn parse_scroll_factor_split() { + // Test split horizontal/vertical syntax + let parsed = do_parse( + r#" + input { + mouse { + scroll-factor horizontal=2.0 vertical=-1.0 + } + touchpad { + scroll-factor horizontal=-1.5 vertical=0.5 + } + } + "#, + ); + + assert_debug_snapshot!(parsed.input.mouse.scroll_factor, @r#" + Some( + ScrollFactor { + base: None, + horizontal: Some( + FloatOrInt( + 2.0, + ), + ), + vertical: Some( + FloatOrInt( + -1.0, + ), + ), + }, + ) + "#); + assert_debug_snapshot!(parsed.input.touchpad.scroll_factor, @r#" + Some( + ScrollFactor { + base: None, + horizontal: Some( + FloatOrInt( + -1.5, + ), + ), + vertical: Some( + FloatOrInt( + 0.5, + ), + ), + }, + ) + "#); + } + + #[test] + fn parse_scroll_factor_partial() { + // Test partial specification (only one axis) + let parsed = do_parse( + r#" + input { + mouse { + scroll-factor horizontal=2.0 + } + touchpad { + scroll-factor vertical=-1.5 + } + } + "#, + ); + + assert_debug_snapshot!(parsed.input.mouse.scroll_factor, @r#" + Some( + ScrollFactor { + base: None, + horizontal: Some( + FloatOrInt( + 2.0, + ), + ), + vertical: None, + }, + ) + "#); + assert_debug_snapshot!(parsed.input.touchpad.scroll_factor, @r#" + Some( + ScrollFactor { + base: None, + horizontal: None, + vertical: Some( + FloatOrInt( + -1.5, + ), + ), + }, + ) + "#); + } + + #[test] + fn parse_scroll_factor_mixed() { + // Test mixed base + override syntax + let parsed = do_parse( + r#" + input { + mouse { + scroll-factor 2 vertical=-1 + } + touchpad { + scroll-factor 1.5 horizontal=3 + } + } + "#, + ); + + assert_debug_snapshot!(parsed.input.mouse.scroll_factor, @r#" + Some( + ScrollFactor { + base: Some( + FloatOrInt( + 2.0, + ), + ), + horizontal: None, + vertical: Some( + FloatOrInt( + -1.0, + ), + ), + }, + ) + "#); + assert_debug_snapshot!(parsed.input.touchpad.scroll_factor, @r#" + Some( + ScrollFactor { + base: Some( + FloatOrInt( + 1.5, + ), + ), + horizontal: Some( + FloatOrInt( + 3.0, + ), + ), + vertical: None, + }, + ) + "#); + } + + #[test] + fn scroll_factor_h_v_factors() { + let sf = ScrollFactor { + base: Some(FloatOrInt(2.0)), + horizontal: None, + vertical: None, + }; + assert_debug_snapshot!(sf.h_v_factors(), @r#" + ( + 2.0, + 2.0, + ) + "#); + + let sf = ScrollFactor { + base: None, + horizontal: Some(FloatOrInt(3.0)), + vertical: Some(FloatOrInt(-1.0)), + }; + assert_debug_snapshot!(sf.h_v_factors(), @r#" + ( + 3.0, + -1.0, + ) + "#); + + let sf = ScrollFactor { + base: Some(FloatOrInt(2.0)), + horizontal: Some(FloatOrInt(1.0)), + vertical: None, + }; + assert_debug_snapshot!(sf.h_v_factors(), @r" + ( + 1.0, + 2.0, + ) + "); + } + + #[test] fn parse() { let parsed = do_parse( r##" @@ -4370,9 +4620,15 @@ mod tests { disabled_on_external_mouse: true, middle_emulation: false, scroll_factor: Some( - FloatOrInt( - 0.9, - ), + ScrollFactor { + base: Some( + FloatOrInt( + 0.9, + ), + ), + horizontal: None, + vertical: None, + }, ), }, mouse: Mouse { @@ -4394,9 +4650,15 @@ mod tests { left_handed: false, middle_emulation: true, scroll_factor: Some( - FloatOrInt( - 0.2, - ), + ScrollFactor { + base: Some( + FloatOrInt( + 0.2, + ), + ), + horizontal: None, + vertical: None, + }, ), }, trackpoint: Trackpoint { |
