118 lines
5.5 KiB
Diff
118 lines
5.5 KiB
Diff
commit 389276be7ecf02826defc58e77e4e5a8868ee4a3 (HEAD -> main)
|
|
Author: Tim Culverhouse <tim@timculverhouse.com>
|
|
Date: Wed Mar 12 11:03:27 2025 -0500
|
|
|
|
scroll: translate non-precision to precision
|
|
|
|
Some wheel mice are capable of reporting fractional wheel ticks. These
|
|
mice don't necessarily report a corresponding precision scroll start
|
|
event, at least in Wayland + GTK. We can treat all discrete (ie
|
|
non-precision) events as the number of wheel ticks - for wheel mice,
|
|
yoff will be "1.0" per tick, while precision wheel mice may report
|
|
fractional values. This unifies handling of scroll events by normalizing
|
|
all events to "pixels to scroll".
|
|
|
|
We now report `mouse-scroll-multiplier` wheel or arrow events per wheel
|
|
tick (or per accumulated cell height). This means that applications
|
|
which subscribe to mouse button events will receive (by default) three
|
|
wheel events per wheel tick. For precision scrolls, they will receive
|
|
one wheel tick per line of scroll. In my opinion, this provides the best
|
|
user experience while also allowing customization of how much a
|
|
wheel tick should scroll
|
|
|
|
diff --git a/src/Surface.zig b/src/Surface.zig
|
|
index 10e8428e1459..bc38c6f8b166 100644
|
|
--- a/src/Surface.zig
|
|
+++ b/src/Surface.zig
|
|
@@ -2325,13 +2325,6 @@ const ScrollAmount = struct {
|
|
pub fn magnitude(self: ScrollAmount) usize {
|
|
return @abs(self.delta);
|
|
}
|
|
-
|
|
- pub fn multiplied(self: ScrollAmount, multiplier: f64) ScrollAmount {
|
|
- const delta_f64: f64 = @floatFromInt(self.delta);
|
|
- const delta_adjusted: f64 = delta_f64 * multiplier;
|
|
- const delta_isize: isize = @intFromFloat(@round(delta_adjusted));
|
|
- return .{ .delta = delta_isize };
|
|
- }
|
|
};
|
|
|
|
/// Mouse scroll event. Negative is down, left. Positive is up, right.
|
|
@@ -2355,20 +2348,18 @@ pub fn scrollCallback(
|
|
if (self.mouse.hidden) self.showMouse();
|
|
|
|
const y: ScrollAmount = if (yoff == 0) .{} else y: {
|
|
- // Non-precision scrolls don't accumulate. We cast that raw yoff to an isize and interpret
|
|
- // it as the number of lines to scroll.
|
|
- if (!scroll_mods.precision) {
|
|
- // Calculate our magnitude of scroll. This is a direct multiple of yoff
|
|
- const y_delta_isize: isize = @intFromFloat(@round(yoff));
|
|
- break :y .{ .delta = y_delta_isize };
|
|
- }
|
|
-
|
|
- // Precision scrolling is more complicated. We need to maintain state
|
|
- // to build up a pending scroll amount if we're only scrolling by a
|
|
- // tiny amount so that we can scroll by a full row when we have enough.
|
|
+ // We use cell_size to determine if we have accumulated enough to trigger a scroll
|
|
+ const cell_size: f64 = @floatFromInt(self.size.cell.height);
|
|
|
|
- // Adjust our offset by the multiplier
|
|
- const yoff_adjusted: f64 = yoff;
|
|
+ // If we have precision scroll, yoff is the number of pixels to scroll. In non-precision
|
|
+ // scroll, yoff is the number of wheel ticks. Some mice are capable of reporting fractional
|
|
+ // wheel ticks, which don't necessarily get reported as precision scrolls. We normalize all
|
|
+ // scroll events to pixels by multiplying the wheel tick value and the cell size. This means
|
|
+ // that a wheel tick of 1 results in single scroll event.
|
|
+ const yoff_adjusted: f64 = if (scroll_mods.precision)
|
|
+ yoff
|
|
+ else
|
|
+ yoff * cell_size * self.config.mouse_scroll_multiplier;
|
|
|
|
// Add our previously saved pending amount to the offset to get the
|
|
// new offset value. The signs of the pending and yoff should match
|
|
@@ -2379,7 +2370,6 @@ pub fn scrollCallback(
|
|
|
|
// If the new offset is less than a single unit of scroll, we save
|
|
// the new pending value and do not scroll yet.
|
|
- const cell_size: f64 = @floatFromInt(self.size.cell.height);
|
|
if (@abs(poff) < cell_size) {
|
|
self.mouse.pending_scroll_y = poff;
|
|
break :y .{};
|
|
@@ -2432,12 +2422,6 @@ pub fn scrollCallback(
|
|
try self.setSelection(null);
|
|
}
|
|
|
|
- // We never use a multiplier for precision scrolls.
|
|
- const multiplier: f64 = if (scroll_mods.precision)
|
|
- 1.0
|
|
- else
|
|
- self.config.mouse_scroll_multiplier;
|
|
-
|
|
// If we're in alternate screen with alternate scroll enabled, then
|
|
// we convert to cursor keys. This only happens if we're:
|
|
// (1) alt screen (2) no explicit mouse reporting and (3) alt
|
|
@@ -2464,9 +2448,7 @@ pub fn scrollCallback(
|
|
.down_left => "\x1b[B",
|
|
};
|
|
};
|
|
- // We multiple by the scroll multiplier when reporting arrows
|
|
- const multiplied = y.multiplied(multiplier);
|
|
- for (0..multiplied.magnitude()) |_| {
|
|
+ for (0..y.magnitude()) |_| {
|
|
self.io.queueMessage(.{ .write_stable = seq }, .locked);
|
|
}
|
|
}
|
|
@@ -2502,11 +2484,9 @@ pub fn scrollCallback(
|
|
}
|
|
|
|
if (y.delta != 0) {
|
|
- // We multiply by the multiplier when scrolling the viewport
|
|
- const multiplied = y.multiplied(multiplier);
|
|
// Modify our viewport, this requires a lock since it affects
|
|
// rendering. We have to switch signs here because our delta
|
|
// is negative down but our viewport is positive down.
|
|
- try self.io.terminal.scrollViewport(.{ .delta = multiplied.delta * -1 });
|
|
+ try self.io.terminal.scrollViewport(.{ .delta = y.delta * -1 });
|
|
}
|
|
}
|