Skip to content

Commit 595aa2f

Browse files
committed
rlottie: Add support of animated emojis
1 parent a5ab736 commit 595aa2f

File tree

9 files changed

+218
-172
lines changed

9 files changed

+218
-172
lines changed

.editorconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ trim_trailing_whitespace = true
77
insert_final_newline = true
88
charset = utf-8
99

10-
[*.{build,yml,ui,yaml}]
10+
[*.{build,yml,ui,yaml,xml}]
1111
indent_size = 2

data/resources/resources.gresource.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<file compressed="true" preprocess="xml-stripblanks">ui/content-chat-info-window.ui</file>
2121
<file compressed="true" preprocess="xml-stripblanks">ui/content-event-row.ui</file>
2222
<file compressed="true" preprocess="xml-stripblanks">ui/content-message-document.ui</file>
23+
<file compressed="true" preprocess="xml-stripblanks">ui/content-message-animated-emoji.ui</file>
2324
<file compressed="true" preprocess="xml-stripblanks">ui/content-message-photo.ui</file>
2425
<file compressed="true" preprocess="xml-stripblanks">ui/content-message-sticker.ui</file>
2526
<file compressed="true" preprocess="xml-stripblanks">ui/content-message-text.ui</file>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<interface>
3+
<template class="ContentMessageAnimatedEmoji" parent="ContentMessageBase">
4+
<child>
5+
<object class="GtkBox" id="gtk_box">
6+
<property name="orientation">vertical</property>
7+
<property name="vexpand">true</property>
8+
<child>
9+
<object class="GtkGestureClick" id="click">
10+
<property name="button">1</property>
11+
</object>
12+
</child>
13+
<child>
14+
<object class="AdwBin" id="bin">
15+
<property name="vexpand">true</property>
16+
</object>
17+
</child>
18+
<child>
19+
<object class="MessageIndicators" id="indicators">
20+
<property name="halign">end</property>
21+
<property name="valign">end</property>
22+
</object>
23+
</child>
24+
</object>
25+
</child>
26+
</template>
27+
</interface>

