Skip to content

Commit 4852ddf

Browse files
committed
chat-history: Add 4-color gradient to messages
1 parent 1cba840 commit 4852ddf

2 files changed

Lines changed: 127 additions & 96 deletions

File tree

src/session/content/background.rs

Lines changed: 103 additions & 43 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(crate) 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<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]
@@ -102,7 +106,7 @@ mod imp {
102106

103107
let target = adw::CallbackAnimationTarget::new(clone!(@weak obj => move |progress| {
104108
let imp = obj.imp();
105-
imp.gradient_texture.take();
109+
imp.background_texture.take();
106110
let progress = progress as f32;
107111
if progress >= 1.0 {
108112
imp.progress.set(0.0);
@@ -171,18 +175,20 @@ mod imp {
171175
size_changed: bool,
172176
) {
173177
if self.progress.get() == 0.0 {
174-
let texture = match self.gradient_texture.take() {
175-
Some(texture) if !size_changed => texture,
178+
let texture = match self.background_texture.take() {
179+
Some(texture) if !size_changed => texture.clone(),
176180
_ => {
177-
let renderer = self.obj().native().unwrap().renderer();
178-
renderer.render_texture(self.gradient_shader_node(bounds), Some(bounds))
181+
self.render_textures(bounds);
182+
self.background_texture.take().unwrap()
179183
}
180184
};
181185

182186
snapshot.append_texture(&texture, bounds);
183-
self.gradient_texture.replace(Some(texture));
187+
self.background_texture.replace(Some(texture));
184188
} else {
185-
snapshot.append_node(self.gradient_shader_node(bounds));
189+
self.render_textures(bounds);
190+
let texture = self.background_texture.borrow().as_ref().unwrap().clone();
191+
snapshot.append_texture(&texture, bounds);
186192
}
187193
}
188194

@@ -220,7 +226,24 @@ mod imp {
220226
}
221227
}
222228

223-
fn gradient_shader_node(&self, bounds: &graphene::Rect) -> gsk::GLShaderNode {
229+
fn render_textures(&self, bounds: &graphene::Rect) {
230+
let colors = [self.bg_colors.borrow(), self.message_colors.borrow()];
231+
232+
let renderer = self.obj().native().unwrap().renderer();
233+
234+
let mut textures = colors.into_iter().map(|color| {
235+
renderer.render_texture(self.gradient_shader_node(bounds, &color), Some(bounds))
236+
});
237+
238+
self.background_texture.replace(textures.next());
239+
self.message_texture.replace(textures.next());
240+
}
241+
242+
fn gradient_shader_node(
243+
&self,
244+
bounds: &graphene::Rect,
245+
colors: &[graphene::Vec3],
246+
) -> gsk::GLShaderNode {
224247
let Some(gradient_shader) = &*self.shader.borrow() else {
225248
unreachable!()
226249
};
@@ -230,10 +253,8 @@ mod imp {
230253
let progress = self.progress.get();
231254
let phase = self.phase.get() as usize;
232255

233-
let colors = self.colors.borrow();
234-
235-
let &[c1, c2, c3, c4] = &colors[..] else {
236-
unimplemented!("Unexpected color count");
256+
let &[c1, c2, c3, c4] = colors else {
257+
unimplemented!("Unexpected color count")
237258
};
238259

239260
args_builder.set_vec3(0, &c1);
@@ -299,38 +320,19 @@ impl Background {
299320

300321
imp.dark.set(background.is_dark);
301322

302-
let fill = match background.r#type {
323+
let bg_fill = match background.r#type {
303324
tdlib::enums::BackgroundType::Pattern(pattern) => pattern.fill,
304325
tdlib::enums::BackgroundType::Fill(fill) => fill.fill,
305326
tdlib::enums::BackgroundType::Wallpaper(_) => {
306327
unimplemented!("Wallpaper chat background")
307328
}
308329
};
309330

310-
match fill {
311-
tdlib::enums::BackgroundFill::FreeformGradient(gradient) => {
312-
if gradient.colors.len() != 4 {
313-
unimplemented!("Unsupported gradient colors count");
314-
}
315-
316-
let colors = gradient
317-
.colors
318-
.into_iter()
319-
.map(|int_color| {
320-
let r = (int_color >> 16) & 0xFF;
321-
let g = (int_color >> 8) & 0xFF;
322-
let b = int_color & 0xFF;
331+
imp.bg_colors.replace(fill_colors(bg_fill));
332+
imp.message_colors
333+
.replace(fill_colors(theme.outgoing_message_fill));
323334

324-
graphene::Vec3::new(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0)
325-
})
326-
.collect();
327-
328-
imp.colors.replace(colors);
329-
}
330-
_ => unimplemented!("Background fill"),
331-
}
332-
333-
imp.gradient_texture.take();
335+
imp.background_texture.take();
334336
self.queue_draw();
335337
}
336338

@@ -343,6 +345,29 @@ impl Background {
343345
}
344346
}
345347

348+
pub fn subscribe_to_redraw(&self, child: &gtk::Widget) {
349+
let animation = self.imp().animation.get().unwrap();
350+
animation.connect_value_notify(clone!(@weak child => move |_| child.queue_draw()));
351+
}
352+
353+
pub fn bg_texture(&self) -> gdk::Texture {
354+
self.imp()
355+
.background_texture
356+
.borrow()
357+
.as_ref()
358+
.unwrap()
359+
.clone()
360+
}
361+
362+
pub fn message_texture(&self) -> gdk::Texture {
363+
self.imp()
364+
.message_texture
365+
.borrow()
366+
.as_ref()
367+
.unwrap()
368+
.clone()
369+
}
370+
346371
fn ensure_shader(&self) {
347372
let imp = self.imp();
348373
if imp.shader.borrow().is_none() {
@@ -370,8 +395,31 @@ impl Default for Background {
370395
}
371396
}
372397

398+
fn fill_colors(fill: tdlib::enums::BackgroundFill) -> Vec<graphene::Vec3> {
399+
match fill {
400+
tdlib::enums::BackgroundFill::FreeformGradient(gradient) if gradient.colors.len() == 4 => {
401+
gradient
402+
.colors
403+
.into_iter()
404+
.map(|int_color| {
405+
let r = (int_color >> 16) & 0xFF;
406+
let g = (int_color >> 8) & 0xFF;
407+
let b = int_color & 0xFF;
408+
409+
graphene::Vec3::new(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0)
410+
})
411+
.collect()
412+
}
413+
_ => unimplemented!("Unsupported background fill: {fill:?}"),
414+
}
415+
}
416+
373417
fn hard_coded_themes(dark: bool) -> tdlib::types::ThemeSettings {
374-
fn theme(dark: bool, colors: Vec<i32>) -> tdlib::types::ThemeSettings {
418+
fn theme(
419+
dark: bool,
420+
bg_colors: Vec<i32>,
421+
message_colors: Vec<i32>,
422+
) -> tdlib::types::ThemeSettings {
375423
use tdlib::enums::BackgroundFill::*;
376424
use tdlib::enums::BackgroundType::Fill;
377425
use tdlib::types::*;
@@ -381,7 +429,7 @@ fn hard_coded_themes(dark: bool) -> tdlib::types::ThemeSettings {
381429
is_default: true,
382430
is_dark: dark,
383431
r#type: Fill(BackgroundTypeFill {
384-
fill: FreeformGradient(BackgroundFillFreeformGradient { colors }),
432+
fill: FreeformGradient(BackgroundFillFreeformGradient { colors: bg_colors }),
385433
}),
386434
id: 0,
387435
name: String::new(),
@@ -390,13 +438,25 @@ fn hard_coded_themes(dark: bool) -> tdlib::types::ThemeSettings {
390438
accent_color: 0,
391439
animate_outgoing_message_fill: false,
392440
outgoing_message_accent_color: 0,
393-
outgoing_message_fill: Solid(BackgroundFillSolid { color: 0 }),
441+
outgoing_message_fill: FreeformGradient(BackgroundFillFreeformGradient {
442+
colors: message_colors,
443+
}),
394444
}
395445
}
396446

447+
// tr tl bl br
448+
397449
if dark {
398-
theme(dark, vec![0xd6932e, 0xbc40db, 0x4280d7, 0x614ed5])
450+
theme(
451+
dark,
452+
vec![0xd6932e, 0xbc40db, 0x4280d7, 0x614ed5],
453+
vec![0xfc27a6, 0xff9201, 0x7827ff, 0x554efe],
454+
)
399455
} else {
400-
theme(dark, vec![0x94dae9, 0x9aeddb, 0x94c3f6, 0xac96f7])
456+
theme(
457+
dark,
458+
vec![0x94dae9, 0x9aeddb, 0x94c3f6, 0xac96f7],
459+
vec![0xddffdf, 0xfff0dd, 0xffddfc, 0xddecff],
460+
)
401461
}
402462
}

src/session/content/message_row/bubble.rs

Lines changed: 24 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

@@ -64,6 +65,7 @@ mod imp {
6465
pub(super) sender_color_class: RefCell<Option<String>>,
6566
pub(super) sender_binding: RefCell<Option<gtk::ExpressionWatch>>,
6667
pub(super) parent_list_view: RefCell<glib::WeakRef<gtk::ListView>>,
68+
pub(super) parent_background: RefCell<glib::WeakRef<Background>>,
6769
#[template_child]
6870
pub(super) overlay: TemplateChild<gtk::Overlay>,
6971
#[template_child]
@@ -136,27 +138,28 @@ mod imp {
136138
widget.queue_draw();
137139
}));
138140
}
141+
142+
if let Some(background) = widget.parent_background() {
143+
self.parent_background.replace(background.downgrade());
144+
background.subscribe_to_redraw(widget.upcast_ref());
145+
}
139146
}
140147

141148
fn snapshot(&self, snapshot: &gtk::Snapshot) {
142149
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-
);
150+
151+
if let Some(background) = self.parent_background.borrow().upgrade() {
152+
if !background.has_css_class("fallback") {
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+
};
162+
}
160163
}
161164

162165
self.parent_snapshot(snapshot);
@@ -353,42 +356,10 @@ impl MessageBubble {
353356
}
354357

355358
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-
}
359+
self.ancestor(gtk::ListView::static_type())?.downcast().ok()
363360
}
364361

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-
}
362+
fn parent_background(&self) -> Option<Background> {
363+
self.ancestor(Background::static_type())?.downcast().ok()
393364
}
394365
}

0 commit comments

Comments
 (0)