Skip to main content

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()]. Tick callbacks will be
36    /// removed automatically when the widget is destroyed, so you do not have
37    /// to remove it yourself.
38    /// ## `callback`
39    /// function
40    ///   to call for updating animations
41    ///
42    /// # Returns
43    ///
44    /// an ID for this callback
45    #[doc(alias = "gtk_widget_add_tick_callback")]
46    fn add_tick_callback<P: Fn(&Self, &gdk::FrameClock) -> ControlFlow + 'static>(
47        &self,
48        callback: P,
49    ) -> TickCallbackId {
50        let callback_data: Box<P> = Box::new(callback);
51
52        unsafe extern "C" fn callback_func<
53            O: IsA<Widget>,
54            P: Fn(&O, &gdk::FrameClock) -> ControlFlow + 'static,
55        >(
56            widget: *mut ffi::GtkWidget,
57            frame_clock: *mut gdk::ffi::GdkFrameClock,
58            user_data: glib::ffi::gpointer,
59        ) -> glib::ffi::gboolean {
60            unsafe {
61                let widget: Borrowed<Widget> = from_glib_borrow(widget);
62                let frame_clock = from_glib_borrow(frame_clock);
63                let callback: &P = &*(user_data as *mut _);
64                let res = (*callback)(widget.unsafe_cast_ref(), &frame_clock);
65                res.into_glib()
66            }
67        }
68        let callback = Some(callback_func::<Self, P> as _);
69
70        unsafe extern "C" fn notify_func<
71            O: IsA<Widget>,
72            P: Fn(&O, &gdk::FrameClock) -> ControlFlow + 'static,
73        >(
74            data: glib::ffi::gpointer,
75        ) {
76            unsafe {
77                let _callback: Box<P> = Box::from_raw(data as *mut _);
78            }
79        }
80        let destroy_call = Some(notify_func::<Self, P> as _);
81
82        let id = unsafe {
83            ffi::gtk_widget_add_tick_callback(
84                self.as_ref().to_glib_none().0,
85                callback,
86                Box::into_raw(callback_data) as *mut _,
87                destroy_call,
88            )
89        };
90        TickCallbackId {
91            id,
92            widget: self.upcast_ref().downgrade(),
93        }
94    }
95}
96
97impl<O: IsA<Widget>> WidgetExtManual for O {}
98
99#[derive(Debug)]
100pub struct TickCallbackId {
101    id: u32,
102    widget: WeakRef<Widget>,
103}
104
105impl PartialEq for TickCallbackId {
106    #[inline]
107    fn eq(&self, other: &Self) -> bool {
108        self.id == other.id
109    }
110}
111
112impl TickCallbackId {
113    /// Removes a tick callback previously registered with
114    /// [`WidgetExtManual::add_tick_callback()`][crate::prelude::WidgetExtManual::add_tick_callback()].
115    /// ## `id`
116    /// an ID returned by [`WidgetExtManual::add_tick_callback()`][crate::prelude::WidgetExtManual::add_tick_callback()]
117    #[doc(alias = "gtk_widget_remove_tick_callback")]
118    #[doc(alias = "remove_tick_callback")]
119    pub fn remove(self) {
120        if let Some(widget) = self.widget.upgrade() {
121            unsafe {
122                ffi::gtk_widget_remove_tick_callback(widget.to_glib_none().0, self.id);
123            }
124        }
125    }
126}