2025-04-30 19:12:00 -07:00
|
|
|
using Gtk;
|
|
|
|
|
using Gee;
|
|
|
|
|
|
|
|
|
|
private class MessageDrawingArea : Widget
|
|
|
|
|
{
|
|
|
|
|
private SortedSet<Message> _messages = new TreeSet<Message>();
|
2025-05-03 22:12:26 -07:00
|
|
|
private ArrayList<ChatItemLayout> _chat_items = new ArrayList<ChatItemLayout>();
|
2025-04-30 19:12:00 -07:00
|
|
|
|
|
|
|
|
private const float bubble_padding = 10.0f;
|
|
|
|
|
private const float bubble_margin = 18.0f;
|
|
|
|
|
|
|
|
|
|
public MessageDrawingArea() {
|
|
|
|
|
add_css_class("message-drawing-area");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void set_messages(SortedSet<Message> messages) {
|
|
|
|
|
_messages = messages;
|
|
|
|
|
recompute_message_layouts();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override SizeRequestMode get_request_mode() {
|
|
|
|
|
return SizeRequestMode.HEIGHT_FOR_WIDTH;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void measure(Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline)
|
|
|
|
|
{
|
|
|
|
|
if (orientation == Orientation.HORIZONTAL) {
|
|
|
|
|
// Horizontal, so we take up the full width provided
|
|
|
|
|
minimum = 0;
|
|
|
|
|
natural = for_size;
|
|
|
|
|
} else {
|
|
|
|
|
// compute total message layout height
|
|
|
|
|
float total_height = 0.0f;
|
2025-05-03 22:12:26 -07:00
|
|
|
_chat_items.foreach((chat_item) => {
|
|
|
|
|
total_height += chat_item.get_height() + bubble_padding;
|
2025-04-30 19:12:00 -07:00
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
minimum = (int)total_height;
|
|
|
|
|
natural = (int)total_height;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
minimum_baseline = -1;
|
|
|
|
|
natural_baseline = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void size_allocate(int width, int height, int baseline) {
|
|
|
|
|
base.size_allocate(width, height, baseline);
|
|
|
|
|
recompute_message_layouts();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void snapshot(Snapshot snapshot) {
|
|
|
|
|
var container_width = get_width();
|
|
|
|
|
float y_offset = 0;
|
2025-05-03 22:12:26 -07:00
|
|
|
_chat_items.foreach((chat_item) => {
|
2025-05-03 22:41:51 -07:00
|
|
|
var item_width = chat_item.get_width();
|
|
|
|
|
var item_height = chat_item.get_height();
|
2025-04-30 19:12:00 -07:00
|
|
|
|
|
|
|
|
snapshot.save();
|
|
|
|
|
|
|
|
|
|
// 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() {
|
2025-05-03 22:41:51 -07:00
|
|
|
x = (chat_item.from_me ? (container_width - item_width - bubble_margin) : bubble_margin),
|
2025-04-30 19:12:00 -07:00
|
|
|
y = y_offset
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Undo the y-axis flip, origin is top left
|
2025-05-03 22:41:51 -07:00
|
|
|
snapshot.translate(Graphene.Point() { x = 0, y = -item_height });
|
2025-04-30 19:12:00 -07:00
|
|
|
|
2025-05-03 22:12:26 -07:00
|
|
|
chat_item.draw(snapshot);
|
2025-04-30 19:12:00 -07:00
|
|
|
snapshot.restore();
|
|
|
|
|
|
2025-05-03 22:41:51 -07:00
|
|
|
y_offset -= item_height + bubble_padding;
|
2025-04-30 19:12:00 -07:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void recompute_message_layouts() {
|
|
|
|
|
var container_width = get_width();
|
|
|
|
|
float max_width = container_width * 0.90f;
|
|
|
|
|
|
2025-05-03 22:12:26 -07:00
|
|
|
_chat_items.clear();
|
|
|
|
|
|
2025-05-03 22:41:51 -07:00
|
|
|
var reversed_messages = _messages
|
|
|
|
|
.order_by((a, b) => b.date.compare(a.date)); // reverse order
|
|
|
|
|
|
|
|
|
|
DateTime? last_date = null;
|
|
|
|
|
reversed_messages.foreach((message) => {
|
|
|
|
|
// Remember everything in here is backwards.
|
|
|
|
|
|
|
|
|
|
if (last_date == null) {
|
|
|
|
|
last_date = message.date;
|
|
|
|
|
} else if (last_date.difference(message.date) > (TimeSpan.MINUTE * 25)) {
|
|
|
|
|
var date_item = new DateItemLayout(last_date.to_local().format("%b %d, %Y at %H:%M"), this, max_width);
|
|
|
|
|
_chat_items.add(date_item);
|
|
|
|
|
last_date = message.date;
|
|
|
|
|
}
|
2025-05-03 22:12:26 -07:00
|
|
|
|
2025-05-03 22:41:51 -07:00
|
|
|
var text_bubble = new TextBubbleLayout(message, this, max_width);
|
|
|
|
|
_chat_items.add(text_bubble);
|
2025-04-30 19:12:00 -07:00
|
|
|
|
2025-05-03 22:12:26 -07:00
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
|
2025-04-30 19:12:00 -07:00
|
|
|
queue_draw();
|
2025-04-30 19:50:36 -07:00
|
|
|
queue_resize();
|
2025-04-30 19:12:00 -07:00
|
|
|
}
|
|
|
|
|
}
|