Skip to content

Commit 6a2f973

Browse files
committed
feat(chat-history): Make message gradient 4-color
1 parent b857a3f commit 6a2f973

2 files changed

Lines changed: 131 additions & 99 deletions

File tree

src/session/content/background.rs

Lines changed: 98 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ mod imp {
5555

5656
#[derive(Default)]
5757
pub(crate) struct Background {
58-
pub(super) gradient_texture: RefCell<Option<gdk::Texture>>,
58+
pub(super) background_texture: RefCell<Option<gdk::Texture>>,
59+
pub(super) message_texture: RefCell<Option<gdk::Texture>>,
60+
5961
pub(super) last_size: Cell<(f32, f32)>,
6062

6163
pub(super) shader: RefCell<Option<gsk::GLShader>>,
@@ -67,7 +69,8 @@ mod imp {
6769

6870
pub(super) dark: Cell<bool>,
6971

70-
pub(super) colors: RefCell<Vec<graphene::Vec3>>,
72+
pub(super) bg_colors: RefCell<Vec<graphene::Vec3>>,
73+
pub(super) message_colors: RefCell<Vec<graphene::Vec3>>,
7174
}
7275

7376
#[glib::object_subclass]
@@ -108,7 +111,7 @@ mod imp {
108111

109112
let target = adw::CallbackAnimationTarget::new(clone!(@weak obj => move |progress| {
110113
let imp = obj.imp();
111-
imp.gradient_texture.take();
114+
imp.background_texture.take();
112115
let progress = progress as f32;
113116
if progress >= 1.0 {
114117
imp.progress.set(0.0);
@@ -177,18 +180,20 @@ mod imp {
177180
size_changed: bool,
178181
) {
179182
if self.progress.get() == 0.0 {
180-
let texture = match self.gradient_texture.take() {
183+
let texture = match self.background_texture.take() {
181184
Some(texture) if !size_changed => texture,
182185
_ => {
183-
let renderer = self.obj().native().unwrap().renderer();
184-
renderer.render_texture(self.gradient_shader_node(bounds), Some(bounds))
186+
self.render_textures(bounds);
187+
self.background_texture.take().unwrap()
185188
}
186189
};
187190

188191
snapshot.append_texture(&texture, bounds);
189-
self.gradient_texture.replace(Some(texture));
192+
self.background_texture.replace(Some(texture));
190193
} else {
191-
snapshot.append_node(self.gradient_shader_node(bounds));
194+
self.render_textures(bounds);
195+
let texture = self.background_texture.borrow().as_ref().unwrap().clone();
196+
snapshot.append_texture(&texture, bounds);
192197
}
193198
}
194199

@@ -226,7 +231,24 @@ mod imp {
226231
}
227232
}
228233

229-
fn gradient_shader_node(&self, bounds: &graphene::Rect) -> gsk::GLShaderNode {
234+
fn render_textures(&self, bounds: &graphene::Rect) {
235+
let colors = [self.bg_colors.borrow(), self.message_colors.borrow()];
236+
237+
let renderer = self.obj().native().unwrap().renderer();
238+
239+
let mut textures = colors.into_iter().map(|colors| {
240+
renderer.render_texture(self.gradient_shader_node(bounds, &colors), Some(bounds))
241+
});
242+
243+
self.background_texture.replace(textures.next());
244+
self.message_texture.replace(textures.next());
245+
}
246+
247+
fn gradient_shader_node(
248+
&self,
249+
bounds: &graphene::Rect,
250+
colors: &[graphene::Vec3],
251+
) -> gsk::GLShaderNode {
230252
let Some(gradient_shader) = &*self.shader.borrow() else {
231253
unreachable!()
232254
};
@@ -236,10 +258,8 @@ mod imp {
236258
let progress = self.progress.get();
237259
let phase = self.phase.get() as usize;
238260

239-
let colors = self.colors.borrow();
240-
241-
let &[c1, c2, c3, c4] = &colors[..] else {
242-
unimplemented!("Unexpected color count");
261+
let &[c1, c2, c3, c4] = colors else {
262+
unimplemented!("Unexpected color count")
243263
};
244264

245265
args_builder.set_vec3(0, &c1);
@@ -305,38 +325,19 @@ impl Background {
305325

306326
imp.dark.set(background.is_dark);
307327

308-
let fill = match background.r#type {
328+
let bg_fill = match background.r#type {
309329
tdlib::enums::BackgroundType::Pattern(pattern) => pattern.fill,
310330
tdlib::enums::BackgroundType::Fill(fill) => fill.fill,
311331
tdlib::enums::BackgroundType::Wallpaper(_) => {
312332
unimplemented!("Wallpaper chat background")
313333
}
314334
};
315335

316-
match fill {
317-
tdlib::enums::BackgroundFill::FreeformGradient(gradient) => {
318-
if gradient.colors.len() != 4 {
319-
unimplemented!("Unsupported gradient colors count");
320-
}
321-
322-
let colors = gradient
323-
.colors
324-
.into_iter()
325-
.map(|int_color| {
326-
let r = (int_color >> 16) & 0xFF;
327-
let g = (int_color >> 8) & 0xFF;
328-
let b = int_color & 0xFF;
336+
imp.bg_colors.replace(fill_colors(bg_fill));
337+
imp.message_colors
338+
.replace(fill_colors(theme.outgoing_message_fill));
329339

330-
graphene::Vec3::new(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0)
331-
})
332-
.collect();
333-
334-
imp.colors.replace(colors);
335-
}
336-
_ => unimplemented!("Background fill"),
337-
}
338-
339-
imp.gradient_texture.take();
340+
imp.background_texture.take();
340341
self.queue_draw();
341342
}
342343

@@ -349,6 +350,29 @@ impl Background {
349350
}
350351
}
351352

353+
pub fn subscribe_to_redraw(&self, child: &gtk::Widget) {
354+
let animation = self.imp().animation.get().unwrap();
355+
animation.connect_value_notify(clone!(@weak child => move |_| child.queue_draw()));
356+
}
357+
358+
pub fn bg_texture(&self) -> gdk::Texture {
359+
self.imp()
360+
.background_texture
361+
.borrow()
362+
.as_ref()
363+
.unwrap()
364+
.clone()
365+
}
366+
367+
pub fn message_texture(&self) -> gdk::Texture {
368+
self.imp()
369+
.message_texture
370+
.borrow()
371+
.as_ref()
372+
.unwrap()
373+
.clone()
374+
}
375+
352376
fn ensure_shader(&self) {
353377
let imp = self.imp();
354378
if imp.shader.borrow().is_none() {
@@ -376,8 +400,28 @@ impl Default for Background {
376400
}
377401
}
378402

403+
fn fill_colors(fill: tdlib::enums::BackgroundFill) -> Vec<graphene::Vec3> {
404+
match fill {
405+
tdlib::enums::BackgroundFill::FreeformGradient(gradient) if gradient.colors.len() == 4 => {
406+
gradient
407+
.colors
408+
.into_iter()
409+
.map(|int_color| {
410+
let [_, r, g, b] = int_color.to_be_bytes();
411+
graphene::Vec3::new(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0)
412+
})
413+
.collect()
414+
}
415+
_ => unimplemented!("Unsupported background fill: {fill:?}"),
416+
}
417+
}
418+
379419
fn hard_coded_themes(dark: bool) -> tdlib::types::ThemeSettings {
380-
fn theme(dark: bool, colors: Vec<i32>) -> tdlib::types::ThemeSettings {
420+
fn theme(
421+
dark: bool,
422+
bg_colors: Vec<i32>,
423+
message_colors: Vec<i32>,
424+
) -> tdlib::types::ThemeSettings {
381425
use tdlib::enums::BackgroundFill::*;
382426
use tdlib::enums::BackgroundType::Fill;
383427
use tdlib::types::*;
@@ -387,7 +431,7 @@ fn hard_coded_themes(dark: bool) -> tdlib::types::ThemeSettings {
387431
is_default: true,
388432
is_dark: dark,
389433
r#type: Fill(BackgroundTypeFill {
390-
fill: FreeformGradient(BackgroundFillFreeformGradient { colors }),
434+
fill: FreeformGradient(BackgroundFillFreeformGradient { colors: bg_colors }),
391435
}),
392436
id: 0,
393437
name: String::new(),
@@ -396,13 +440,25 @@ fn hard_coded_themes(dark: bool) -> tdlib::types::ThemeSettings {
396440
accent_color: 0,
397441
animate_outgoing_message_fill: false,
398442
outgoing_message_accent_color: 0,
399-
outgoing_message_fill: Solid(BackgroundFillSolid { color: 0 }),
443+
outgoing_message_fill: FreeformGradient(BackgroundFillFreeformGradient {
444+
colors: message_colors,
445+
}),
400446
}
401447
}
402448

449+
// tr tl bl br
450+
403451
if dark {
404-
theme(dark, vec![0xd6932e, 0xbc40db, 0x4280d7, 0x614ed5])
452+
theme(
453+
dark,
454+
vec![0xd6932e, 0xbc40db, 0x4280d7, 0x614ed5],
455+
vec![0xfc27a6, 0xff9201, 0x7827ff, 0x554efe],
456+
)
405457
} else {
406-
theme(dark, vec![0x94dae9, 0x9aeddb, 0x94c3f6, 0xac96f7])
458+
theme(
459+
dark,
460+
vec![0x94dae9, 0x9aeddb, 0x94c3f6, 0xac96f7],
461+
vec![0xddffdf, 0xfff0dd, 0xffddfc, 0xddecff],
462+
)
407463
}
408464
}

src/session/content/message_row/bubble.rs

Lines changed: 33 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
1-
use adw::prelude::*;
2-
use glib::clone;
3-
use gtk::subclass::prelude::*;
4-
use gtk::{gdk, glib, graphene, gsk, CompositeTemplate};
5-
use once_cell::sync::Lazy;
61
use std::cell::RefCell;
72
use std::collections::hash_map::DefaultHasher;
83
use std::hash::Hash;
94
use std::hash::Hasher;
105

6+
use adw::prelude::*;
7+
use glib::clone;
8+
use gtk::gdk;
9+
use gtk::glib;
10+
use gtk::graphene;
11+
use gtk::gsk;
12+
use gtk::subclass::prelude::*;
13+
use gtk::CompositeTemplate;
14+
use once_cell::sync::Lazy;
15+
16+
use crate::session::content::background::Background;
1117
use crate::session::content::message_row::MessageIndicators;
1218
use crate::session::content::message_row::MessageLabel;
1319
use crate::session::content::message_row::MessageReply;
@@ -71,6 +77,7 @@ mod imp {
7177
pub(super) sender_color_class: RefCell<Option<String>>,
7278
pub(super) sender_binding: RefCell<Option<gtk::ExpressionWatch>>,
7379
pub(super) parent_list_view: RefCell<glib::WeakRef<gtk::ListView>>,
80+
pub(super) parent_background: RefCell<glib::WeakRef<Background>>,
7481
#[template_child]
7582
pub(super) overlay: TemplateChild<gtk::Overlay>,
7683
#[template_child]
@@ -143,27 +150,28 @@ mod imp {
143150
widget.queue_draw();
144151
}));
145152
}
153+
154+
if let Some(background) = widget.parent_background() {
155+
self.parent_background.replace(background.downgrade());
156+
background.subscribe_to_redraw(widget.upcast_ref());
157+
}
146158
}
147159

148160
fn snapshot(&self, snapshot: &gtk::Snapshot) {
149161
let widget = self.obj();
150-
if widget.has_css_class("outgoing") {
151-
let width = widget.width() as f32;
152-
let height = widget.height() as f32;
153-
154-
let bounds = graphene::Rect::new(0.0, 0.0, width, height);
155-
let gradient_bounds = widget.gradient_bounds();
156-
let [first, second] = widget.linear_gradient_colors();
157-
158-
snapshot.append_linear_gradient(
159-
&bounds,
160-
&graphene::Point::new(0.0, gradient_bounds.y()),
161-
&graphene::Point::new(0.0, gradient_bounds.height()),
162-
&[
163-
gsk::ColorStop::new(0.0, first),
164-
gsk::ColorStop::new(1.0, second),
165-
],
166-
);
162+
163+
if let Some(background) = self.parent_background.borrow().upgrade() {
164+
if !background.has_css_class("fallback") {
165+
let gradient_bounds = background.compute_bounds(self.obj().as_ref()).unwrap();
166+
167+
if widget.has_css_class("outgoing") {
168+
snapshot.append_texture(&background.message_texture(), &gradient_bounds);
169+
} else {
170+
snapshot.push_opacity(0.1);
171+
snapshot.append_texture(&background.bg_texture(), &gradient_bounds);
172+
snapshot.pop();
173+
};
174+
}
167175
}
168176

169177
self.parent_snapshot(snapshot);
@@ -360,42 +368,10 @@ impl MessageBubble {
360368
}
361369

362370
fn parent_list_view(&self) -> Option<gtk::ListView> {
363-
let mut parent = self.parent()?;
364-
loop {
365-
match parent.downcast() {
366-
Ok(list_view) => return Some(list_view),
367-
Err(not_list_view) => parent = not_list_view.parent()?,
368-
}
369-
}
371+
self.ancestor(gtk::ListView::static_type())?.downcast().ok()
370372
}
371373

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

0 commit comments

Comments
 (0)