fix scroll

This commit is contained in:
2025-04-30 18:16:18 +02:00
parent e5ce246760
commit 5ba5a52c14

View File

@@ -16,6 +16,7 @@ pub struct TerminalText {
renderer: TextRenderer, renderer: TextRenderer,
dirty_regions: Vec<TextBounds>, dirty_regions: Vec<TextBounds>,
cursor: Cursor, cursor: Cursor,
max_scroll_lines: usize,
} }
// TODO // TODO
@@ -66,12 +67,13 @@ impl TerminalText {
index: 0, index: 0,
affinity: glyphon::Affinity::After, affinity: glyphon::Affinity::After,
}, },
max_scroll_lines: 100,
} }
} }
fn get_line_bounds(&self, line: usize) -> TextBounds { // Calculate the bounds delimited by the `start` and `end` lines
// Calculate the bounds for a specific line fn get_text_bounds(&self, start: usize, end: usize) -> TextBounds {
if line >= self.buffer.lines.len() { if start >= self.buffer.lines.len() || end > self.buffer.lines.len() {
// Return default bounds for invalid line // Return default bounds for invalid line
return TextBounds { return TextBounds {
left: 0, left: 0,
@@ -84,25 +86,24 @@ impl TerminalText {
let line_height = self.buffer.metrics().line_height; let line_height = self.buffer.metrics().line_height;
let viewport_width = self.viewport.resolution().width; let viewport_width = self.viewport.resolution().width;
// Use layout_runs to get information about wrapped lines
let layout_iter = self.buffer.layout_runs(); let layout_iter = self.buffer.layout_runs();
let mut top_line = 0; let mut top_line = 0;
let mut bottom_line = 0; let mut bottom_line = 0;
let mut found_line = false;
// Iterate through layout runs to find our target line let mut found_start_line = false;
let mut found_end_line = false;
for (visual_line_count, run) in layout_iter.enumerate() { for (visual_line_count, run) in layout_iter.enumerate() {
if run.line_i == line { if run.line_i == start && !found_start_line {
// This run belongs to our target line top_line = visual_line_count;
if !found_line { found_start_line = true;
// First run of our target line }
top_line = visual_line_count; if run.line_i == end {
found_line = true; if !found_end_line {
found_end_line = true;
} }
// Update bottom line for each run we find for this line
bottom_line = visual_line_count + 1; bottom_line = visual_line_count + 1;
} else if found_line { } else if found_end_line {
// We've processed all runs for our target line
break; break;
} }
} }
@@ -150,14 +151,7 @@ impl TerminalText {
} }
// Add 1 to include partially visible lines at the bottom // Add 1 to include partially visible lines at the bottom
let end_line = if self.buffer.lines.is_empty() { let end_line = last_logical_line;
0
} else {
// Make sure we include at least the last logical line that has content in the viewport
trace!("Last logical line: {last_logical_line}");
trace!("Number of lines: {}", self.buffer.lines.len());
(last_logical_line + 1).min(self.buffer.lines.len())
};
trace!("visible line range goes from {start_line} to {end_line}"); trace!("visible line range goes from {start_line} to {end_line}");
@@ -166,9 +160,8 @@ impl TerminalText {
fn ensure_visible_text_rendered(&mut self) { fn ensure_visible_text_rendered(&mut self) {
let (start_line, end_line) = self.get_visible_line_range(); let (start_line, end_line) = self.get_visible_line_range();
for i in start_line..end_line { self.dirty_regions
self.dirty_regions.push(self.get_line_bounds(i)); .push(self.get_text_bounds(start_line, end_line));
}
} }
fn merge_dirty_regions(&self) -> TextBounds { fn merge_dirty_regions(&self) -> TextBounds {
@@ -239,6 +232,14 @@ impl TerminalText {
F: FnOnce(&mut Self) -> R, F: FnOnce(&mut Self) -> R,
{ {
let result = operation(self); let result = operation(self);
let mut scroll = self.buffer.scroll();
if self.buffer.lines.len() > self.max_scroll_lines {
self.buffer.lines.remove(0);
self.cursor.line -= 1;
}
scroll.line = self.buffer.lines.len();
self.buffer.set_scroll(scroll);
self.buffer.shape_until_scroll(&mut self.font_system, false); self.buffer.shape_until_scroll(&mut self.font_system, false);
self.ensure_visible_text_rendered(); self.ensure_visible_text_rendered();
result result
@@ -309,26 +310,6 @@ impl TerminalText {
this.cursor.index = 0; this.cursor.index = 0;
// Create a new line with text after cursor // Create a new line with text after cursor
this.buffer.lines.insert(this.cursor.line, new_line); this.buffer.lines.insert(this.cursor.line, new_line);
// Only adjust scroll if cursor would be outside visible area
let mut scroll = this.buffer.scroll();
let current_scroll_line =
(scroll.vertical / this.buffer.metrics().line_height).floor() as usize;
let max_visible_lines =
(safe_casts::u32_to_f32_or_max(this.viewport.resolution().height)
/ this.buffer.metrics().line_height)
.floor() as usize;
// Only scroll if cursor would be below visible area
if this.cursor.line >= current_scroll_line + max_visible_lines {
scroll.vertical =
safe_casts::usize_to_f32_or_max(this.cursor.line - max_visible_lines + 1)
* this.buffer.metrics().line_height;
trace!("adjusting scroll to keep cursor visible: {:?}", scroll);
this.buffer.set_scroll(scroll);
} else {
trace!("keeping current scroll: {:?}", scroll);
}
}) })
} }
@@ -378,11 +359,6 @@ impl TerminalText {
ret = true; ret = true;
} }
if line > this.buffer.lines.len() {
let mut scroll = this.buffer.scroll();
scroll.vertical -= 1.0 * this.buffer.metrics().line_height;
this.buffer.set_scroll(scroll);
}
ret ret
}) })
} }