gsk4/render_replay.rs
1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use crate::{ffi, RenderNode};
4use glib::{prelude::*, translate::*};
5use std::boxed::Box as Box_;
6
7/// A facility to replay a [`RenderNode`][crate::RenderNode] and its children, potentially
8/// modifying them.
9///
10/// This is a utility tool to walk a rendernode tree. The most powerful way
11/// is to provide a function via [`set_node_filter()`][Self::set_node_filter()]
12/// to filter each individual node and then run
13/// [`filter_node()`][Self::filter_node()] on the nodes you want to filter.
14///
15/// An easier method exists to just walk the node tree and extract information
16/// without any modifications. If you want to do that, the functions
17/// [`set_node_foreach()`][Self::set_node_foreach()] exists. You can also call
18/// [`foreach_node()`][Self::foreach_node()] to run that function. Note that
19/// the previously mentioned complex functionality will still be invoked if you
20/// have set up a function for it, but its result will not be returned.
21///
22/// Here is an example that combines both approaches to print the whole tree:
23///
24/// **⚠️ The following code is in c ⚠️**
25///
26/// ```c
27/// #include <gtk/gtk.h>
28///
29/// static GskRenderNode *
30/// print_nodes (GskRenderReplay *replay,
31/// GskRenderNode *node,
32/// gpointer user_data)
33/// {
34/// int *depth = user_data;
35/// GskRenderNode *result;
36///
37/// g_print ("%*s%s\n", 2 * *depth, "", g_type_name_from_instance ((GTypeInstance *) node));
38///
39/// *depth += 1;
40/// result = gsk_render_replay_default (replay, node);
41/// *depth -= 1;
42///
43/// return result;
44/// }
45///
46/// int
47/// main (int argc, char *argv[])
48/// {
49/// GFile *file;
50/// GBytes *bytes;
51/// GskRenderNode *node;
52/// GskRenderReplay *replay;
53/// int depth = 0;
54///
55/// gtk_init ();
56///
57/// if (argc < 2)
58/// {
59/// g_print ("usage: %s NODEFILE\n", argv[0]);
60/// return 0;
61/// }
62///
63/// file = g_file_new_for_commandline_arg (argv[1]);
64/// bytes = g_file_load_bytes (file, NULL, NULL, NULL);
65/// g_object_unref (file);
66/// if (bytes == NULL)
67/// return 1;
68///
69/// node = gsk_render_node_deserialize (bytes, NULL, NULL);
70/// g_bytes_unref (bytes);
71/// if (node == NULL)
72/// return 1;
73///
74/// replay = gsk_render_replay_new ();
75/// gsk_render_replay_set_node_filter (replay, print_nodes, &depth, NULL);
76/// gsk_render_node_foreach_node (replay, node);
77/// gsk_render_node_unref (node);
78///
79/// return 0;
80/// }
81/// ```
82#[repr(C)]
83#[doc(alias = "GskRenderReplay")]
84pub struct RenderReplay(std::ptr::NonNull<ffi::GskRenderReplay>);
85
86impl Drop for RenderReplay {
87 fn drop(&mut self) {
88 unsafe {
89 ffi::gsk_render_replay_free(self.0.as_ptr());
90 }
91 }
92}
93
94impl RenderReplay {
95 /// Replays the node using the default method.
96 ///
97 /// The default method calls [`filter_node()`][Self::filter_node()]
98 /// on all its child nodes and the filter functions for all its
99 /// proeprties. If none of them are changed, it returns the passed
100 /// in node. Otherwise it constructs a new node with the changed
101 /// children and properties.
102 ///
103 /// It may not be possible to construct a new node when any of the
104 /// callbacks return NULL. In that case, this function will return
105 /// NULL, too.
106 /// ## `node`
107 /// the node to replay
108 ///
109 /// # Returns
110 ///
111 /// The replayed node
112 #[doc(alias = "gsk_render_replay_default")]
113 #[allow(clippy::should_implement_trait)]
114 pub fn default(&self, node: impl AsRef<RenderNode>) -> Option<RenderNode> {
115 unsafe {
116 from_glib_full(ffi::gsk_render_replay_default(
117 self.0.as_ptr(),
118 node.as_ref().to_glib_none().0,
119 ))
120 }
121 }
122
123 /// Filters a font using the current filter function.
124 /// ## `font`
125 /// The font to filter
126 ///
127 /// # Returns
128 ///
129 /// the filtered font
130 #[doc(alias = "gsk_render_replay_filter_font")]
131 pub fn filter_font(&self, font: &impl IsA<pango::Font>) -> pango::Font {
132 unsafe {
133 from_glib_full(ffi::gsk_render_replay_filter_font(
134 self.0.as_ptr(),
135 font.as_ref().to_glib_none().0,
136 ))
137 }
138 }
139
140 /// Replays a node using the replay's filter function.
141 ///
142 /// After the replay the node may be unchanged, or it may be
143 /// removed, which will result in [`None`] being returned.
144 ///
145 /// This function calls the registered callback in the following order:
146 ///
147 /// 1. If a foreach function is set, it is called first. If it returns
148 /// false, this function immediately exits and returns the passed
149 /// in node.
150 ///
151 /// 2. If a node filter is set, it is called and its result is returned.
152 ///
153 /// 3. [`default()`][Self::default()] is called and its result is
154 /// returned.
155 /// ## `node`
156 /// the node to replay
157 ///
158 /// # Returns
159 ///
160 /// The replayed node
161 #[doc(alias = "gsk_render_replay_filter_node")]
162 pub fn filter_node(&self, node: impl AsRef<RenderNode>) -> Option<RenderNode> {
163 unsafe {
164 from_glib_full(ffi::gsk_render_replay_filter_node(
165 self.0.as_ptr(),
166 node.as_ref().to_glib_none().0,
167 ))
168 }
169 }
170
171 /// Filters a texture using the current filter function.
172 /// ## `texture`
173 /// The texture to filter
174 ///
175 /// # Returns
176 ///
177 /// the filtered texture
178 #[doc(alias = "gsk_render_replay_filter_texture")]
179 pub fn filter_texture(&self, texture: &impl IsA<gdk::Texture>) -> gdk::Texture {
180 unsafe {
181 from_glib_full(ffi::gsk_render_replay_filter_texture(
182 self.0.as_ptr(),
183 texture.as_ref().to_glib_none().0,
184 ))
185 }
186 }
187
188 /// Calls the filter and foreach functions for each node.
189 ///
190 /// This function calls [`filter_node()`][Self::filter_node()] internally,
191 /// but discards the result assuming no changes were made.
192 /// ## `node`
193 /// the node to replay
194 #[doc(alias = "gsk_render_replay_foreach_node")]
195 pub fn foreach_node(&self, node: impl AsRef<RenderNode>) {
196 unsafe {
197 ffi::gsk_render_replay_foreach_node(self.0.as_ptr(), node.as_ref().to_glib_none().0);
198 }
199 }
200
201 /// Sets a filter function to be called by [`default()`][Self::default()]
202 /// for nodes that contain fonts.
203 ///
204 /// You can call `GskRenderReplay::filter_font()` to filter
205 /// a font yourself.
206 /// ## `filter`
207 /// the font filter function
208 #[doc(alias = "gsk_render_replay_set_font_filter")]
209 pub fn set_font_filter<P: Fn(&RenderReplay, &pango::Font) -> pango::Font + 'static>(
210 &mut self,
211 filter: P,
212 ) {
213 let filter_data: Box_<P> = Box_::new(filter);
214 unsafe extern "C" fn filter_func<
215 P: Fn(&RenderReplay, &pango::Font) -> pango::Font + 'static,
216 >(
217 replay: *mut ffi::GskRenderReplay,
218 font: *mut pango::ffi::PangoFont,
219 user_data: glib::ffi::gpointer,
220 ) -> *mut pango::ffi::PangoFont {
221 let replay = RenderReplay(std::ptr::NonNull::new_unchecked(replay));
222 let font = from_glib_borrow(font);
223 let callback = &*(user_data as *mut P);
224 (*callback)(&replay, &font).into_glib_ptr()
225 }
226 let filter = Some(filter_func::<P> as _);
227 unsafe extern "C" fn user_destroy_func<
228 P: Fn(&RenderReplay, &pango::Font) -> pango::Font + 'static,
229 >(
230 data: glib::ffi::gpointer,
231 ) {
232 let _callback = Box_::from_raw(data as *mut P);
233 }
234 let destroy_call3 = Some(user_destroy_func::<P> as _);
235 let super_callback0: Box_<P> = filter_data;
236 unsafe {
237 ffi::gsk_render_replay_set_font_filter(
238 self.0.as_mut(),
239 filter,
240 Box_::into_raw(super_callback0) as *mut _,
241 destroy_call3,
242 );
243 }
244 }
245
246 #[doc(alias = "gsk_render_replay_set_font_filter")]
247 #[doc(alias = "set_font_filter")]
248 pub fn unset_font_filter(&mut self) {
249 unsafe {
250 ffi::gsk_render_replay_set_font_filter(
251 self.0.as_mut(),
252 None,
253 std::ptr::null_mut(),
254 None,
255 )
256 }
257 }
258
259 /// Sets the function to use as a node filter.
260 ///
261 /// This is the most complex function to use for replaying nodes.
262 /// It can either:
263 ///
264 /// * keep the node and just return it unchanged
265 ///
266 /// * create a replacement node and return that
267 ///
268 /// * discard the node by returning [`None`]
269 ///
270 /// * call [`default()`][Self::default()] to have the default handler
271 /// run for this node, which calls your function on its children
272 /// ## `filter`
273 /// The function to call to replay nodes
274 #[doc(alias = "gsk_render_replay_set_node_filter")]
275 pub fn set_node_filter<P: Fn(&RenderReplay, &RenderNode) -> Option<RenderNode> + 'static>(
276 &mut self,
277 filter: P,
278 ) {
279 let filter_data: Box_<P> = Box_::new(filter);
280 unsafe extern "C" fn filter_func<
281 P: Fn(&RenderReplay, &RenderNode) -> Option<RenderNode> + 'static,
282 >(
283 replay: *mut ffi::GskRenderReplay,
284 node: *mut ffi::GskRenderNode,
285 user_data: glib::ffi::gpointer,
286 ) -> *mut ffi::GskRenderNode {
287 let replay = RenderReplay(std::ptr::NonNull::new_unchecked(replay));
288 let node = from_glib_borrow(node);
289 let callback = &*(user_data as *mut P);
290 (*callback)(&replay, &node).into_glib_ptr()
291 }
292 let filter = Some(filter_func::<P> as _);
293 unsafe extern "C" fn user_destroy_func<
294 P: Fn(&RenderReplay, &RenderNode) -> Option<RenderNode> + 'static,
295 >(
296 data: glib::ffi::gpointer,
297 ) {
298 let _callback = Box_::from_raw(data as *mut P);
299 }
300 let destroy_call3 = Some(user_destroy_func::<P> as _);
301 let super_callback0: Box_<P> = filter_data;
302 unsafe {
303 ffi::gsk_render_replay_set_node_filter(
304 self.0.as_mut(),
305 filter,
306 Box_::into_raw(super_callback0) as *mut _,
307 destroy_call3,
308 );
309 }
310 }
311
312 #[doc(alias = "gsk_render_replay_set_node_filter")]
313 #[doc(alias = "set_node_filter")]
314 pub fn unset_node_filter(&mut self) {
315 unsafe {
316 ffi::gsk_render_replay_set_node_filter(
317 self.0.as_mut(),
318 None,
319 std::ptr::null_mut(),
320 None,
321 )
322 }
323 }
324
325 /// Sets the function to call for every node.
326 ///
327 /// This function is called before the node filter, so if it returns
328 /// FALSE, the node filter will never be called.
329 /// ## `foreach`
330 /// the function to call for all nodes
331 #[doc(alias = "gsk_render_replay_set_node_foreach")]
332 pub fn set_node_foreach<P: Fn(&RenderReplay, &RenderNode) -> glib::ControlFlow + 'static>(
333 &mut self,
334 foreach: P,
335 ) {
336 let foreach_data: Box_<P> = Box_::new(foreach);
337 unsafe extern "C" fn foreach_func<
338 P: Fn(&RenderReplay, &RenderNode) -> glib::ControlFlow + 'static,
339 >(
340 replay: *mut ffi::GskRenderReplay,
341 node: *mut ffi::GskRenderNode,
342 user_data: glib::ffi::gpointer,
343 ) -> glib::ffi::gboolean {
344 let replay = RenderReplay(std::ptr::NonNull::new_unchecked(replay));
345 let node = from_glib_borrow(node);
346 let callback = &*(user_data as *mut P);
347 (*callback)(&replay, &node).into_glib()
348 }
349 let foreach = Some(foreach_func::<P> as _);
350 unsafe extern "C" fn user_destroy_func<
351 P: Fn(&RenderReplay, &RenderNode) -> glib::ControlFlow + 'static,
352 >(
353 data: glib::ffi::gpointer,
354 ) {
355 let _callback = Box_::from_raw(data as *mut P);
356 }
357 let destroy_call3 = Some(user_destroy_func::<P> as _);
358 let super_callback0: Box_<P> = foreach_data;
359 unsafe {
360 ffi::gsk_render_replay_set_node_foreach(
361 self.0.as_mut(),
362 foreach,
363 Box_::into_raw(super_callback0) as *mut _,
364 destroy_call3,
365 );
366 }
367 }
368
369 #[doc(alias = "gsk_render_replay_set_node_foreach")]
370 #[doc(alias = "set_node_foreach")]
371 pub fn unset_foreach_node(&mut self) {
372 unsafe {
373 ffi::gsk_render_replay_set_node_foreach(
374 self.0.as_mut(),
375 None,
376 std::ptr::null_mut(),
377 None,
378 )
379 }
380 }
381
382 /// Sets a filter function to be called by [`default()`][Self::default()]
383 /// for nodes that contain textures.
384 ///
385 /// You can call `GskRenderReplay::filter_texture()` to filter
386 /// a texture yourself.
387 /// ## `filter`
388 /// the texture filter function
389 #[doc(alias = "gsk_render_replay_set_texture_filter")]
390 pub fn set_texture_filter<P: Fn(&RenderReplay, &gdk::Texture) -> gdk::Texture + 'static>(
391 &mut self,
392 filter: P,
393 ) {
394 let filter_data: Box_<P> = Box_::new(filter);
395 unsafe extern "C" fn filter_func<
396 P: Fn(&RenderReplay, &gdk::Texture) -> gdk::Texture + 'static,
397 >(
398 replay: *mut ffi::GskRenderReplay,
399 texture: *mut gdk::ffi::GdkTexture,
400 user_data: glib::ffi::gpointer,
401 ) -> *mut gdk::ffi::GdkTexture {
402 let replay = RenderReplay(std::ptr::NonNull::new_unchecked(replay));
403 let texture = from_glib_borrow(texture);
404 let callback = &*(user_data as *mut P);
405 (*callback)(&replay, &texture).into_glib_ptr()
406 }
407 let filter = Some(filter_func::<P> as _);
408 unsafe extern "C" fn user_destroy_func<
409 P: Fn(&RenderReplay, &gdk::Texture) -> gdk::Texture + 'static,
410 >(
411 data: glib::ffi::gpointer,
412 ) {
413 let _callback = Box_::from_raw(data as *mut P);
414 }
415 let destroy_call3 = Some(user_destroy_func::<P> as _);
416 let super_callback0: Box_<P> = filter_data;
417 unsafe {
418 ffi::gsk_render_replay_set_texture_filter(
419 self.0.as_mut(),
420 filter,
421 Box_::into_raw(super_callback0) as *mut _,
422 destroy_call3,
423 );
424 }
425 }
426
427 #[doc(alias = "gsk_render_replay_set_texture_filter")]
428 #[doc(alias = "set_texture_filter")]
429 pub fn unset_texture_filter(&mut self) {
430 unsafe {
431 ffi::gsk_render_replay_set_texture_filter(
432 self.0.as_mut(),
433 None,
434 std::ptr::null_mut(),
435 None,
436 )
437 }
438 }
439
440 /// Creates a new replay object to replay nodes.
441 ///
442 /// # Returns
443 ///
444 /// A new replay object to replay nodes
445 #[doc(alias = "gsk_render_replay_new")]
446 pub fn new() -> RenderReplay {
447 assert_initialized_main_thread!();
448 unsafe {
449 RenderReplay(std::ptr::NonNull::new_unchecked(
450 ffi::gsk_render_replay_new(),
451 ))
452 }
453 }
454}
455
456#[cfg(feature = "v4_22")]
457#[cfg_attr(docsrs, doc(cfg(feature = "v4_22")))]
458impl Default for RenderReplay {
459 fn default() -> Self {
460 Self::new()
461 }
462}