gtk4/
widget.rs

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