reorg: message layout becomes interface for other types of chat items (like date)
This commit is contained in:
@@ -31,7 +31,10 @@ sources = [
|
|||||||
'message-list/message-list-view.vala',
|
'message-list/message-list-view.vala',
|
||||||
'message-list/message-list-model.vala',
|
'message-list/message-list-model.vala',
|
||||||
'message-list/message-drawing-area.vala',
|
'message-list/message-drawing-area.vala',
|
||||||
'message-list/message-layout.vala',
|
|
||||||
|
'message-list/layouts/bubble-layout.vala',
|
||||||
|
'message-list/layouts/chat-item-layout.vala',
|
||||||
|
'message-list/layouts/text-bubble-layout.vala',
|
||||||
|
|
||||||
'models/conversation.vala',
|
'models/conversation.vala',
|
||||||
'models/message.vala',
|
'models/message.vala',
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Gtk;
|
using Gtk;
|
||||||
using Gee;
|
|
||||||
|
|
||||||
private struct MessageLayoutConstants {
|
private struct BubbleLayoutConstants {
|
||||||
public float tail_width;
|
public float tail_width;
|
||||||
public float tail_curve_offset;
|
public float tail_curve_offset;
|
||||||
public float tail_side_offset;
|
public float tail_side_offset;
|
||||||
@@ -9,7 +8,7 @@ private struct MessageLayoutConstants {
|
|||||||
public float corner_radius;
|
public float corner_radius;
|
||||||
public float text_padding;
|
public float text_padding;
|
||||||
|
|
||||||
public MessageLayoutConstants(float scale_factor) {
|
public BubbleLayoutConstants(float scale_factor) {
|
||||||
tail_width = 15.0f / scale_factor;
|
tail_width = 15.0f / scale_factor;
|
||||||
tail_curve_offset = 2.5f / scale_factor;
|
tail_curve_offset = 2.5f / scale_factor;
|
||||||
tail_side_offset = 0.0f / scale_factor;
|
tail_side_offset = 0.0f / scale_factor;
|
||||||
@@ -19,61 +18,18 @@ private struct MessageLayoutConstants {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MessageLayout : Object
|
private abstract class BubbleLayout : Object, ChatItemLayout
|
||||||
{
|
{
|
||||||
public Message message;
|
public bool from_me { get; set; }
|
||||||
|
|
||||||
private float max_width;
|
protected float max_width;
|
||||||
private Pango.Layout layout;
|
protected Widget parent;
|
||||||
private Widget parent;
|
protected BubbleLayoutConstants constants;
|
||||||
private MessageLayoutConstants constants;
|
|
||||||
|
|
||||||
public MessageLayout(Message message, Widget parent, float max_width) {
|
protected BubbleLayout(Widget parent, float max_width) {
|
||||||
this.message = message;
|
|
||||||
this.max_width = max_width;
|
this.max_width = max_width;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.constants = MessageLayoutConstants(parent.get_scale_factor());
|
this.constants = BubbleLayoutConstants(parent.get_scale_factor());
|
||||||
|
|
||||||
layout = parent.create_pango_layout(message.text);
|
|
||||||
|
|
||||||
// Get the system font settings
|
|
||||||
var settings = Gtk.Settings.get_default();
|
|
||||||
var font_name = settings.gtk_font_name;
|
|
||||||
|
|
||||||
// Create font description from system font
|
|
||||||
var font_desc = Pango.FontDescription.from_string(font_name);
|
|
||||||
|
|
||||||
var size = font_desc.get_size();
|
|
||||||
font_desc.set_size((int)(size));
|
|
||||||
layout.set_font_description(font_desc);
|
|
||||||
|
|
||||||
// Set max width
|
|
||||||
layout.set_width((int)text_available_width * Pango.SCALE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool from_me {
|
|
||||||
get {
|
|
||||||
return message.from_me;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private float text_available_width {
|
|
||||||
get {
|
|
||||||
return max_width - text_x_offset - constants.text_padding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private float text_x_offset {
|
|
||||||
get {
|
|
||||||
return from_me ? constants.text_padding : constants.tail_width + constants.text_padding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private float text_x_padding {
|
|
||||||
get {
|
|
||||||
// Opposite of text_x_offset
|
|
||||||
return from_me ? constants.tail_width + constants.text_padding : constants.text_padding;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Gdk.RGBA background_color {
|
private Gdk.RGBA background_color {
|
||||||
@@ -87,47 +43,18 @@ private class MessageLayout : Object
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public float get_height() {
|
public abstract float get_height();
|
||||||
Pango.Rectangle ink_rect, logical_rect;
|
|
||||||
layout.get_pixel_extents(out ink_rect, out logical_rect);
|
|
||||||
|
|
||||||
return logical_rect.height + constants.corner_radius + constants.tail_bottom_padding;
|
public abstract float get_width();
|
||||||
}
|
|
||||||
|
|
||||||
public float get_width() {
|
|
||||||
Pango.Rectangle ink_rect, logical_rect;
|
|
||||||
layout.get_pixel_extents(out ink_rect, out logical_rect);
|
|
||||||
|
|
||||||
return logical_rect.width + text_x_offset + text_x_padding;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void draw(Snapshot snapshot) {
|
public void draw(Snapshot snapshot) {
|
||||||
with_bubble_clip(snapshot, (snapshot) => {
|
with_bubble_clip(snapshot, (snapshot) => {
|
||||||
draw_background(snapshot);
|
draw_background(snapshot);
|
||||||
draw_text(snapshot);
|
draw_content(snapshot);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void draw_text(Snapshot snapshot)
|
public abstract void draw_content(Snapshot snapshot);
|
||||||
{
|
|
||||||
snapshot.save();
|
|
||||||
|
|
||||||
Pango.Rectangle ink_rect, logical_rect;
|
|
||||||
layout.get_pixel_extents(out ink_rect, out logical_rect);
|
|
||||||
snapshot.translate(Graphene.Point() {
|
|
||||||
x = text_x_offset,
|
|
||||||
y = ((get_height() - constants.tail_bottom_padding) - logical_rect.height) / 2
|
|
||||||
});
|
|
||||||
|
|
||||||
snapshot.append_layout(layout, Gdk.RGBA() {
|
|
||||||
red = 1.0f,
|
|
||||||
green = 1.0f,
|
|
||||||
blue = 1.0f,
|
|
||||||
alpha = 1.0f
|
|
||||||
});
|
|
||||||
|
|
||||||
snapshot.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void draw_background(Snapshot snapshot)
|
private void draw_background(Snapshot snapshot)
|
||||||
{
|
{
|
||||||
@@ -152,7 +79,7 @@ private class MessageLayout : Object
|
|||||||
snapshot.append_linear_gradient(bounds, start_point, end_point, stops);
|
snapshot.append_linear_gradient(bounds, start_point, end_point, stops);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void with_bubble_clip(Snapshot snapshot, Func<Snapshot> func) {
|
protected void with_bubble_clip(Snapshot snapshot, Func<Snapshot> func) {
|
||||||
var width = get_width();
|
var width = get_width();
|
||||||
var height = get_height();
|
var height = get_height();
|
||||||
|
|
||||||
10
src/message-list/layouts/chat-item-layout.vala
Normal file
10
src/message-list/layouts/chat-item-layout.vala
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using Gtk;
|
||||||
|
|
||||||
|
interface ChatItemLayout : Object
|
||||||
|
{
|
||||||
|
public abstract bool from_me { get; set; }
|
||||||
|
|
||||||
|
public abstract float get_height();
|
||||||
|
public abstract float get_width();
|
||||||
|
public abstract void draw(Snapshot snapshot);
|
||||||
|
}
|
||||||
96
src/message-list/layouts/text-bubble-layout.vala
Normal file
96
src/message-list/layouts/text-bubble-layout.vala
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
using Gtk;
|
||||||
|
|
||||||
|
private class TextBubbleLayout : BubbleLayout
|
||||||
|
{
|
||||||
|
public Message message;
|
||||||
|
private Pango.Layout layout;
|
||||||
|
|
||||||
|
public TextBubbleLayout(Message message, Widget parent, float max_width) {
|
||||||
|
base(parent, max_width);
|
||||||
|
|
||||||
|
this.from_me = message.from_me;
|
||||||
|
this.message = message;
|
||||||
|
|
||||||
|
layout = parent.create_pango_layout(message.text);
|
||||||
|
|
||||||
|
// Get the system font settings
|
||||||
|
var settings = Gtk.Settings.get_default();
|
||||||
|
var font_name = settings.gtk_font_name;
|
||||||
|
|
||||||
|
// Create font description from system font
|
||||||
|
var font_desc = Pango.FontDescription.from_string(font_name);
|
||||||
|
|
||||||
|
var size = font_desc.get_size();
|
||||||
|
font_desc.set_size((int)(size));
|
||||||
|
layout.set_font_description(font_desc);
|
||||||
|
|
||||||
|
// Set max width
|
||||||
|
layout.set_width((int)text_available_width * Pango.SCALE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private float text_available_width {
|
||||||
|
get {
|
||||||
|
return max_width - text_x_offset - constants.text_padding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float text_x_offset {
|
||||||
|
get {
|
||||||
|
return from_me ? constants.text_padding : constants.tail_width + constants.text_padding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float text_x_padding {
|
||||||
|
get {
|
||||||
|
// Opposite of text_x_offset
|
||||||
|
return from_me ? constants.tail_width + constants.text_padding : constants.text_padding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Gdk.RGBA background_color {
|
||||||
|
get {
|
||||||
|
return from_me ? parent.get_color() : Gdk.RGBA() {
|
||||||
|
red = 1.0f,
|
||||||
|
green = 1.0f,
|
||||||
|
blue = 1.0f,
|
||||||
|
alpha = 0.08f
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override float get_height() {
|
||||||
|
Pango.Rectangle ink_rect, logical_rect;
|
||||||
|
layout.get_pixel_extents(out ink_rect, out logical_rect);
|
||||||
|
|
||||||
|
return logical_rect.height + constants.corner_radius + constants.tail_bottom_padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override float get_width() {
|
||||||
|
Pango.Rectangle ink_rect, logical_rect;
|
||||||
|
layout.get_pixel_extents(out ink_rect, out logical_rect);
|
||||||
|
|
||||||
|
return logical_rect.width + text_x_offset + text_x_padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void draw_content(Snapshot snapshot) {
|
||||||
|
snapshot.save();
|
||||||
|
|
||||||
|
Pango.Rectangle ink_rect, logical_rect;
|
||||||
|
layout.get_pixel_extents(out ink_rect, out logical_rect);
|
||||||
|
snapshot.translate(Graphene.Point() {
|
||||||
|
x = text_x_offset,
|
||||||
|
y = ((get_height() - constants.tail_bottom_padding) - logical_rect.height) / 2
|
||||||
|
});
|
||||||
|
|
||||||
|
snapshot.append_layout(layout, Gdk.RGBA() {
|
||||||
|
red = 1.0f,
|
||||||
|
green = 1.0f,
|
||||||
|
blue = 1.0f,
|
||||||
|
alpha = 1.0f
|
||||||
|
});
|
||||||
|
|
||||||
|
snapshot.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -4,7 +4,7 @@ using Gee;
|
|||||||
private class MessageDrawingArea : Widget
|
private class MessageDrawingArea : Widget
|
||||||
{
|
{
|
||||||
private SortedSet<Message> _messages = new TreeSet<Message>();
|
private SortedSet<Message> _messages = new TreeSet<Message>();
|
||||||
private ArrayList<MessageLayout> _message_layouts = new ArrayList<MessageLayout>();
|
private ArrayList<ChatItemLayout> _chat_items = new ArrayList<ChatItemLayout>();
|
||||||
|
|
||||||
private const float bubble_padding = 10.0f;
|
private const float bubble_padding = 10.0f;
|
||||||
private const float bubble_margin = 18.0f;
|
private const float bubble_margin = 18.0f;
|
||||||
@@ -31,8 +31,8 @@ private class MessageDrawingArea : Widget
|
|||||||
} else {
|
} else {
|
||||||
// compute total message layout height
|
// compute total message layout height
|
||||||
float total_height = 0.0f;
|
float total_height = 0.0f;
|
||||||
_message_layouts.foreach((message_layout) => {
|
_chat_items.foreach((chat_item) => {
|
||||||
total_height += message_layout.get_height() + bubble_padding;
|
total_height += chat_item.get_height() + bubble_padding;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -52,9 +52,9 @@ private class MessageDrawingArea : Widget
|
|||||||
public override void snapshot(Snapshot snapshot) {
|
public override void snapshot(Snapshot snapshot) {
|
||||||
var container_width = get_width();
|
var container_width = get_width();
|
||||||
float y_offset = 0;
|
float y_offset = 0;
|
||||||
_message_layouts.foreach((message_layout) => {
|
_chat_items.foreach((chat_item) => {
|
||||||
var message_width = message_layout.get_width();
|
var message_width = chat_item.get_width();
|
||||||
var message_height = message_layout.get_height();
|
var message_height = chat_item.get_height();
|
||||||
|
|
||||||
snapshot.save();
|
snapshot.save();
|
||||||
|
|
||||||
@@ -63,14 +63,14 @@ private class MessageDrawingArea : Widget
|
|||||||
|
|
||||||
// Translate to the correct position
|
// Translate to the correct position
|
||||||
snapshot.translate(Graphene.Point() {
|
snapshot.translate(Graphene.Point() {
|
||||||
x = (message_layout.from_me ? (container_width - message_width - bubble_margin) : bubble_margin),
|
x = (chat_item.from_me ? (container_width - message_width - bubble_margin) : bubble_margin),
|
||||||
y = y_offset
|
y = y_offset
|
||||||
});
|
});
|
||||||
|
|
||||||
// Undo the y-axis flip, origin is top left
|
// Undo the y-axis flip, origin is top left
|
||||||
snapshot.translate(Graphene.Point() { x = 0, y = -message_height });
|
snapshot.translate(Graphene.Point() { x = 0, y = -message_height });
|
||||||
|
|
||||||
message_layout.draw(snapshot);
|
chat_item.draw(snapshot);
|
||||||
snapshot.restore();
|
snapshot.restore();
|
||||||
|
|
||||||
y_offset -= message_height + bubble_padding;
|
y_offset -= message_height + bubble_padding;
|
||||||
@@ -83,13 +83,16 @@ private class MessageDrawingArea : Widget
|
|||||||
var container_width = get_width();
|
var container_width = get_width();
|
||||||
float max_width = container_width * 0.90f;
|
float max_width = container_width * 0.90f;
|
||||||
|
|
||||||
_message_layouts.clear();
|
_chat_items.clear();
|
||||||
_messages
|
|
||||||
.order_by((a, b) => (int)b.date - (int)a.date) // reverse order
|
var sorted_messages = _messages
|
||||||
.foreach((message) => {
|
.order_by((a, b) => (int)b.date - (int)a.date); // reverse order
|
||||||
_message_layouts.add(new MessageLayout(message, this, max_width));
|
|
||||||
return true;
|
|
||||||
});
|
sorted_messages.foreach((message) => {
|
||||||
|
_chat_items.add(new TextBubbleLayout(message, this, max_width));
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
queue_draw();
|
queue_draw();
|
||||||
queue_resize();
|
queue_resize();
|
||||||
|
|||||||
Reference in New Issue
Block a user