mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 16:26:08 +03:00
terminal: do not scroll viewport if new lines are added while scrolled
This commit is contained in:
@ -1545,14 +1545,15 @@ fn scrollDelta(self: *Screen, delta: isize, grow: bool) !void {
|
|||||||
// Add our delta to our viewport. If we're less than the max currently
|
// Add our delta to our viewport. If we're less than the max currently
|
||||||
// allowed to scroll to the bottom (the end of the history), then we
|
// allowed to scroll to the bottom (the end of the history), then we
|
||||||
// have space and we just return.
|
// have space and we just return.
|
||||||
self.viewport += @as(usize, @intCast(delta));
|
const start_viewport_bottom = self.viewportIsBottom();
|
||||||
if (self.viewport <= self.history) return;
|
const viewport = self.history + @as(usize, @intCast(delta));
|
||||||
|
if (viewport <= self.history) return;
|
||||||
|
|
||||||
// If our viewport is past the top of our history then we potentially need
|
// If our viewport is past the top of our history then we potentially need
|
||||||
// to write more blank rows. If our viewport is more than our rows written
|
// to write more blank rows. If our viewport is more than our rows written
|
||||||
// then we expand out to there.
|
// then we expand out to there.
|
||||||
const rows_written = self.rowsWritten();
|
const rows_written = self.rowsWritten();
|
||||||
const viewport_bottom = self.viewport + self.rows;
|
const viewport_bottom = viewport + self.rows;
|
||||||
if (viewport_bottom <= rows_written) return;
|
if (viewport_bottom <= rows_written) return;
|
||||||
|
|
||||||
// The number of new rows we need is the number of rows off our
|
// The number of new rows we need is the number of rows off our
|
||||||
@ -1598,7 +1599,6 @@ fn scrollDelta(self: *Screen, delta: isize, grow: bool) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.viewport -= rows_to_delete;
|
|
||||||
self.storage.deleteOldest(rows_to_delete * (self.cols + 1));
|
self.storage.deleteOldest(rows_to_delete * (self.cols + 1));
|
||||||
break :deleted rows_to_delete;
|
break :deleted rows_to_delete;
|
||||||
} else 0;
|
} else 0;
|
||||||
@ -1632,6 +1632,18 @@ fn scrollDelta(self: *Screen, delta: isize, grow: bool) !void {
|
|||||||
(rows_written_final - 1) * (self.cols + 1),
|
(rows_written_final - 1) * (self.cols + 1),
|
||||||
self.cols + 1,
|
self.cols + 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (start_viewport_bottom) {
|
||||||
|
// If our viewport is on the bottom, we always update the viewport
|
||||||
|
// to the latest so that it remains in view.
|
||||||
|
self.viewport = self.history;
|
||||||
|
} else if (rows_deleted > 0) {
|
||||||
|
// If our viewport is NOT on the bottom, we want to keep our viewport
|
||||||
|
// where it was so that we don't jump around. However, we need to
|
||||||
|
// subtract the final rows written if we had to delete rows since
|
||||||
|
// that changes the viewport offset.
|
||||||
|
self.viewport -|= rows_deleted;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The options for where you can jump to on the screen.
|
/// The options for where you can jump to on the screen.
|
||||||
@ -2969,6 +2981,54 @@ test "Screen: scrollback empty" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "Screen: scrollback doesn't move viewport if not at bottom" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
|
||||||
|
var s = try init(alloc, 3, 5, 3);
|
||||||
|
defer s.deinit();
|
||||||
|
try s.testWriteString("1ABCD\n2EFGH\n3IJKL\n4ABCD\n5EFGH");
|
||||||
|
|
||||||
|
// First test: we scroll up by 1, so we're not at the bottom anymore.
|
||||||
|
try s.scroll(.{ .delta = -1 });
|
||||||
|
try testing.expect(!s.viewportIsBottom());
|
||||||
|
{
|
||||||
|
var contents = try s.testString(alloc, .viewport);
|
||||||
|
defer alloc.free(contents);
|
||||||
|
try testing.expectEqualStrings("2EFGH\n3IJKL\n4ABCD", contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, we scroll back down by 1, this grows the scrollback but we
|
||||||
|
// shouldn't move.
|
||||||
|
try s.scroll(.{ .delta = 1 });
|
||||||
|
try testing.expect(!s.viewportIsBottom());
|
||||||
|
{
|
||||||
|
var contents = try s.testString(alloc, .viewport);
|
||||||
|
defer alloc.free(contents);
|
||||||
|
try testing.expectEqualStrings("2EFGH\n3IJKL\n4ABCD", contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scroll again, this clears scrollback so we should move viewports
|
||||||
|
// but still see the same thing since our original view fits.
|
||||||
|
try s.scroll(.{ .delta = 1 });
|
||||||
|
try testing.expect(!s.viewportIsBottom());
|
||||||
|
{
|
||||||
|
var contents = try s.testString(alloc, .viewport);
|
||||||
|
defer alloc.free(contents);
|
||||||
|
try testing.expectEqualStrings("2EFGH\n3IJKL\n4ABCD", contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scroll again, this again goes into scrollback but is now deleting
|
||||||
|
// what we were looking at. We should see changes.
|
||||||
|
try s.scroll(.{ .delta = 1 });
|
||||||
|
try testing.expect(!s.viewportIsBottom());
|
||||||
|
{
|
||||||
|
var contents = try s.testString(alloc, .viewport);
|
||||||
|
defer alloc.free(contents);
|
||||||
|
try testing.expectEqualStrings("3IJKL\n4ABCD\n5EFGH", contents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "Screen: scrolling moves selection" {
|
test "Screen: scrolling moves selection" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
@ -3075,13 +3135,9 @@ test "Screen: scrolling with scrollback available doesn't move selection" {
|
|||||||
|
|
||||||
// Scroll down, this sends us off the scrollback
|
// Scroll down, this sends us off the scrollback
|
||||||
try s.scroll(.{ .delta = 2 });
|
try s.scroll(.{ .delta = 2 });
|
||||||
try testing.expect(s.viewportIsBottom());
|
|
||||||
|
|
||||||
// Selection should move since we went down 2
|
// Selection should be gone since we selected a line that went off.
|
||||||
try testing.expectEqual(Selection{
|
try testing.expect(s.selection == null);
|
||||||
.start = .{ .x = 0, .y = 0 },
|
|
||||||
.end = .{ .x = s.cols - 1, .y = 0 },
|
|
||||||
}, s.selection.?);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
// Test our contents rotated
|
// Test our contents rotated
|
||||||
|
Reference in New Issue
Block a user