transcriptview perf: only draw the items that are actually visible.
This commit is contained in:
@@ -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");
|
||||||
}
|
}
|
||||||
@@ -49,35 +62,65 @@ private class TranscriptDrawingArea : Widget
|
|||||||
base.size_allocate(width, height, baseline);
|
base.size_allocate(width, height, baseline);
|
||||||
recompute_message_layouts();
|
recompute_message_layouts();
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user