src/session/content/chat_action_bar.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ impl ChatActionBar {
250250
let chooser = gtk::EmojiChooser::new();
251251
chooser.set_parent(parent);
252252
chooser.connect_emoji_picked(clone!(@weak self as obj => move |_, emoji| {
253-
obj.imp().message_entry.insert_at_cursor(emoji);
253+
obj.imp().message_entry.insert_at_cursor(emoji.trim_end_matches('\u{fe0f}'));
254254
}));
255255
chooser.connect_hide(clone!(@weak self as obj => move |_| {
256256
obj.imp().message_entry.grab_focus();
@@ -303,9 +303,10 @@ impl ChatActionBar {
303303
client_id,
304304
)
305305
.await;
306+
306307
if let Err(e) = result {
307-
log::warn!("Error sending a message: {:?}", e);
308-
}
308+
log::warn!("Error sending a message: {:?}", e)
309+
};
309310

310311
self.reset();
311312
}
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
use adw::prelude::*;
2+
use glib::clone;
3+
use gtk::subclass::prelude::*;
4+
use gtk::{glib, CompositeTemplate};
5+
use tdlib::enums::MessageContent;
6+
use tdlib::types::File;
7+
8+
use crate::session::components::StickerPreview;
9+
use crate::session::content::message_row::{MessageBase, MessageBaseImpl, MessageIndicators};
10+
use crate::tdlib::Message;
11+
12+
use super::base::MessageBaseExt;
13+
14+
mod imp {
15+
use super::*;
16+
use once_cell::sync::Lazy;
17+
use std::cell::RefCell;
18+
19+
#[derive(Debug, Default, CompositeTemplate)]
20+
#[template(resource = "/com/github/melix99/telegrand/ui/content-message-animated-emoji.ui")]
21+
pub(crate) struct MessageAnimatedEmoji {
22+
pub(super) message: RefCell<Option<Message>>,
23+
#[template_child]
24+
pub(super) gtk_box: TemplateChild<gtk::Box>,
25+
#[template_child]
26+
pub(super) click: TemplateChild<gtk::GestureClick>,
27+
#[template_child]
28+
pub(super) bin: TemplateChild<adw::Bin>,
29+
#[template_child]
30+
pub(super) indicators: TemplateChild<MessageIndicators>,
31+
}
32+
33+
#[glib::object_subclass]
34+
impl ObjectSubclass for MessageAnimatedEmoji {
35+
const NAME: &'static str = "ContentMessageAnimatedEmoji";
36+
type Type = super::MessageAnimatedEmoji;
37+
type ParentType = MessageBase;
38+
39+
fn class_init(klass: &mut Self::Class) {
40+
Self::bind_template(klass);
41+
klass.set_css_name("messagesticker");
42+
}
43+
44+
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
45+
obj.init_template();
46+
}
47+
}
48+
49+
impl ObjectImpl for MessageAnimatedEmoji {
50+
fn properties() -> &'static [glib::ParamSpec] {
51+
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
52+
vec![glib::ParamSpecObject::new(
53+
"message",
54+
"Message",
55+
"The message represented by this row",
56+
Message::static_type(),
57+
glib::ParamFlags::READWRITE | glib::ParamFlags::EXPLICIT_NOTIFY,
58+
)]
59+
});
60+
PROPERTIES.as_ref()
61+
}
62+
63+
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
64+
match pspec.name() {
65+
"message" => self.obj().set_message(value.get().unwrap()),
66+
_ => unimplemented!(),
67+
}
68+
}
69+
70+
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
71+
match pspec.name() {
72+
"message" => self.message.borrow().to_value(),
73+
_ => unimplemented!(),
74+
}
75+
}
76+
77+
fn constructed(&self) {
78+
self.click
79+
.connect_released(clone!(@to-owned self as imp => move |_, _, _, _| {
80+
if let Some(animation) = imp.bin.child() {
81+
if let Ok(animation) = animation.downcast::<rlt::Animation>() {
82+
animation.play();
83+
}
84+
}
85+
}));
86+
}
87+
}
88+
89+
impl WidgetImpl for MessageAnimatedEmoji {
90+
fn measure(&self, orientation: gtk::Orientation, for_size: i32) -> (i32, i32, i32, i32) {
91+
const SIZE: i32 = 120;
92+
let min = self.gtk_box.measure(orientation, for_size).0;
93+
if orientation == gtk::Orientation::Horizontal {
94+
(SIZE, SIZE, -1, -1)
95+
} else {
96+
(SIZE + min, SIZE + min, -1, -1)
97+
}
98+
}
99+
100+
fn size_allocate(&self, width: i32, height: i32, baseline: i32) {
101+
self.gtk_box.allocate(width, height, baseline, None);
102+
}
103+
}
104+
impl MessageBaseImpl for MessageAnimatedEmoji {}
105+
}
106+
107+
glib::wrapper! {
108+
pub(crate) struct MessageAnimatedEmoji(ObjectSubclass<imp::MessageAnimatedEmoji>)
109+
@extends gtk::Widget, MessageBase;
110+
}
111+
112+
impl MessageBaseExt for MessageAnimatedEmoji {
113+
type Message = Message;
114+
115+
fn set_message(&self, message: Self::Message) {
116+
let imp = self.imp();
117+
118+
if imp.message.borrow().as_ref() == Some(&message) {
119+
return;
120+
}
121+
122+
imp.indicators.set_message(message.clone().upcast());
123+
124+
if let MessageContent::MessageAnimatedEmoji(data) = message.content().0 {
125+
let data = data.animated_emoji;
126+
127+
let sticker = data.sticker.unwrap();
128+
129+
let preview = StickerPreview::new(sticker.outline.clone());
130+
self.imp().bin.set_child(Some(&preview));
131+
132+
let file = sticker.sticker;
133+
134+
let looped = matches!(
135+
sticker.full_type,
136+
tdlib::enums::StickerFullType::CustomEmoji(_)
137+
);
138+
139+
if file.local.is_downloading_completed {
140+
self.load_sticker(&file.local.path, looped)
141+
} else {
142+
let (sender, receiver) =
143+
glib::MainContext::sync_channel::<File>(Default::default(), 5);
144+
145+
receiver.attach(
146+
None,
147+
clone!(@weak self as obj => @default-return glib::Continue(false), move |file| {
148+
if file.local.is_downloading_completed {
149+
obj.load_sticker(&file.local.path, looped);
150+
}
151+
152+
glib::Continue(true)
153+
}),
154+
);
155+
156+
message.chat().session().download_file(file.id, sender);
157+
}
158+
}
159+
160+
imp.message.replace(Some(message));
161+
162+
self.notify("message");
163+
}
164+
}
165+
166+
impl MessageAnimatedEmoji {
167+
fn load_sticker(&self, path: &str, looped: bool) {
168+
let path = path.to_owned();
169+
170+
let animation = rlt::Animation::from_filename(&path);
171+
animation.set_loop(looped);
172+
animation.use_cache(looped);
173+
animation.play();
174+
175+
self.imp().bin.set_child(Some(&animation));
176+
}
177+
}

src/session/content/message_row/mod.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod animated_emoji;
12
mod base;
23
mod bubble;
34
mod document;
@@ -7,10 +8,10 @@ mod label;
78
mod media_picture;
89
mod photo;
910
mod sticker;
10-
mod sticker_picture;
1111
mod text;
1212
mod video;
1313

14+
use self::animated_emoji::MessageAnimatedEmoji;
1415
use self::base::{MessageBase, MessageBaseExt, MessageBaseImpl};
1516
use self::bubble::MessageBubble;
1617
use self::document::MessageDocument;
@@ -19,7 +20,6 @@ use self::label::MessageLabel;
1920
use self::media_picture::MediaPicture;
2021
use self::photo::MessagePhoto;
2122
use self::sticker::MessageSticker;
22-
use self::sticker_picture::StickerPicture;
2323
use self::text::MessageText;
2424
use self::video::MessageVideo;
2525

@@ -316,6 +316,9 @@ impl MessageRow {
316316
MessageContent::MessageAnimation(_) /*| MessageContent::MessageVideo(_)*/ => {
317317
self.update_specific_content::<_, MessageVideo>(message_.clone());
318318
}
319+
MessageContent::MessageAnimatedEmoji(_) => {
320+
self.update_specific_content::<_, MessageAnimatedEmoji>(message_.clone());
321+
}
319322
MessageContent::MessagePhoto(_) => {
320323
self.update_specific_content::<_, MessagePhoto>(message_.clone());
321324
}

0 commit comments

Comments
 (0)