Private
Public Access
1
0

transcriptview perf: only draw the items that are actually visible.

This commit is contained in:
2025-06-16 20:09:56 -07:00
parent 2db0e3136e
commit c70ae00d5b
2 changed files with 69 additions and 18 deletions

View File

@@ -4,12 +4,25 @@ using Gee;
private class TranscriptDrawingArea : Widget private class TranscriptDrawingArea : Widget
{ {
public bool show_sender = true; public bool show_sender = true;
public Adjustment? viewport {
get {
return _viewport;
}
set {
_viewport = value;
queue_draw();
}
}
private ArrayList<Message> _messages = new ArrayList<Message>(); private ArrayList<Message> _messages = new ArrayList<Message>();
private ArrayList<ChatItemLayout> _chat_items = new ArrayList<ChatItemLayout>(); private ArrayList<ChatItemLayout> _chat_items = new ArrayList<ChatItemLayout>();
private Adjustment? _viewport = null;
private const float bubble_margin = 18.0f; private const float bubble_margin = 18.0f;
private const bool debug_viewport = false;
public TranscriptDrawingArea() { public TranscriptDrawingArea() {
add_css_class("transcript-drawing-area"); add_css_class("transcript-drawing-area");
} }
@@ -51,33 +64,63 @@ private class TranscriptDrawingArea : Widget
} }
public override void snapshot(Snapshot snapshot) { public override void snapshot(Snapshot snapshot) {
float y_offset = 0; const int viewport_y_padding = 50;
var container_width = get_width(); var viewport_rect = Graphene.Rect() {
origin = Graphene.Point() { x = 0, y = ((int)viewport.value - viewport_y_padding) },
size = Graphene.Size() { width = get_width(), height = (int)viewport.page_size + (viewport_y_padding * 2) }
};
if (debug_viewport) {
// Draw viewport outline for debugging
var color = Gdk.RGBA() { red = 1.0f, green = 0, blue = 0, alpha = 1.0f };
snapshot.append_border(
Gsk.RoundedRect().init_from_rect(viewport_rect, 0),
{ 1, 1, 1, 1 },
{ color, color, color, color }
);
}
// Draw each item in reverse order, since the messages are in reverse order // Draw each item in reverse order, since the messages are in reverse order
float y_offset = 0;
int container_width = get_width();
for (int i = _chat_items.size - 1; i >= 0; i--) { for (int i = _chat_items.size - 1; i >= 0; i--) {
var chat_item = _chat_items[i]; var chat_item = _chat_items[i];
var item_width = chat_item.get_width(); var item_width = chat_item.get_width();
var item_height = chat_item.get_height(); var item_height = chat_item.get_height();
snapshot.save(); var origin = Graphene.Point() {
// Flip the y-axis, since our parent is upside down (so newest messages are at the bottom)
snapshot.scale(1.0f, -1.0f);
// Translate to the correct position
snapshot.translate(Graphene.Point() {
x = (chat_item.from_me ? (container_width - item_width - bubble_margin) : bubble_margin), x = (chat_item.from_me ? (container_width - item_width - bubble_margin) : bubble_margin),
y = y_offset y = y_offset
}); };
// Undo the y-axis flip, origin is top left var size = Graphene.Size() {
snapshot.translate(Graphene.Point() { x = 0, y = -item_height }); width = item_width,
height = item_height
};
chat_item.draw(snapshot); var rect = Graphene.Rect() {
snapshot.restore(); origin = origin,
size = size
};
y_offset -= item_height + chat_item.vertical_padding; // Skip drawing if this item is not in the viewport
if (viewport_rect.intersection(rect, null)) {
snapshot.save();
// Translate to the correct position
snapshot.translate(origin);
// Flip the y-axis, since our parent is upside down (so newest messages are at the bottom)
snapshot.scale(1.0f, -1.0f);
// Undo the y-axis flip, origin is top left
snapshot.translate(Graphene.Point() { x = 0, y = -item_height });
chat_item.draw(snapshot);
snapshot.restore();
}
y_offset += item_height + chat_item.vertical_padding;
} }
} }

View File

@@ -17,8 +17,10 @@ public class TranscriptView : Adw.Bin
_model = value; _model = value;
if (value != null) { if (value != null) {
// Reset scroll position // Reset scroll position by updating the existing adjustment
scrolled_window.vadjustment = new Gtk.Adjustment(0, 0, 0, 0, 0, 0); scrolled_window.vadjustment.value = 0;
scrolled_window.vadjustment.upper = 0;
scrolled_window.vadjustment.page_size = 0;
weak TranscriptView self = this; weak TranscriptView self = this;
messages_changed_handler_id = value.messages_changed.connect(() => { messages_changed_handler_id = value.messages_changed.connect(() => {
@@ -56,8 +58,14 @@ public class TranscriptView : Adw.Bin
scrolled_window.set_child(transcript_drawing_area); scrolled_window.set_child(transcript_drawing_area);
scrolled_window.add_css_class("message-list-scroller"); scrolled_window.add_css_class("message-list-scroller");
transcript_drawing_area.viewport = scrolled_window.vadjustment;
container.set_content(scrolled_window); container.set_content(scrolled_window);
// Connect to the adjustment's value_changed signal
scrolled_window.vadjustment.value_changed.connect(() => {
transcript_drawing_area.viewport = scrolled_window.vadjustment;
});
var header_bar = new Adw.HeaderBar(); var header_bar = new Adw.HeaderBar();
title_label.single_line_mode = true; title_label.single_line_mode = true;
title_label.ellipsize = Pango.EllipsizeMode.END; title_label.ellipsize = Pango.EllipsizeMode.END;