Skip to content

Commit c6e858b

Browse files
committed
chat: Add linear gradient to outgoing messages
1 parent 28655d6 commit c6e858b

3 files changed

Lines changed: 100 additions & 5 deletions

File tree

data/resources/style-dark.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ messagebubble:not(.outgoing) {
77
background: alpha(#303030, 0.9);
88
}
99

10+
messagebubble.document.outgoing .file > overlay > image {
11+
background: @accent_fg_color;
12+
color: @accent_bg_color;
13+
}
14+
1015
.event-row {
1116
/* don't forget to edit the pattern shader if change opacity */
1217
background-color: alpha(#404040, 0.8);

data/resources/style.css

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,8 @@ messagebubble {
156156
}
157157

158158
messagebubble.outgoing {
159-
background: @accent_bg_color;
160-
color: @accent_fg_color;
159+
background: transparent;
160+
color: @window_fg_color;
161161
}
162162

163163
messagebubble:not(.outgoing):dir(ltr),
@@ -209,8 +209,9 @@ messagebubble.document .file > overlay > image {
209209
}
210210

211211
messagebubble.document.outgoing .file > overlay > image {
212-
background: @accent_fg_color;
213-
color: @accent_bg_color;
212+
/* depends on bubble color */
213+
background: #79c271;
214+
color: #eafcd2;
214215
}
215216

216217
messagebubble.document .file:hover > overlay > image {
@@ -330,6 +331,10 @@ messagereply label.message {
330331
color: @window_fg_color;
331332
}
332333

334+
messagebubble.outgoing messagereply label.message {
335+
color: currentColor;
336+
}
337+
333338
messagesticker {
334339
border-spacing: 6px;
335340
}

src/session/content/message_row/bubble.rs

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use adw::prelude::*;
2+
use glib::clone;
23
use gtk::subclass::prelude::*;
3-
use gtk::{glib, CompositeTemplate};
4+
use gtk::{gdk, glib, graphene, gsk, CompositeTemplate};
45
use std::collections::hash_map::DefaultHasher;
56
use std::hash::{Hash, Hasher};
67

@@ -60,6 +61,7 @@ mod imp {
6061
pub(crate) struct MessageBubble {
6162
pub(super) sender_color_class: RefCell<Option<String>>,
6263
pub(super) sender_binding: RefCell<Option<gtk::ExpressionWatch>>,
64+
pub(super) parent_list_view: RefCell<glib::WeakRef<gtk::ListView>>,
6365
#[template_child]
6466
pub(super) overlay: TemplateChild<gtk::Overlay>,
6567
#[template_child]
@@ -119,6 +121,49 @@ mod imp {
119121
}
120122

121123
impl WidgetImpl for MessageBubble {
124+
fn realize(&self) {
125+
self.parent_realize();
126+
127+
let widget = self.obj();
128+
129+
if let Some(view) = widget.parent_list_view() {
130+
self.parent_list_view.replace(view.downgrade());
131+
view.vadjustment()
132+
.unwrap()
133+
.connect_value_notify(clone!(@weak widget => move |_| {
134+
widget.queue_draw();
135+
}));
136+
}
137+
}
138+
139+
fn snapshot(&self, snapshot: &gtk::Snapshot) {
140+
let widget = self.obj();
141+
if widget.has_css_class("outgoing") {
142+
let width = widget.width() as f32;
143+
let height = widget.height() as f32;
144+
145+
let bounds = graphene::Rect::new(0.0, 0.0, width, height);
146+
let gradient_bounds = widget.gradient_bounds();
147+
let [first, second] = widget.linear_gradient_colors();
148+
149+
snapshot.push_rounded_clip(&gsk::RoundedRect::from_rect(bounds, 15.0));
150+
151+
snapshot.append_linear_gradient(
152+
&bounds,
153+
&graphene::Point::new(0.0, gradient_bounds.y()),
154+
&graphene::Point::new(0.0, gradient_bounds.height()),
155+
&[
156+
gsk::ColorStop::new(0.0, first),
157+
gsk::ColorStop::new(1.0, second),
158+
],
159+
);
160+
161+
snapshot.pop();
162+
}
163+
164+
self.parent_snapshot(snapshot);
165+
}
166+
122167
fn measure(&self, orientation: gtk::Orientation, for_size: i32) -> (i32, i32, i32, i32) {
123168
// Limit the widget width
124169
if orientation == gtk::Orientation::Horizontal {
@@ -308,4 +353,44 @@ impl MessageBubble {
308353
.set_indicators(Some(imp.indicators.clone()));
309354
}
310355
}
356+
357+
fn parent_list_view(&self) -> Option<gtk::ListView> {
358+
let mut parent = self.parent()?;
359+
loop {
360+
match parent.downcast() {
361+
Ok(list_view) => return Some(list_view),
362+
Err(not_list_view) => parent = not_list_view.parent()?,
363+
}
364+
}
365+
}
366+
367+
fn gradient_bounds(&self) -> graphene::Rect {
368+
if let Some(view) = self.imp().parent_list_view.borrow().upgrade() {
369+
let view_bounds = view.compute_bounds(self.imp().obj().as_ref()).unwrap();
370+
371+
graphene::Rect::new(
372+
view_bounds.x(),
373+
view_bounds.y(),
374+
view_bounds.width() + view_bounds.x(),
375+
view_bounds.height() + view_bounds.y(),
376+
)
377+
} else {
378+
panic!("can't get parent ListView");
379+
}
380+
}
381+
382+
fn linear_gradient_colors(&self) -> [gdk::RGBA; 2] {
383+
// default colors from iOS
384+
if !adw::StyleManager::default().is_dark() {
385+
[
386+
gdk::RGBA::new(0.91764706, 0.9882353, 0.8235294, 1.0),
387+
gdk::RGBA::new(0.91764706, 0.9882353, 0.8235294, 1.0),
388+
]
389+
} else {
390+
[
391+
gdk::RGBA::new(0.21960784, 0.32156864, 0.89411765, 1.0),
392+
gdk::RGBA::new(0.63529414, 0.3372549, 0.58431375, 1.0),
393+
]
394+
}
395+
}
311396
}

0 commit comments

Comments
 (0)