Skip to content

Commit a8b75f7

Browse files
committed
chat-history: Add 4-color gradient to messages
1 parent c3ba612 commit a8b75f7

3 files changed

Lines changed: 116 additions & 103 deletions

File tree

data/resources/style.css

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,9 @@ messagebubble:not(.outgoing):dir(rtl) {
170170
margin-left: 38px;
171171
}
172172

173-
messagebubble.outgoing messagereply {
174-
color: @accent_color;
175-
}
173+
/* messagebubble.outgoing messagereply {
174+
color: @win;
175+
} */
176176

177177
messagebubble.outgoing link,
178178
messagebubble.outgoing messagereply label.message {

src/session/content/background.rs

Lines changed: 90 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use adw::prelude::*;
22
use adw::subclass::prelude::*;
3+
use glib::clone;
34
use gtk::{gdk, gio, glib, graphene, gsk};
45
use std::cell::{Cell, RefCell};
56

@@ -49,7 +50,9 @@ mod imp {
4950

5051
#[derive(Default)]
5152
pub struct Background {
52-
pub(super) gradient_texture: RefCell<Option<gdk::Texture>>,
53+
pub(super) background_texture: RefCell<Option<gdk::Texture>>,
54+
pub(super) message_texture: RefCell<Option<gdk::Texture>>,
55+
5356
pub(super) last_size: Cell<(f32, f32)>,
5457

5558
pub(super) shader: RefCell<Option<Option<gsk::GLShader>>>,
@@ -61,7 +64,8 @@ mod imp {
6164

6265
pub(super) dark: Cell<bool>,
6366

64-
pub(super) colors: RefCell<Vec<graphene::Vec3>>,
67+
pub(super) bg_colors: RefCell<Vec<graphene::Vec3>>,
68+
pub(super) message_colors: RefCell<Vec<graphene::Vec3>>,
6569
}
6670

6771
#[glib::object_subclass]
@@ -92,7 +96,7 @@ mod imp {
9296

9397
let target = adw::CallbackAnimationTarget::new(clone!(@weak obj => move |progress| {
9498
let imp = obj.imp();
95-
imp.gradient_texture.take();
99+
imp.background_texture.take();
96100
let progress = progress as f32;
97101
if progress >= 1.0 {
98102
imp.progress.set(0.0);
@@ -150,27 +154,25 @@ mod imp {
150154
) {
151155
if self.progress.get() == 0.0 {
152156
let texture = {
153-
let cache = self.gradient_texture.borrow();
157+
let cache = self.background_texture.borrow();
154158

155159
match &*cache {
156160
Some(texture) if !size_changed => texture.clone(),
157161
_ => {
158162
drop(cache);
159163

160-
let renderer = self.obj().native().unwrap().renderer();
161-
let texture = renderer
162-
.render_texture(self.gradient_shader_node(bounds), Some(bounds));
163-
164-
self.gradient_texture.replace(Some(texture.clone()));
164+
self.render_textures(bounds);
165165

166-
texture
166+
self.background_texture.borrow().as_ref().unwrap().clone()
167167
}
168168
}
169169
};
170170

171171
snapshot.append_texture(&texture, bounds);
172172
} else {
173-
snapshot.append_node(self.gradient_shader_node(bounds));
173+
self.render_textures(bounds);
174+
let texture = self.background_texture.borrow().as_ref().unwrap().clone();
175+
snapshot.append_texture(&texture, bounds);
174176
}
175177
}
176178

@@ -189,7 +191,7 @@ mod imp {
189191
let mut offset = [0.0; 4];
190192
if self.dark.get() {
191193
matrix[15] = -0.3;
192-
offset = [0.04; 4];
194+
offset = [0.08; 4];
193195
offset[3] = 1.0;
194196
} else {
195197
matrix[15] = 0.1;
@@ -208,7 +210,24 @@ mod imp {
208210
}
209211
}
210212

211-
fn gradient_shader_node(&self, bounds: &graphene::Rect) -> gsk::GLShaderNode {
213+
fn render_textures(&self, bounds: &graphene::Rect) {
214+
let colors = [self.bg_colors.borrow(), self.message_colors.borrow()];
215+
216+
let renderer = self.obj().native().unwrap().renderer();
217+
218+
let mut textures = colors.into_iter().map(|color| {
219+
renderer.render_texture(self.gradient_shader_node(bounds, &color), Some(bounds))
220+
});
221+
222+
self.background_texture.replace(textures.next());
223+
self.message_texture.replace(textures.next());
224+
}
225+
226+
fn gradient_shader_node(
227+
&self,
228+
bounds: &graphene::Rect,
229+
colors: &[graphene::Vec3],
230+
) -> gsk::GLShaderNode {
212231
let Some(Some(gradient_shader)) = &*self.shader.borrow() else {
213232
unreachable!()
214233
};
@@ -218,10 +237,8 @@ mod imp {
218237
let progress = self.progress.get();
219238
let phase = self.phase.get() as usize;
220239

221-
let colors = self.colors.borrow();
222-
223-
let &[c1, c2, c3, c4] = &colors[..] else {
224-
unimplemented!("Unexpected color count");
240+
let &[c1, c2, c3, c4] = colors else {
241+
unimplemented!("Unexpected color count")
225242
};
226243

227244
args_builder.set_vec3(0, &c1);
@@ -287,38 +304,19 @@ impl Background {
287304

288305
imp.dark.set(background.is_dark);
289306

290-
let fill = match background.r#type {
307+
let bg_fill = match background.r#type {
291308
tdlib::enums::BackgroundType::Pattern(pattern) => pattern.fill,
292309
tdlib::enums::BackgroundType::Fill(fill) => fill.fill,
293310
tdlib::enums::BackgroundType::Wallpaper(_) => {
294311
unimplemented!("Wallpaper chat background")
295312
}
296313
};
297314

298-
match fill {
299-
tdlib::enums::BackgroundFill::FreeformGradient(gradient) => {
300-
if gradient.colors.len() != 4 {
301-
unimplemented!("Unsupported gradient colors count");
302-
}
303-
304-
let colors = gradient
305-
.colors
306-
.into_iter()
307-
.map(|int_color| {
308-
let r = (int_color >> 16) & 0xFF;
309-
let g = (int_color >> 8) & 0xFF;
310-
let b = int_color & 0xFF;
311-
312-
graphene::Vec3::new(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0)
313-
})
314-
.collect();
315+
imp.bg_colors.replace(fill_colors(bg_fill));
316+
imp.message_colors
317+
.replace(fill_colors(theme.outgoing_message_fill));
315318

316-
imp.colors.replace(colors);
317-
}
318-
_ => unimplemented!("Background fill"),
319-
}
320-
321-
imp.gradient_texture.take();
319+
imp.background_texture.take();
322320
self.queue_draw();
323321
}
324322

@@ -331,6 +329,29 @@ impl Background {
331329
}
332330
}
333331

332+
pub fn subscribe_to_redraw(&self, child: &gtk::Widget) {
333+
let animation = self.imp().animation.get().unwrap();
334+
animation.connect_value_notify(clone!(@weak child => move |_| child.queue_draw()));
335+
}
336+
337+
pub fn bg_texture(&self) -> gdk::Texture {
338+
self.imp()
339+
.background_texture
340+
.borrow()
341+
.as_ref()
342+
.unwrap()
343+
.clone()
344+
}
345+
346+
pub fn message_texture(&self) -> gdk::Texture {
347+
self.imp()
348+
.message_texture
349+
.borrow()
350+
.as_ref()
351+
.unwrap()
352+
.clone()
353+
}
354+
334355
fn ensure_shader(&self) {
335356
let imp = self.imp();
336357
if imp.shader.borrow().is_none() {
@@ -365,9 +386,28 @@ impl Default for Background {
365386
}
366387
}
367388

389+
fn fill_colors(fill: tdlib::enums::BackgroundFill) -> Vec<graphene::Vec3> {
390+
match fill {
391+
tdlib::enums::BackgroundFill::FreeformGradient(gradient) if gradient.colors.len() == 4 => {
392+
gradient
393+
.colors
394+
.into_iter()
395+
.map(|int_color| {
396+
let r = (int_color >> 16) & 0xFF;
397+
let g = (int_color >> 8) & 0xFF;
398+
let b = int_color & 0xFF;
399+
400+
graphene::Vec3::new(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0)
401+
})
402+
.collect()
403+
}
404+
_ => unimplemented!("Unsupported background fill: {fill:?}"),
405+
}
406+
}
407+
368408
fn hard_coded_themes(dark: bool) -> tdlib::types::ThemeSettings {
369409
macro_rules! theme {
370-
($dark:expr, colors => $($color:expr),+) => {{
410+
($dark:expr, background => $($bg_color:literal),+, messages => $($m_color:literal),+) => {{
371411
use tdlib::enums::{BackgroundFill::*, BackgroundType::Fill};
372412
use tdlib::types::*;
373413

@@ -377,8 +417,7 @@ fn hard_coded_themes(dark: bool) -> tdlib::types::ThemeSettings {
377417
is_dark: $dark,
378418
r#type: Fill(BackgroundTypeFill {
379419
fill: FreeformGradient(BackgroundFillFreeformGradient {
380-
381-
colors: vec![$($color),+],
420+
colors: vec![$($bg_color),+],
382421
}),
383422
}),
384423
id: 0,
@@ -388,14 +427,18 @@ fn hard_coded_themes(dark: bool) -> tdlib::types::ThemeSettings {
388427
accent_color: 0,
389428
animate_outgoing_message_fill: false,
390429
outgoing_message_accent_color: 0,
391-
outgoing_message_fill: Solid(BackgroundFillSolid { color: 0 }),
430+
outgoing_message_fill: FreeformGradient(BackgroundFillFreeformGradient {
431+
colors: vec![$($m_color),+],
432+
}),
392433
}
393434
}}
394435
}
395436

437+
// tr tl bl br
438+
396439
if dark {
397-
theme!(dark, colors => 0xd6932e, 0xbc40db, 0x4280d7, 0x614ed5)
440+
theme!(dark, background => 0xd6932e, 0xbc40db, 0x4280d7, 0x614ed5, messages => 0xfc27a6, 0xff9201, 0x7827ff, 0x554efe)
398441
} else {
399-
theme!(dark, colors => 0x94dae9, 0x9aeddb, 0x94c3f6, 0xac96f7)
442+
theme!(dark, background => 0x94dae9, 0x9aeddb, 0x94c3f6, 0xac96f7, messages => 0xddffdf, 0xfff0dd, 0xffddfc, 0xddecff)
400443
}
401444
}

src/session/content/message_row/bubble.rs

Lines changed: 23 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use adw::prelude::*;
22
use glib::clone;
33
use gtk::subclass::prelude::*;
4-
use gtk::{gdk, glib, graphene, gsk, CompositeTemplate};
4+
use gtk::{glib, CompositeTemplate};
55
use std::collections::hash_map::DefaultHasher;
66
use std::hash::{Hash, Hasher};
77

8+
use crate::session::content::background::Background;
89
use crate::session::content::message_row::{MessageIndicators, MessageLabel, MessageReply};
910
use crate::tdlib::{Chat, ChatType, Message, MessageSender, SponsoredMessage};
1011

@@ -20,6 +21,7 @@ const SENDER_COLOR_CLASSES: &[&str] = &[
2021
];
2122

2223
mod imp {
24+
2325
use super::*;
2426
use once_cell::sync::Lazy;
2527
use std::cell::RefCell;
@@ -64,6 +66,7 @@ mod imp {
6466
pub(super) sender_color_class: RefCell<Option<String>>,
6567
pub(super) sender_binding: RefCell<Option<gtk::ExpressionWatch>>,
6668
pub(super) parent_list_view: RefCell<glib::WeakRef<gtk::ListView>>,
69+
pub(super) parent_background: RefCell<glib::WeakRef<Background>>,
6770
#[template_child]
6871
pub(super) overlay: TemplateChild<gtk::Overlay>,
6972
#[template_child]
@@ -136,27 +139,26 @@ mod imp {
136139
widget.queue_draw();
137140
}));
138141
}
142+
143+
if let Some(background) = widget.parent_background() {
144+
self.parent_background.replace(background.downgrade());
145+
background.subscribe_to_redraw(widget.upcast_ref());
146+
}
139147
}
140148

141149
fn snapshot(&self, snapshot: &gtk::Snapshot) {
142150
let widget = self.obj();
143-
if widget.has_css_class("outgoing") {
144-
let width = widget.width() as f32;
145-
let height = widget.height() as f32;
146-
147-
let bounds = graphene::Rect::new(0.0, 0.0, width, height);
148-
let gradient_bounds = widget.gradient_bounds();
149-
let [first, second] = widget.linear_gradient_colors();
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-
);
151+
152+
if let Some(background) = self.parent_background.borrow().upgrade() {
153+
let gradient_bounds = background.compute_bounds(self.obj().as_ref()).unwrap();
154+
155+
if widget.has_css_class("outgoing") {
156+
snapshot.append_texture(&background.message_texture(), &gradient_bounds);
157+
} else {
158+
snapshot.push_opacity(0.1);
159+
snapshot.append_texture(&background.bg_texture(), &gradient_bounds);
160+
snapshot.pop();
161+
};
160162
}
161163

162164
self.parent_snapshot(snapshot);
@@ -353,42 +355,10 @@ impl MessageBubble {
353355
}
354356

355357
fn parent_list_view(&self) -> Option<gtk::ListView> {
356-
let mut parent = self.parent()?;
357-
loop {
358-
match parent.downcast() {
359-
Ok(list_view) => return Some(list_view),
360-
Err(not_list_view) => parent = not_list_view.parent()?,
361-
}
362-
}
358+
self.ancestor(gtk::ListView::static_type())?.downcast().ok()
363359
}
364360

365-
fn gradient_bounds(&self) -> graphene::Rect {
366-
if let Some(view) = self.imp().parent_list_view.borrow().upgrade() {
367-
let view_bounds = view.compute_bounds(self.imp().obj().as_ref()).unwrap();
368-
369-
graphene::Rect::new(
370-
view_bounds.x(),
371-
view_bounds.y(),
372-
view_bounds.width() + view_bounds.x(),
373-
view_bounds.height() + view_bounds.y(),
374-
)
375-
} else {
376-
panic!("can't get parent ListView");
377-
}
378-
}
379-
380-
fn linear_gradient_colors(&self) -> [gdk::RGBA; 2] {
381-
// default colors from iOS
382-
if !adw::StyleManager::default().is_dark() {
383-
[
384-
gdk::RGBA::new(0.91764706, 0.9882353, 0.8235294, 1.0),
385-
gdk::RGBA::new(0.91764706, 0.9882353, 0.8235294, 1.0),
386-
]
387-
} else {
388-
[
389-
gdk::RGBA::new(0.21960784, 0.32156864, 0.89411765, 1.0),
390-
gdk::RGBA::new(0.63529414, 0.3372549, 0.58431375, 1.0),
391-
]
392-
}
361+
fn parent_background(&self) -> Option<Background> {
362+
self.ancestor(Background::static_type())?.downcast().ok()
393363
}
394364
}

0 commit comments

Comments
 (0)