using Gtk; using Gee; private class MessageDrawingArea : Widget { private SortedSet _messages = new TreeSet(); private ArrayList _chat_items = new ArrayList(); 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 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; _chat_items.foreach((chat_item) => { total_height += chat_item.get_height() + bubble_padding; 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; _chat_items.foreach((chat_item) => { var item_width = chat_item.get_width(); var item_height = chat_item.get_height(); 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() { x = (chat_item.from_me ? (container_width - item_width - bubble_margin) : bubble_margin), y = y_offset }); // 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 + bubble_padding; return true; }); } private void recompute_message_layouts() { var container_width = get_width(); float max_width = container_width * 0.90f; _chat_items.clear(); 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; } var text_bubble = new TextBubbleLayout(message, this, max_width); _chat_items.add(text_bubble); return true; }); queue_draw(); queue_resize(); } }