1+ use adw:: prelude:: * ;
12use glib:: clone;
2- use gtk:: prelude:: * ;
33use gtk:: subclass:: prelude:: * ;
44use gtk:: { gdk, gio, glib, CompositeTemplate } ;
55use image:: io:: Reader as ImageReader ;
66use image:: ImageFormat ;
77use std:: io:: Cursor ;
8- use tdlib:: enums:: MessageContent ;
8+ use tdlib:: enums:: { MessageContent , StickerFormat } ;
99use tdlib:: types:: File ;
1010
11- use crate :: session:: content:: message_row:: {
12- MessageBase , MessageBaseImpl , MessageIndicators , StickerPicture ,
13- } ;
11+ use crate :: session:: components:: StickerPreview ;
12+ use crate :: session:: content:: message_row:: { MessageBase , MessageBaseImpl , MessageIndicators } ;
1413use crate :: tdlib:: Message ;
1514use crate :: utils:: spawn;
1615
@@ -19,14 +18,19 @@ use super::base::MessageBaseExt;
1918mod imp {
2019 use super :: * ;
2120 use once_cell:: sync:: Lazy ;
22- use std:: cell:: RefCell ;
21+ use once_cell:: unsync:: OnceCell ;
22+ use std:: cell:: { Cell , RefCell } ;
2323
2424 #[ derive( Debug , Default , CompositeTemplate ) ]
2525 #[ template( resource = "/com/github/melix99/telegrand/ui/content-message-sticker.ui" ) ]
2626 pub ( crate ) struct MessageSticker {
27+ pub ( super ) format : OnceCell < StickerFormat > ,
28+ pub ( super ) aspect_ratio : Cell < f64 > ,
2729 pub ( super ) message : RefCell < Option < Message > > ,
2830 #[ template_child]
29- pub ( super ) picture : TemplateChild < StickerPicture > ,
31+ pub ( super ) overlay : TemplateChild < gtk:: Overlay > ,
32+ #[ template_child]
33+ pub ( super ) bin : TemplateChild < adw:: Bin > ,
3034 #[ template_child]
3135 pub ( super ) indicators : TemplateChild < MessageIndicators > ,
3236 }
@@ -74,7 +78,31 @@ mod imp {
7478 }
7579 }
7680
77- impl WidgetImpl for MessageSticker { }
81+ impl WidgetImpl for MessageSticker {
82+ fn measure ( & self , orientation : gtk:: Orientation , for_size : i32 ) -> ( i32 , i32 , i32 , i32 ) {
83+ const SIZE : i32 = 208 ;
84+ let aspect_ratio = self . aspect_ratio . get ( ) ;
85+ let min_size = self . overlay . measure ( orientation, for_size) . 0 ;
86+ let size = if let gtk:: Orientation :: Horizontal = orientation {
87+ if aspect_ratio >= 1.0 {
88+ SIZE
89+ } else {
90+ ( SIZE as f64 * aspect_ratio) as i32
91+ }
92+ } else if aspect_ratio >= 1.0 {
93+ ( SIZE as f64 / aspect_ratio) as i32
94+ } else {
95+ SIZE
96+ }
97+ . max ( min_size) ;
98+
99+ ( size, size, -1 , -1 )
100+ }
101+
102+ fn size_allocate ( & self , width : i32 , height : i32 , baseline : i32 ) {
103+ self . overlay . allocate ( width, height, baseline, None ) ;
104+ }
105+ }
78106 impl MessageBaseImpl for MessageSticker { }
79107}
80108
@@ -95,15 +123,19 @@ impl MessageBaseExt for MessageSticker {
95123
96124 imp. indicators . set_message ( message. clone ( ) . upcast ( ) ) ;
97125
98- imp. picture . set_texture ( None ) ;
99-
100126 if let MessageContent :: MessageSticker ( data) = message. content ( ) . 0 {
101- self . imp ( )
102- . picture
103- . set_aspect_ratio ( data. sticker . width as f64 / data. sticker . height as f64 ) ;
127+ let sticker = data. sticker ;
104128
105- if data. sticker . sticker . local . is_downloading_completed {
106- self . load_sticker ( & data. sticker . sticker . local . path ) ;
129+ imp. format . set ( sticker. format ) . unwrap ( ) ;
130+
131+ imp. aspect_ratio
132+ . set ( sticker. width as f64 / sticker. height as f64 ) ;
133+
134+ let preview = StickerPreview :: new ( sticker. outline . clone ( ) ) ;
135+ self . imp ( ) . bin . set_child ( Some ( & preview) ) ;
136+
137+ if sticker. sticker . local . is_downloading_completed {
138+ self . load_sticker ( & sticker. sticker . local . path ) ;
107139 } else {
108140 let ( sender, receiver) =
109141 glib:: MainContext :: sync_channel :: < File > ( Default :: default ( ) , 5 ) ;
@@ -122,45 +154,67 @@ impl MessageBaseExt for MessageSticker {
122154 message
123155 . chat ( )
124156 . session ( )
125- . download_file ( data . sticker . sticker . id , sender) ;
157+ . download_file ( sticker. sticker . id , sender) ;
126158 }
127159 }
128-
129160 imp. message . replace ( Some ( message) ) ;
161+
130162 self . notify ( "message" ) ;
131163 }
132164}
133165
134166impl MessageSticker {
135167 fn load_sticker ( & self , path : & str ) {
136- let picture = & * self . imp ( ) . picture ;
137- let file = gio:: File :: for_path ( path) ;
138- spawn ( clone ! ( @weak picture => async move {
139- match file. load_bytes_future( ) . await {
140- Ok ( ( bytes, _) ) => {
141- let flat_samples = ImageReader :: with_format( Cursor :: new( bytes) , ImageFormat :: WebP )
142- . decode( )
143- . unwrap( )
144- . into_rgba8( )
145- . into_flat_samples( ) ;
146-
147- let ( stride, width, height) = flat_samples. extents( ) ;
148- let gtk_stride = stride * width;
149-
150- let bytes = glib:: Bytes :: from_owned( flat_samples. samples) ;
151- let texture = gdk:: MemoryTexture :: new(
152- width as i32 ,
153- height as i32 ,
154- gdk:: MemoryFormat :: R8g8b8a8 ,
155- & bytes,
156- gtk_stride,
157- ) ;
158- picture. set_texture( Some ( texture. upcast( ) ) ) ;
159- }
160- Err ( e) => {
161- log:: warn!( "Failed to load a sticker: {}" , e) ;
168+ let path = path. to_owned ( ) ;
169+ spawn ( clone ! ( @weak self as obj => async move {
170+ let widget: gtk:: Widget = match obj. imp( ) . format. get( ) . unwrap( ) {
171+ StickerFormat :: Tgs => {
172+ let animation = rlt:: Animation :: from_filename( & path) ;
173+ animation. set_loop( true ) ;
174+ animation. use_cache( true ) ;
175+ animation. play( ) ;
176+ animation. upcast( )
177+ }
178+ StickerFormat :: Webp => {
179+ let file = gio:: File :: for_path( & path) ;
180+ match file. load_bytes_future( ) . await {
181+ Ok ( ( bytes, _) ) => {
182+ let flat_samples =
183+ ImageReader :: with_format( Cursor :: new( bytes) , ImageFormat :: WebP )
184+ . decode( )
185+ . unwrap( )
186+ . into_rgba8( )
187+ . into_flat_samples( ) ;
188+
189+ let ( stride, width, height) = flat_samples. extents( ) ;
190+ let gtk_stride = stride * width;
191+
192+ let bytes = glib:: Bytes :: from_owned( flat_samples. samples) ;
193+ let texture = gdk:: MemoryTexture :: new(
194+ width as i32 ,
195+ height as i32 ,
196+ gdk:: MemoryFormat :: R8g8b8a8 ,
197+ & bytes,
198+ gtk_stride,
199+ ) ;
200+
201+ let picture = gtk:: Picture :: new( ) ;
202+
203+ picture. set_paintable( Some ( & texture) ) ;
204+
205+ picture. upcast( )
206+ }
207+ Err ( e) => {
208+ log:: warn!( "Failed to load a sticker: {}" , e) ;
209+ return ;
210+ }
162211 }
163212 }
213+ _ => unimplemented!( ) ,
214+ } ;
215+
216+ obj. imp( ) . bin. set_child( Some ( & widget) ) ;
217+
164218 } ) ) ;
165219 }
166220}
0 commit comments