Private
Public Access
1
0

reorg: message-list -> transcript

This commit is contained in:
2025-05-03 22:47:56 -07:00
parent d3dfffd652
commit dd91746310
11 changed files with 46 additions and 35 deletions

View File

@@ -0,0 +1,195 @@
using Gtk;
private struct BubbleLayoutConstants {
public float tail_width;
public float tail_curve_offset;
public float tail_side_offset;
public float tail_bottom_padding;
public float corner_radius;
public float text_padding;
public BubbleLayoutConstants(float scale_factor) {
tail_width = 15.0f / scale_factor;
tail_curve_offset = 2.5f / scale_factor;
tail_side_offset = 0.0f / scale_factor;
tail_bottom_padding = 4.0f / scale_factor;
corner_radius = 24.0f / scale_factor;
text_padding = 18.0f / scale_factor;
}
}
private abstract class BubbleLayout : Object, ChatItemLayout
{
public bool from_me { get; set; }
protected float max_width;
protected Widget parent;
protected BubbleLayoutConstants constants;
protected BubbleLayout(Widget parent, float max_width) {
this.max_width = max_width;
this.parent = parent;
this.constants = BubbleLayoutConstants(parent.get_scale_factor());
}
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 abstract float get_height();
public abstract float get_width();
public void draw(Snapshot snapshot) {
with_bubble_clip(snapshot, (snapshot) => {
draw_background(snapshot);
draw_content(snapshot);
});
}
public abstract void draw_content(Snapshot snapshot);
private void draw_background(Snapshot snapshot)
{
var width = get_width();
var height = get_height();
var color = background_color;
var bounds = Graphene.Rect().init(0, 0, width, height);
const float gradient_darkening = 0.67f;
var start_point = Graphene.Point() { x = 0, y = 0 };
var end_point = Graphene.Point() { x = 0, y = height };
var stops = new Gsk.ColorStop[] {
Gsk.ColorStop() { offset = 0.0f, color = color },
Gsk.ColorStop() { offset = 1.0f, color = Gdk.RGBA () {
red = color.red * gradient_darkening,
green = color.green * gradient_darkening,
blue = color.blue * gradient_darkening,
alpha = color.alpha
} },
};
snapshot.append_linear_gradient(bounds, start_point, end_point, stops);
}
protected void with_bubble_clip(Snapshot snapshot, Func<Snapshot> func) {
var width = get_width();
var height = get_height();
if (width > 10 && height > 10) {
var path = create_bubble_path(width, height, from_me);
snapshot.push_fill(path, Gsk.FillRule.WINDING);
func(snapshot);
snapshot.pop();
} else {
func(snapshot);
}
}
private Gsk.Path create_bubble_path(float width, float height, bool tail_on_right = false)
{
var builder = new Gsk.PathBuilder();
float bubble_width = width - constants.tail_width;
float bubble_height = height - constants.tail_bottom_padding;
// Base position adjustments based on tail position
float x = tail_on_right ? 0.0f : constants.tail_width;
float y = 0.0f;
// Calculate tail direction multiplier (-1 for left, 1 for right)
float dir = tail_on_right ? 1.0f : -1.0f;
// Calculate tail side positions based on direction
float tail_side_x = tail_on_right ? (x + bubble_width) : x;
// Start at top corner opposite to the tail
builder.move_to(tail_on_right ? (x + constants.corner_radius) : (x + bubble_width - constants.corner_radius), y);
// Top edge
builder.line_to(tail_on_right ? (x + bubble_width - constants.corner_radius) : (x + constants.corner_radius), y);
// Top corner on tail side
if (tail_on_right) {
builder.html_arc_to(x + bubble_width, y,
x + bubble_width, y + constants.corner_radius,
constants.corner_radius);
} else {
builder.html_arc_to(x, y,
x, y + constants.corner_radius,
constants.corner_radius);
}
// Side edge on tail side
builder.line_to(tail_side_x, y + bubble_height - constants.corner_radius);
// Corner with tail
float tail_tip_x = tail_side_x + (dir * (constants.tail_width - constants.tail_curve_offset));
float tail_tip_y = y + bubble_height;
// Control points for the bezier curve
float ctrl_point1_x = tail_side_x + (dir * constants.tail_side_offset);
float ctrl_point1_y = y + bubble_height - constants.corner_radius/3;
float ctrl_point2_x = tail_side_x + (dir * (constants.tail_width / 2.0f));
float ctrl_point2_y = y + bubble_height - 2;
// Point where the tail meets the bottom edge
float tail_base_x = tail_side_x - (dir * constants.corner_radius/2);
float tail_base_y = y + bubble_height;
// Draw the corner with tail using bezier curves
builder.cubic_to(ctrl_point1_x, ctrl_point1_y,
ctrl_point2_x, ctrl_point2_y,
tail_tip_x, tail_tip_y);
builder.cubic_to(tail_tip_x - (dir * constants.tail_curve_offset), tail_tip_y + constants.tail_curve_offset,
tail_base_x + (dir * constants.tail_width), tail_base_y,
tail_base_x, tail_base_y);
// Bottom edge
builder.line_to(tail_on_right ? (x + constants.corner_radius) : (x + bubble_width - constants.corner_radius), y + bubble_height);
// Bottom corner opposite to tail
if (tail_on_right) {
builder.html_arc_to(x, y + bubble_height,
x, y + bubble_height - constants.corner_radius,
constants.corner_radius);
} else {
builder.html_arc_to(x + bubble_width, y + bubble_height,
x + bubble_width, y + bubble_height - constants.corner_radius,
constants.corner_radius);
}
// Side edge opposite to tail
if (tail_on_right) {
builder.line_to(x, y + constants.corner_radius);
// Top corner to close path
builder.html_arc_to(x, y,
x + constants.corner_radius, y,
constants.corner_radius);
} else {
builder.line_to(x + bubble_width, y + constants.corner_radius);
// Top corner to close path
builder.html_arc_to(x + bubble_width, y,
x + bubble_width - constants.corner_radius, y,
constants.corner_radius);
}
// Close the path
builder.close();
return builder.to_path();
}
}