Skip to content

Add option to reverse scroll direction on middle-click vector scrolling - #2

Merged
anibalribeiro merged 6 commits into
mainfrom
feature/reverse-scroll-direction
Sep 3, 2026
Merged

Add option to reverse scroll direction on middle-click vector scrolling#2
anibalribeiro merged 6 commits into
mainfrom
feature/reverse-scroll-direction

Conversation

@anibalribeiro

@anibalribeiro anibalribeiro commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Closes #1.

Summary

Adds two independent settings that reverse the direction of middle-click vector (autoscroll) scrolling, separately for the vertical and horizontal axes. Both default to off, so existing installs keep today's behavior.

Requested in #1: an option so that middle-click-dragging down scrolls the page down (and vice versa), rather than the default mapping.

Changes

  • ScrollEngine moved to its own target — the autoscroll delta math had no test coverage because it lived in the WinMice executable target. It now sits in a library target alongside SwipeGesturePoster, with a ScrollEngineTests target. This is a mechanical move with no behavior change, kept as its own commit.
  • ScrollEngine — new reverseVertical / reverseHorizontal flags (default false) that flip the sign of the wheel deltas in tick(offset:). The sign is applied before the sub-pixel remainder is added, so the carryover stays in the same sign convention as the deltas it accumulates toward. Flipping the sum instead would alternate the carryover's sign each tick and stall slow drags at zero — there's a test for exactly that.
  • AppSettings — new persisted reverseScrollDirectionVertical / reverseScrollDirectionHorizontal preferences (default false), registered in Key.all so Restore Defaults clears them.
  • main.swiftapplySettings() copies both flags onto the engine live, so toggling takes effect immediately without a restart, and even mid-scroll (same pattern as scroll.speed).
  • Scrolling settings pane — new Direction section with two toggles.
  • README — documents both options under Scrolling.

Behavior

Both toggles off leaves today's mapping untouched. Turning on Reverse vertical makes dragging down scroll up; Reverse horizontal makes dragging right scroll left. Each axis is independent, and enabling one provably does not disturb the other. Choices persist across launches and are cleared by Restore Defaults.

Tests

ScrollEngineTests covers 11 cases: the four baseline directions and the dead zone (characterizing pre-existing behavior), each reverse flag's effect, that each flag leaves the other axis byte-identical, both flags at once, and the reversed sub-pixel carryover on slow drags.

Each new test was watched failing before the code that satisfies it existed. The two flag tests failed on their assertions against a stubbed no-op property, and the carryover test was verified by mutating the implementation to the plausible buggy variant, where the slow drag stalled at 0 while the other tests still passed.

Deliberate non-fix: toggling mid-scroll

If a flag is flipped while a fractional remainder is pending, the carryover briefly opposes the new direction. This is left alone on purpose. Because |remainder| < 1 always, it can never drag a truncated value across zero into the opposite sign, so the worst case is a single ~16 ms tick of sub-pixel lag. Resetting the remainder in applySettings() would also clear it on unrelated writes such as the speed slider, and detecting a genuine flag change is more machinery than a sub-pixel justifies.

Verification

swift test — 16 tests, 0 failures. swift build — clean under -warnings-as-errors. ./scripts/build-app.sh — bundles and signs dist/WinMice.app.

Reviewed by two independent reviewers. Bugbot found no bugs. A senior-reviewer pass independently confirmed the sign-flip placement and both axes' wheel conventions against the y-up coordinate system, and mutation-tested the engine with five injected bugs, all of which the suite caught. Its minor feedback is applied in the final commit: the wheel2 baseline inversion is now written separately from the user flag so that "reverse means -1" reads uniformly across both axes, the cross-axis tests unwrap their deltas so they cannot pass vacuously if both sides go nil, both flags are covered together, and two engine tuning knobs that no consumer reads were narrowed back from public.

Manual verification still recommended

The toggles' appearance in the settings window and the end-to-end feel of reversed scrolling were not exercised here, since both need a GUI session with accessibility permissions granted. A quick check of both toggles against a scrollable view before merge is worthwhile, since the requester's underlying complaint is about feel rather than sign convention.

Made with Cursor

Anibal Ribeiro and others added 5 commits September 3, 2026 23:45
The autoscroll delta math had no test coverage because it lived in the
WinMice executable target. Move it into a library target alongside
SwipeGesturePoster so the direction mapping can be tested directly.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Requested in #1: dragging down scrolled the opposite way to what some
users expect. Both axes are exposed independently and default to off, so
existing installs are unaffected.

Co-authored-by: Cursor <cursoragent@cursor.com>
@anibalribeiro
anibalribeiro force-pushed the feature/reverse-scroll-direction branch from c2457b7 to 3983db7 Compare September 3, 2026 21:49
Keep the wheel2 baseline inversion separate from the user's reverse flag
so that "reverse means -1" reads uniformly across both axes, unwrap the
deltas in the cross-axis tests so they cannot pass vacuously, cover both
flags at once, and narrow two engine knobs that no consumer reads.

Co-authored-by: Cursor <cursoragent@cursor.com>
@anibalribeiro
anibalribeiro merged commit 20d1cf4 into main Sep 3, 2026
2 checks passed
@anibalribeiro
anibalribeiro deleted the feature/reverse-scroll-direction branch September 3, 2026 22:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request] Ability to reverse scroll direction when middle click vector scrolling

1 participant