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, subclass::SignalId, translate::*};
4
5use crate::{
6    AccessibleRole, Shortcut, Widget, ffi, prelude::*, subclass::widget::WidgetActionIter,
7};
8
9// rustdoc-stripper-ignore-next
10/// Trait containing manually implemented methods of [`Widget`](crate::Widget).
11pub trait WidgetExtManual: IsA<Widget> + 'static {
12    /// t changing widget properties
13    /// that would trigger that (for example, changing the text of a label),
14    /// then you will have to call [`WidgetExt::queue_resize()`][crate::prelude::WidgetExt::queue_resize()] or
15    /// [`WidgetExt::queue_draw()`][crate::prelude::WidgetExt::queue_draw()] yourself.
16    ///
17    /// [`FrameClock::frame_time()`][crate::gdk::FrameClock::frame_time()] should generally be used
18    /// for timing continuous animations and
19    /// `Gdk::FrameTimings::get_predicted_presentation_time()` should be
20    /// used if you are trying to display isolated frames at particular times.
21    ///
22    /// This is a more convenient alternative to connecting directly to the
23    /// [`update`][struct@crate::gdk::FrameClock#update] signal of the frame clock, since you
24    /// don't have to worry about when a frame clock is assigned to a widget.
25    ///
26    /// To remove a tick callback, pass the ID that is returned by this function
27    /// to [`WidgetExtManual::remove()`][crate::prelude::WidgetExtManual::remove()]. Tick callbacks will be
28    /// removed automatically when the widget is destroyed, so you do not have
29    /// to remove it yourself.
30    /// ## `callback`
31    /// function
32    ///   to call for updating animations
33    ///
34    /// # Returns
35    ///
36    /// an ID for this callback
37    #[doc(alias = "gtk_widget_add_tick_callback")]
38    fn add_tick_callback<P: Fn(&Self, &gdk::FrameClock) -> ControlFlow + 'static>(
39        &self,
40        callback: P,
41    ) -> TickCallbackId {
42        let callback_data: Box<P> = Box::new(callback);
43
44        unsafe extern "C" fn callback_func<
45            O: IsA<Widget>,
46            P: Fn(&O, &gdk::FrameClock) -> ControlFlow + 'static,
47        >(
48            widget: *mut ffi::GtkWidget,
49            frame_clock: *mut gdk::ffi::GdkFrameClock,
50            user_data: glib::ffi::gpointer,
51        ) -> glib::ffi::gboolean {
52            unsafe {
53                let widget: Borrowed<Widget> = from_glib_borrow(widget);
54                let frame_clock = from_glib_borrow(frame_clock);
55                let callback: &P = &*(user_data as *mut _);
56                let res = (*callback)(widget.unsafe_cast_ref(), &frame_clock);
57                res.into_glib()
58            }
59        }
60        let callback = Some(callback_func::<Self, P> as _);
61
62        unsafe extern "C" fn notify_func<
63            O: IsA<Widget>,
64            P: Fn(&O, &gdk::FrameClock) -> ControlFlow + 'static,
65        >(
66            data: glib::ffi::gpointer,
67        ) {
68            unsafe {
69                let _callback: Box<P> = Box::from_raw(data as *mut _);
70            }
71        }
72        let destroy_call = Some(notify_func::<Self, P> as _);
73
74        let id = unsafe {
75            ffi::gtk_widget_add_tick_callback(
76                self.as_ref().to_glib_none().0,
77                callback,
78                Box::into_raw(callback_data) as *mut _,
79                destroy_call,
80            )
81        };
82        TickCallbackId {
83            id,
84            widget: self.upcast_ref().downgrade(),
85        }
86    }
87}
88
89impl<O: IsA<Widget>> WidgetExtManual for O {}
90
91#[derive(Debug)]
92pub struct TickCallbackId {
93    id: u32,
94    widget: WeakRef<Widget>,
95}
96
97impl PartialEq for TickCallbackId {
98    #[inline]
99    fn eq(&self, other: &Self) -> bool {
100        self.id == other.id
101    }
102}
103
104impl TickCallbackId {
105    /// Removes a tick callback previously registered with
106    /// [`WidgetExtManual::add_tick_callback()`][crate::prelude::WidgetExtManual::add_tick_callback()].
107    /// ## `id`
108    /// an ID returned by [`WidgetExtManual::add_tick_callback()`][crate::prelude::WidgetExtManual::add_tick_callback()]
109    #[doc(alias = "gtk_widget_remove_tick_callback")]
110    #[doc(alias = "remove_tick_callback")]
111    pub fn remove(self) {
112        if let Some(widget) = self.widget.upgrade() {
113            unsafe {
114                ffi::gtk_widget_remove_tick_callback(widget.to_glib_none().0, self.id);
115            }
116        }
117    }
118}
119
120// rustdoc-stripper-ignore-next
121/// Trait containing widget class methods that can be called on any widget type
122/// at runtime, without requiring a subclass.
123///
124/// This trait is implemented for `glib::Class<T>` for any `T: IsA<Widget>`
125/// (e.g., `Class<Widget>`, `Class<TextView>`, `Class<Button>`).
126///
127/// # Example
128///
129/// ```no_run
130/// # use gtk4 as gtk;
131/// use gtk::prelude::*;
132///
133/// let class = glib::Class::<gtk::TextView>::from_type(gtk::TextView::static_type()).unwrap();
134/// let trigger = gtk::ShortcutTrigger::parse_string("<Meta>c").unwrap();
135/// let shortcut = gtk::Shortcut::new(Some(trigger), Some(gtk::NamedAction::new("clipboard.copy")));
136/// class.add_shortcut(&shortcut);
137/// ```
138pub trait WidgetClassManualExt {
139    #[doc(alias = "gtk_widget_class_add_shortcut")]
140    fn add_shortcut(&self, shortcut: &Shortcut);
141
142    #[doc(alias = "gtk_widget_class_add_binding_action")]
143    fn add_binding_action(&self, keyval: gdk::Key, mods: gdk::ModifierType, action_name: &str);
144
145    #[doc(alias = "gtk_widget_class_install_property_action")]
146    fn install_property_action(&self, action_name: &str, property_name: &str);
147
148    #[doc(alias = "gtk_widget_class_query_action")]
149    fn query_action(&self) -> WidgetActionIter;
150
151    #[doc(alias = "gtk_widget_class_get_activate_signal")]
152    #[doc(alias = "get_activate_signal")]
153    fn activate_signal(&self) -> Option<SignalId>;
154
155    #[doc(alias = "gtk_widget_class_get_layout_manager_type")]
156    #[doc(alias = "get_layout_manager_type")]
157    fn layout_manager_type(&self) -> glib::Type;
158
159    #[doc(alias = "gtk_widget_class_get_css_name")]
160    #[doc(alias = "get_css_name")]
161    fn css_name(&self) -> glib::GString;
162
163    #[doc(alias = "gtk_widget_class_get_accessible_role")]
164    #[doc(alias = "get_accessible_role")]
165    fn accessible_role(&self) -> AccessibleRole;
166}
167
168impl<T: IsA<Widget> + glib::object::IsClass> WidgetClassManualExt for glib::Class<T> {
169    fn add_shortcut(&self, shortcut: &Shortcut) {
170        unsafe {
171            let widget_class = self as *const Self as *mut ffi::GtkWidgetClass;
172            ffi::gtk_widget_class_add_shortcut(widget_class, shortcut.to_glib_none().0);
173        }
174    }
175
176    fn add_binding_action(&self, keyval: gdk::Key, mods: gdk::ModifierType, action_name: &str) {
177        let shortcut = Shortcut::new(
178            Some(crate::KeyvalTrigger::new(keyval, mods)),
179            Some(crate::NamedAction::new(action_name)),
180        );
181        self.add_shortcut(&shortcut);
182    }
183
184    fn install_property_action(&self, action_name: &str, property_name: &str) {
185        unsafe {
186            let widget_class = self as *const Self as *mut ffi::GtkWidgetClass;
187            ffi::gtk_widget_class_install_property_action(
188                widget_class,
189                action_name.to_glib_none().0,
190                property_name.to_glib_none().0,
191            );
192        }
193    }
194
195    fn query_action(&self) -> WidgetActionIter {
196        let widget_class = self as *const Self as *mut ffi::GtkWidgetClass;
197        WidgetActionIter::new(widget_class)
198    }
199
200    fn activate_signal(&self) -> Option<SignalId> {
201        unsafe {
202            let widget_class = self as *const Self as *mut ffi::GtkWidgetClass;
203            let signal_id = ffi::gtk_widget_class_get_activate_signal(widget_class);
204            if signal_id == 0 {
205                None
206            } else {
207                Some(from_glib(signal_id))
208            }
209        }
210    }
211
212    fn layout_manager_type(&self) -> glib::Type {
213        unsafe {
214            let widget_class = self as *const Self as *mut ffi::GtkWidgetClass;
215            from_glib(ffi::gtk_widget_class_get_layout_manager_type(widget_class))
216        }
217    }
218
219    fn css_name(&self) -> glib::GString {
220        unsafe {
221            let widget_class = self as *const Self as *mut ffi::GtkWidgetClass;
222            from_glib_none(ffi::gtk_widget_class_get_css_name(widget_class))
223        }
224    }
225
226    fn accessible_role(&self) -> AccessibleRole {
227        unsafe {
228            let widget_class = self as *const Self as *mut ffi::GtkWidgetClass;
229            from_glib(ffi::gtk_widget_class_get_accessible_role(widget_class))
230        }
231    }
232}