using Gtk; using Gee; private class MessageDrawingArea : Widget { private SortedSet _messages = new TreeSet(); private ArrayList _message_layouts = 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; _message_layouts.foreach((message_layout) => { total_height += message_layout.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; _message_layouts.foreach((message_layout) => { var message_width = message_layout.get_width(); var message_height = message_layout.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 = (message_layout.from_me ? (container_width - message_width - bubble_margin) : bubble_margin), y = y_offset }); // Undo the y-axis flip, origin is top left snapshot.translate(Graphene.Point() { x = 0, y = -message_height }); message_layout.draw(snapshot); snapshot.restore(); y_offset -= message_height + bubble_padding; return true; }); } private void recompute_message_layouts() { var container_width = get_width(); float max_width = container_width * 0.90f; _message_layouts.clear(); _messages .order_by((a, b) => (int)b.date - (int)a.date) // reverse order .foreach((message) => { _message_layouts.add(new MessageLayout(message, this, max_width)); return true; }); queue_draw(); queue_resize(); } }