I’m creating an app using relm4 and gtk-rs crates in Rust. I creates a textview so I can show app’s logs and info in it, I want it to auto scroll to the end when new lines are added. Here’s my update() function:
fn update(&mut self, message: Self::Input, _sender: ComponentSender<Self>) {
match message {
SimpleTextViewInput::Append(text) => {
let mut end_iter = self.buffer.end_iter();
let log_level = ["ERROR", "WARN", "INFO", "DEBUG", "TRACE"]
.iter()
.find(|&&level| text.contains(&format!("[{}]", level)))
.unwrap_or(&"INFO");
self.buffer.insert_with_tags_by_name(
&mut end_iter,
&text.to_string(),
&[log_level],
);
// Log rotation
let line_count = self.buffer.line_count();
if line_count > MAX_LINES {
let lines_to_remove = line_count - MAX_LINES;
let mut start = self.buffer.start_iter();
let mut end = self.buffer.iter_at_line(lines_to_remove).unwrap();
self.buffer.delete(&mut start, &mut end);
}
// Create a mark at the end of the buffer
let end_iter = self.buffer.end_iter();
let end_mark = self.buffer.create_mark(Some("end"), &end_iter, false);
// Scroll to make the end mark visible
self.view.scroll_mark_onscreen(&end_mark);
// Delete the mark after scrolling
self.buffer.delete_mark(&end_mark);
}
}
}
Auto-scrolling doesn’t work and this line self.view.scroll_mark_onscreen(&end_mark); causes errors:
When inserting text in a textview, there are some validation done in background, asynchronously. Only after the validation is done, the scrolling is processed.
Creating and deleting marks using the same name seems to confuse the textview… (I suppose a reference is kept for the async processing, but the succession of create/delete leads to some kind of race condition).
Usually, we create marks only once, no need to recreate them every time.