gtk4/widget.rs
1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use glib::{ControlFlow, WeakRef, translate::*};
4
5use crate::{Widget, ffi, prelude::*};
6
7// rustdoc-stripper-ignore-next
8/// Trait containing manually implemented methods of [`Widget`](crate::Widget).
9pub trait WidgetExtManual: IsA<Widget> + 'static {
10 /// Queues an animation frame update and adds a callback to be called
11 /// before each frame.
12 ///
13 /// Until the tick callback is removed, it will be called frequently
14 /// (usually at the frame rate of the output device or as quickly as
15 /// the application can be repainted, whichever is slower). For this
16 /// reason, is most suitable for handling graphics that change every
17 /// frame or every few frames.
18 ///
19 /// The tick callback does not automatically imply a relayout or repaint.
20 /// If you want a repaint or relayout, and aren’t changing widget properties
21 /// that would trigger that (for example, changing the text of a label),
22 /// then you will have to call [`WidgetExt::queue_resize()`][crate::prelude::WidgetExt::queue_resize()] or
23 /// [`WidgetExt::queue_draw()`][crate::prelude::WidgetExt::queue_draw()] yourself.
24 ///
25 /// [`FrameClock::frame_time()`][crate::gdk::FrameClock::frame_time()] should generally be used
26 /// for timing continuous animations and
27 /// `Gdk::FrameTimings::get_predicted_presentation_time()` should be
28 /// used if you are trying to display isolated frames at particular times.
29 ///
30 /// This is a more convenient alternative to connecting directly to the
31 /// [`update`][struct@crate::gdk::FrameClock#update] signal of the frame clock, since you
32 /// don't have to worry about when a frame clock is assigned to a widget.
33 ///
34 /// To remove a tick callback, pass the ID that is returned by this function
35 /// to [`WidgetExtManual::remove()`][crate::prelude::WidgetExtManual::remove()].
36 /// ## `callback`
37 /// function
38 /// to call for updating animations
39 ///
40 /// # Returns
41 ///
42 /// an ID for this callback
43 #[doc(alias = "gtk_widget_add_tick_callback")]
44 fn add_tick_callback<P: Fn(&Self, &gdk::FrameClock) -> ControlFlow + 'static>(
45 &self,
46 callback: P,
47 ) -> TickCallbackId {
48 let callback_data: Box<P> = Box::new(callback);
49
50 unsafe extern "C" fn callback_func<
51 O: IsA<Widget>,
52 P: Fn(&O, &gdk::FrameClock) -> ControlFlow + 'static,
53 >(
54 widget: *mut ffi::GtkWidget,
55 frame_clock: *mut gdk::ffi::GdkFrameClock,
56 user_data: glib::ffi::gpointer,
57 ) -> glib::ffi::gboolean {
58 unsafe {
59 let widget: Borrowed<Widget> = from_glib_borrow(widget);
60 let frame_clock = from_glib_borrow(frame_clock);
61 let callback: &P = &*(user_data as *mut _);
62 let res = (*callback)(widget.unsafe_cast_ref(), &frame_clock);
63 res.into_glib()
64 }
65 }
66 let callback = Some(callback_func::<Self, P> as _);
67
68 unsafe extern "C" fn notify_func<
69 O: IsA<Widget>,
70 P: Fn(&O, &gdk::FrameClock) -> ControlFlow + 'static,
71 >(
72 data: glib::ffi::gpointer,
73 ) {
74 unsafe {
75 let _callback: Box<P> = Box::from_raw(data as *mut _);
76 }
77 }
78 let destroy_call = Some(notify_func::<Self, P> as _);
79
80 let id = unsafe {
81 ffi::gtk_widget_add_tick_callback(
82 self.as_ref().to_glib_none().0,
83 callback,
84 Box::into_raw(callback_data) as *mut _,
85 destroy_call,
86 )
87 };
88 TickCallbackId {
89 id,
90 widget: self.upcast_ref().downgrade(),
91 }
92 }
93}
94
95impl<O: IsA<Widget>> WidgetExtManual for O {}
96
97#[derive(Debug)]
98pub struct TickCallbackId {
99 id: u32,
100 widget: WeakRef<Widget>,
101}
102
103impl PartialEq for TickCallbackId {
104 #[inline]
105 fn eq(&self, other: &Self) -> bool {
106 self.id == other.id
107 }
108}
109
110impl TickCallbackId {
111 /// Removes a tick callback previously registered with
112 /// [`WidgetExtManual::add_tick_callback()`][crate::prelude::WidgetExtManual::add_tick_callback()].
113 /// ## `id`
114 /// an ID returned by [`WidgetExtManual::add_tick_callback()`][crate::prelude::WidgetExtManual::add_tick_callback()]
115 #[doc(alias = "gtk_widget_remove_tick_callback")]
116 #[doc(alias = "remove_tick_callback")]
117 pub fn remove(self) {
118 if let Some(widget) = self.widget.upgrade() {
119 unsafe {
120 ffi::gtk_widget_remove_tick_callback(widget.to_glib_none().0, self.id);
121 }
122 }
123 }
124}