Skip to content

Commit e780d7b

Browse files
committed
feat(chat-history): Support chat themes
1 parent 05b00cf commit e780d7b

File tree

4 files changed

+188
-31
lines changed

4 files changed

+188
-31
lines changed

src/session/content/background.rs

Lines changed: 95 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ mod imp {
6060

6161
#[derive(Default)]
6262
pub(crate) struct Background {
63+
pub(super) chat_theme: RefCell<Option<tdlib::types::ChatTheme>>,
64+
6365
pub(super) background_texture: RefCell<Option<gdk::Texture>>,
6466

6567
pub(super) last_size: Cell<(f32, f32)>,
@@ -95,10 +97,10 @@ mod imp {
9597
self.pattern.set(pattern).unwrap();
9698

9799
let style_manager = adw::StyleManager::default();
98-
obj.set_theme(hard_coded_themes(style_manager.is_dark()));
100+
obj.refresh_theme(style_manager.is_dark());
99101

100102
style_manager.connect_dark_notify(clone!(@weak obj => move |style_manager| {
101-
obj.set_theme(hard_coded_themes(style_manager.is_dark()))
103+
obj.refresh_theme(style_manager.is_dark());
102104
}));
103105

104106
if style_manager.is_high_contrast() {
@@ -234,6 +236,40 @@ mod imp {
234236
}
235237
}
236238

239+
pub(super) fn fill_node(
240+
&self,
241+
bounds: &graphene::Rect,
242+
gradient_bounds: &graphene::Rect,
243+
colors: &[graphene::Vec3],
244+
) -> gsk::RenderNode {
245+
match colors.len() {
246+
1 => gsk::ColorNode::new(&vec3_to_rgba(&colors[0]), bounds).upcast(),
247+
2 => gsk::LinearGradientNode::new(
248+
bounds,
249+
&gradient_bounds.top_left(),
250+
&gradient_bounds.bottom_left(),
251+
&[
252+
gsk::ColorStop::new(0.0, vec3_to_rgba(&colors[0])),
253+
gsk::ColorStop::new(1.0, vec3_to_rgba(&colors[1])),
254+
],
255+
)
256+
.upcast(),
257+
3 => {
258+
log::error!("Three color gradients aren't supported yet");
259+
260+
let mut colors = colors.to_vec();
261+
colors.push(colors[2]);
262+
263+
self.gradient_shader_node(bounds, gradient_bounds, &colors)
264+
.upcast()
265+
}
266+
4 => self
267+
.gradient_shader_node(bounds, gradient_bounds, colors)
268+
.upcast(),
269+
_ => unreachable!("Unsupported color count"),
270+
}
271+
}
272+
237273
pub(super) fn gradient_shader_node(
238274
&self,
239275
bounds: &graphene::Rect,
@@ -327,29 +363,41 @@ impl Background {
327363
glib::Object::new()
328364
}
329365

330-
pub(crate) fn set_theme(&self, theme: tdlib::types::ThemeSettings) {
331-
let Some(background) = theme.background else { return; };
366+
pub(crate) fn set_chat_theme(&self, theme: Option<tdlib::types::ChatTheme>) {
367+
self.imp().chat_theme.replace(theme);
368+
self.refresh_theme(adw::StyleManager::default().is_dark());
369+
}
370+
371+
pub(crate) fn set_theme(&self, theme: &tdlib::types::ThemeSettings) {
372+
let Some(background) = &theme.background else { return; };
332373
let imp = self.imp();
333374

334375
imp.dark.set(background.is_dark);
335376

336-
let bg_fill = match background.r#type {
337-
tdlib::enums::BackgroundType::Pattern(pattern) => pattern.fill,
338-
tdlib::enums::BackgroundType::Fill(fill) => fill.fill,
377+
let bg_fill = match &background.r#type {
378+
tdlib::enums::BackgroundType::Pattern(pattern) => &pattern.fill,
379+
tdlib::enums::BackgroundType::Fill(fill) => &fill.fill,
339380
tdlib::enums::BackgroundType::Wallpaper(_) => {
340381
unimplemented!("Wallpaper chat background")
341382
}
342383
};
343384

344385
imp.bg_colors.replace(fill_colors(bg_fill));
345386
imp.message_colors
346-
.replace(fill_colors(theme.outgoing_message_fill));
387+
.replace(fill_colors(&theme.outgoing_message_fill));
347388

348389
imp.background_texture.take();
349390
self.queue_draw();
350391
}
351392

352393
pub(crate) fn animate(&self) {
394+
let nothing_to_animate = self.imp().bg_colors.borrow().len() <= 2
395+
&& self.imp().message_colors.borrow().len() <= 2;
396+
397+
if nothing_to_animate {
398+
return;
399+
}
400+
353401
let animation = self.imp().animation.get().unwrap();
354402

355403
let val = animation.value();
@@ -367,21 +415,18 @@ impl Background {
367415
&self,
368416
bounds: &graphene::Rect,
369417
gradient_bounds: &graphene::Rect,
370-
) -> gsk::GLShaderNode {
418+
) -> gsk::RenderNode {
371419
self.imp()
372-
.gradient_shader_node(bounds, gradient_bounds, &self.imp().bg_colors.borrow())
420+
.fill_node(bounds, gradient_bounds, &self.imp().bg_colors.borrow())
373421
}
374422

375423
pub fn message_bg_node(
376424
&self,
377425
bounds: &graphene::Rect,
378426
gradient_bounds: &graphene::Rect,
379-
) -> gsk::GLShaderNode {
380-
self.imp().gradient_shader_node(
381-
bounds,
382-
gradient_bounds,
383-
&self.imp().message_colors.borrow(),
384-
)
427+
) -> gsk::RenderNode {
428+
self.imp()
429+
.fill_node(bounds, gradient_bounds, &self.imp().message_colors.borrow())
385430
}
386431

387432
fn ensure_shader(&self) {
@@ -403,6 +448,23 @@ impl Background {
403448
}
404449
};
405450
}
451+
452+
fn refresh_theme(&self, dark: bool) {
453+
if let Some(chat_theme) = &*self.imp().chat_theme.borrow() {
454+
let theme = if dark {
455+
&chat_theme.dark_settings
456+
} else {
457+
&chat_theme.light_settings
458+
};
459+
460+
self.set_theme(theme);
461+
462+
// For some reason tdlib tells that light theme is dark
463+
self.imp().dark.set(dark);
464+
} else {
465+
self.set_theme(&hard_coded_themes(dark));
466+
}
467+
}
406468
}
407469

408470
impl Default for Background {
@@ -411,22 +473,30 @@ impl Default for Background {
411473
}
412474
}
413475

414-
fn fill_colors(fill: tdlib::enums::BackgroundFill) -> Vec<graphene::Vec3> {
476+
fn fill_colors(fill: &tdlib::enums::BackgroundFill) -> Vec<graphene::Vec3> {
415477
match fill {
416478
tdlib::enums::BackgroundFill::FreeformGradient(gradient) if gradient.colors.len() == 4 => {
417-
gradient
418-
.colors
419-
.into_iter()
420-
.map(|int_color| {
421-
let [_, r, g, b] = int_color.to_be_bytes();
422-
graphene::Vec3::new(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0)
423-
})
424-
.collect()
479+
gradient.colors.iter().map(int_color_to_vec3).collect()
425480
}
481+
tdlib::enums::BackgroundFill::Solid(solid) => vec![int_color_to_vec3(&solid.color)],
482+
tdlib::enums::BackgroundFill::Gradient(gradient) => vec![
483+
int_color_to_vec3(&gradient.top_color),
484+
int_color_to_vec3(&gradient.bottom_color),
485+
],
426486
_ => unimplemented!("Unsupported background fill: {fill:?}"),
427487
}
428488
}
429489

490+
fn int_color_to_vec3(color: &i32) -> graphene::Vec3 {
491+
let [_, r, g, b] = color.to_be_bytes();
492+
graphene::Vec3::new(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0)
493+
}
494+
495+
fn vec3_to_rgba(vec3: &graphene::Vec3) -> gdk::RGBA {
496+
let [red, green, blue] = vec3.to_float();
497+
gdk::RGBA::new(red, green, blue, 1.0)
498+
}
499+
430500
fn hard_coded_themes(dark: bool) -> tdlib::types::ThemeSettings {
431501
fn theme(
432502
dark: bool,

src/session/content/chat_history.rs

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ mod imp {
3737
pub(crate) struct ChatHistory {
3838
pub(super) compact: Cell<bool>,
3939
pub(super) chat: RefCell<Option<Chat>>,
40-
pub(super) chat_handler: RefCell<Option<glib::SignalHandlerId>>,
40+
pub(super) chat_handlers: RefCell<Vec<glib::SignalHandlerId>>,
41+
pub(super) session_handlers: RefCell<Vec<glib::SignalHandlerId>>,
4142
pub(super) model: RefCell<Option<ChatHistoryModel>>,
4243
pub(super) message_menu: OnceCell<gtk::PopoverMenu>,
4344
pub(super) is_auto_scrolling: Cell<bool>,
@@ -357,13 +358,43 @@ impl ChatHistory {
357358
}
358359
}));
359360

360-
let handler = chat.connect_new_message(clone!(@weak self as obj => move |_, msg| {
361-
if msg.is_outgoing() {
362-
obj.imp().background.animate();
361+
self.imp().background.set_chat_theme(chat.chat_theme());
362+
363+
let new_message_handler =
364+
chat.connect_new_message(clone!(@weak self as obj => move |_, msg| {
365+
if msg.is_outgoing() {
366+
obj.imp().background.animate();
367+
}
368+
}));
369+
370+
let chat_theme_handler = chat.connect_notify_local(
371+
Some("theme-name"),
372+
clone!(@weak self as obj => move |chat, _| {
373+
obj.imp().background.set_chat_theme(chat.chat_theme());
374+
}),
375+
);
376+
377+
for old_handler in self
378+
.imp()
379+
.chat_handlers
380+
.replace(vec![new_message_handler, chat_theme_handler])
381+
{
382+
if let Some(old_chat) = &*imp.chat.borrow() {
383+
old_chat.disconnect(old_handler);
363384
}
364-
}));
385+
}
365386

366-
if let Some(old_handler) = self.imp().chat_handler.replace(Some(handler)) {
387+
let chat_themes_handler = chat.session().connect_update_chat_themes(
388+
clone!(@weak self as obj, @weak chat => move || {
389+
obj.imp().background.set_chat_theme(chat.chat_theme());
390+
}),
391+
);
392+
393+
for old_handler in self
394+
.imp()
395+
.session_handlers
396+
.replace(vec![chat_themes_handler])
397+
{
367398
if let Some(old_chat) = &*imp.chat.borrow() {
368399
old_chat.disconnect(old_handler);
369400
}

src/session/mod.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::collections::hash_map::HashMap;
1010

1111
use adw::subclass::prelude::BinImpl;
1212
use glib::clone;
13+
use glib::subclass::Signal;
1314
use glib::Sender;
1415
use gtk::glib;
1516
use gtk::glib::WeakRef;
@@ -59,6 +60,7 @@ mod imp {
5960
pub(super) archive_chat_list: OnceCell<ChatList>,
6061
pub(super) folder_chat_lists: RefCell<HashMap<i32, ChatList>>,
6162
pub(super) chats: RefCell<HashMap<i64, Chat>>,
63+
pub(super) chat_themes: RefCell<Vec<tdlib::types::ChatTheme>>,
6264
pub(super) users: RefCell<HashMap<i64, User>>,
6365
pub(super) basic_groups: RefCell<HashMap<i64, BasicGroup>>,
6466
pub(super) supergroups: RefCell<HashMap<i64, Supergroup>>,
@@ -154,6 +156,12 @@ mod imp {
154156
PROPERTIES.as_ref()
155157
}
156158

159+
fn signals() -> &'static [Signal] {
160+
static SIGNALS: Lazy<Vec<Signal>> =
161+
Lazy::new(|| vec![Signal::builder("update-chat-themes").build()]);
162+
SIGNALS.as_ref()
163+
}
164+
157165
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
158166
match pspec.name() {
159167
"client-id" => {
@@ -227,6 +235,7 @@ impl Session {
227235
}
228236
Update::ChatTitle(ref data) => self.chat(data.chat_id).handle_update(update),
229237
Update::ChatPhoto(ref data) => self.chat(data.chat_id).handle_update(update),
238+
Update::ChatTheme(ref data) => self.chat(data.chat_id).handle_update(update),
230239
Update::ChatPermissions(ref data) => self.chat(data.chat_id).handle_update(update),
231240
Update::ChatLastMessage(ref data) => {
232241
let chat = self.chat(data.chat_id);
@@ -338,6 +347,10 @@ impl Session {
338347
Update::UserStatus(data) => {
339348
self.user(data.user_id).update_status(data.status);
340349
}
350+
Update::ChatThemes(chat_themes) => {
351+
self.imp().chat_themes.replace(chat_themes.chat_themes);
352+
self.emit_by_name::<()>("update-chat-themes", &[]);
353+
}
341354
_ => {}
342355
}
343356
}
@@ -510,6 +523,25 @@ impl Session {
510523
}
511524
}
512525

526+
pub(crate) fn find_chat_theme(&self, name: &str) -> Option<tdlib::types::ChatTheme> {
527+
self.imp()
528+
.chat_themes
529+
.borrow()
530+
.iter()
531+
.find(|theme| theme.name == name)
532+
.cloned()
533+
}
534+
535+
pub(crate) fn connect_update_chat_themes<F>(&self, callback: F) -> glib::SignalHandlerId
536+
where
537+
F: Fn() + 'static,
538+
{
539+
self.connect_local("update-chat-themes", true, move |_| {
540+
callback();
541+
None
542+
})
543+
}
544+
513545
pub(crate) fn handle_paste_action(&self) {
514546
self.imp().content.handle_paste_action();
515547
}

0 commit comments

Comments
 (0)