Skip to main content

gtk/
widget.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use gdk::{DragAction, Event, ModifierType};
4use glib::ffi::gboolean;
5use glib::signal::{SignalHandlerId, connect_raw};
6use glib::subclass::SignalId;
7use glib::translate::*;
8use std::mem::transmute;
9use std::num::NonZeroU32;
10use std::ptr;
11
12use crate::prelude::*;
13use crate::{DestDefaults, Rectangle, TargetEntry, Widget, ffi};
14
15pub struct TickCallbackId {
16    id: u32,
17    widget: glib::WeakRef<Widget>,
18}
19
20impl TickCallbackId {
21    #[doc(alias = "gtk_widget_remove_tick_callback")]
22    pub fn remove(self) {
23        if let Some(widget) = self.widget.upgrade() {
24            unsafe {
25                ffi::gtk_widget_remove_tick_callback(widget.to_glib_none().0, self.id);
26            }
27        }
28    }
29}
30
31mod sealed {
32    pub trait Sealed {}
33    impl<T: glib::object::IsA<crate::Widget>> Sealed for T {}
34}
35
36pub trait WidgetExtManual: IsA<Widget> + sealed::Sealed + 'static {
37    /// Determines whether an accelerator that activates the signal
38    /// identified by `signal_id` can currently be activated.
39    /// This is done by emitting the [`can-activate-accel`][struct@crate::Widget#can-activate-accel]
40    /// signal on `self`; if the signal isn’t overridden by a
41    /// handler or in a derived widget, then the default check is
42    /// that the widget must be sensitive, and the widget and all
43    /// its ancestors mapped.
44    /// ## `signal_id`
45    /// the ID of a signal installed on `self`
46    ///
47    /// # Returns
48    ///
49    /// [`true`] if the accelerator can be activated.
50    #[doc(alias = "gtk_widget_can_activate_accel")]
51    fn can_activate_accel(&self, signal_id: SignalId) -> bool {
52        unsafe {
53            from_glib(ffi::gtk_widget_can_activate_accel(
54                self.as_ref().to_glib_none().0,
55                signal_id.into_glib(),
56            ))
57        }
58    }
59
60    ///  GDK_CONTROL_MASK)
61    ///  gdk_drag_status (context, GDK_ACTION_COPY, time);
62    ///  else
63    ///  gdk_drag_status (context, GDK_ACTION_MOVE, time);
64    /// }
65    /// ]|
66    /// ## `flags`
67    /// which types of default drag behavior to use
68    /// ## `targets`
69    /// a pointer to an array of
70    ///  `GtkTargetEntrys` indicating the drop types that this `self` will
71    ///  accept, or [`None`]. Later you can access the list with
72    ///  [`WidgetExt::drag_dest_get_target_list()`][crate::prelude::WidgetExt::drag_dest_get_target_list()] and [`WidgetExt::drag_dest_find_target()`][crate::prelude::WidgetExt::drag_dest_find_target()].
73    /// ## `actions`
74    /// a bitmask of possible actions for a drop onto this `self`.
75    #[doc(alias = "gtk_drag_dest_set")]
76    fn drag_dest_set(&self, flags: DestDefaults, targets: &[TargetEntry], actions: DragAction) {
77        let stashes: Vec<_> = targets.iter().map(|e| e.to_glib_none()).collect();
78        let t: Vec<_> = stashes.iter().map(|stash| unsafe { *stash.0 }).collect();
79        let t_ptr: *mut ffi::GtkTargetEntry = if !t.is_empty() {
80            t.as_ptr() as *mut _
81        } else {
82            ptr::null_mut()
83        };
84        unsafe {
85            ffi::gtk_drag_dest_set(
86                self.as_ref().to_glib_none().0,
87                flags.into_glib(),
88                t_ptr,
89                t.len() as i32,
90                actions.into_glib(),
91            )
92        };
93    }
94
95    /// Sets up a widget so that GTK+ will start a drag operation when the user
96    /// clicks and drags on the widget. The widget must have a window.
97    /// ## `start_button_mask`
98    /// the bitmask of buttons that can start the drag
99    /// ## `targets`
100    /// the table of targets
101    ///  that the drag will support, may be [`None`]
102    /// ## `actions`
103    /// the bitmask of possible actions for a drag from this widget
104    #[doc(alias = "gtk_drag_source_set")]
105    fn drag_source_set(
106        &self,
107        start_button_mask: ModifierType,
108        targets: &[TargetEntry],
109        actions: DragAction,
110    ) {
111        let stashes: Vec<_> = targets.iter().map(|e| e.to_glib_none()).collect();
112        let t: Vec<_> = stashes.iter().map(|stash| unsafe { *stash.0 }).collect();
113        let t_ptr: *mut ffi::GtkTargetEntry = if !t.is_empty() {
114            t.as_ptr() as *mut _
115        } else {
116            ptr::null_mut()
117        };
118        unsafe {
119            ffi::gtk_drag_source_set(
120                self.as_ref().to_glib_none().0,
121                start_button_mask.into_glib(),
122                t_ptr,
123                t.len() as i32,
124                actions.into_glib(),
125            )
126        };
127    }
128
129    /// Computes the intersection of a `self`’s area and `area`, storing
130    /// the intersection in `intersection`, and returns [`true`] if there was
131    /// an intersection. `intersection` may be [`None`] if you’re only
132    /// interested in whether there was an intersection.
133    /// ## `area`
134    /// a rectangle
135    ///
136    /// # Returns
137    ///
138    /// [`true`] if there was an intersection
139    ///
140    /// ## `intersection`
141    /// rectangle to store
142    ///  intersection of `self` and `area`
143    #[doc(alias = "gtk_widget_intersect")]
144    fn intersect(&self, area: &Rectangle, mut intersection: Option<&mut Rectangle>) -> bool {
145        unsafe {
146            from_glib(ffi::gtk_widget_intersect(
147                self.as_ref().to_glib_none().0,
148                area.to_glib_none().0,
149                intersection.to_glib_none_mut().0,
150            ))
151        }
152    }
153
154    /// Determines whether an accelerator that activates the signal
155    /// identified by `signal_id` can currently be activated.
156    /// This signal is present to allow applications and derived
157    /// widgets to override the default [`Widget`][crate::Widget] handling
158    /// for determining whether an accelerator can be activated.
159    /// ## `signal_id`
160    /// the ID of a signal installed on `widget`
161    ///
162    /// # Returns
163    ///
164    /// [`true`] if the signal can be activated.
165    #[doc(alias = "can-activate-accel")]
166    fn connect_can_activate_accel<F: Fn(&Self, SignalId) -> bool + 'static>(
167        &self,
168        f: F,
169    ) -> SignalHandlerId {
170        unsafe extern "C" fn can_activate_accel_trampoline<
171            P: IsA<Widget>,
172            F: Fn(&P, SignalId) -> bool + 'static,
173        >(
174            this: *mut ffi::GtkWidget,
175            signal_id: std::ffi::c_uint,
176            f: glib::ffi::gpointer,
177        ) -> glib::ffi::gboolean {
178            unsafe {
179                if let Some(signal_id) = NonZeroU32::new(signal_id).map(|nz| SignalId::new(nz)) {
180                    let f: &F = &*(f as *const F);
181                    f(Widget::from_glib_borrow(this).unsafe_cast_ref(), signal_id).into_glib()
182                } else {
183                    false.into_glib()
184                }
185            }
186        }
187        unsafe {
188            let f: Box<F> = Box::new(f);
189            connect_raw(
190                self.as_ptr() as *mut _,
191                c"can-activate-accel".as_ptr(),
192                Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(
193                    can_activate_accel_trampoline::<Self, F> as *const (),
194                )),
195                Box::into_raw(f),
196            )
197        }
198    }
199
200    /// The ::map-event signal will be emitted when the `widget`'s window is
201    /// mapped. A window is mapped when it becomes visible on the screen.
202    ///
203    /// To receive this signal, the [`gdk::Window`][crate::gdk::Window] associated to the widget needs
204    /// to enable the [`gdk::EventMask::STRUCTURE_MASK`][crate::gdk::EventMask::STRUCTURE_MASK] mask. GDK will enable this mask
205    /// automatically for all new windows.
206    /// ## `event`
207    /// the `GdkEventAny` which triggered this signal.
208    ///
209    /// # Returns
210    ///
211    /// [`true`] to stop other handlers from being invoked for the event.
212    ///  [`false`] to propagate the event further.
213    fn connect_map_event<F: Fn(&Self, &Event) -> glib::Propagation + 'static>(
214        &self,
215        f: F,
216    ) -> SignalHandlerId {
217        unsafe extern "C" fn event_any_trampoline<
218            T,
219            F: Fn(&T, &Event) -> glib::Propagation + 'static,
220        >(
221            this: *mut ffi::GtkWidget,
222            event: *mut gdk::ffi::GdkEventAny,
223            f: &F,
224        ) -> gboolean
225        where
226            T: IsA<Widget>,
227        {
228            unsafe {
229                f(
230                    Widget::from_glib_borrow(this).unsafe_cast_ref(),
231                    &from_glib_borrow(event),
232                )
233                .into_glib()
234            }
235        }
236        unsafe {
237            let f: Box<F> = Box::new(f);
238            connect_raw(
239                self.to_glib_none().0 as *mut _,
240                c"map-event".as_ptr() as *mut _,
241                Some(transmute::<*const (), unsafe extern "C" fn()>(
242                    event_any_trampoline::<Self, F> as *const (),
243                )),
244                Box::into_raw(f),
245            )
246        }
247    }
248
249    /// The ::unmap-event signal will be emitted when the `widget`'s window is
250    /// unmapped. A window is unmapped when it becomes invisible on the screen.
251    ///
252    /// To receive this signal, the [`gdk::Window`][crate::gdk::Window] associated to the widget needs
253    /// to enable the [`gdk::EventMask::STRUCTURE_MASK`][crate::gdk::EventMask::STRUCTURE_MASK] mask. GDK will enable this mask
254    /// automatically for all new windows.
255    /// ## `event`
256    /// the `GdkEventAny` which triggered this signal
257    ///
258    /// # Returns
259    ///
260    /// [`true`] to stop other handlers from being invoked for the event.
261    ///  [`false`] to propagate the event further.
262    fn connect_unmap_event<F: Fn(&Self, &Event) -> glib::Propagation + 'static>(
263        &self,
264        f: F,
265    ) -> SignalHandlerId {
266        unsafe extern "C" fn event_any_trampoline<
267            T,
268            F: Fn(&T, &Event) -> glib::Propagation + 'static,
269        >(
270            this: *mut ffi::GtkWidget,
271            event: *mut gdk::ffi::GdkEventAny,
272            f: &F,
273        ) -> gboolean
274        where
275            T: IsA<Widget>,
276        {
277            unsafe {
278                f(
279                    Widget::from_glib_borrow(this).unsafe_cast_ref(),
280                    &from_glib_borrow(event),
281                )
282                .into_glib()
283            }
284        }
285        unsafe {
286            let f: Box<F> = Box::new(f);
287            connect_raw(
288                self.to_glib_none().0 as *mut _,
289                c"unmap-event".as_ptr() as *mut _,
290                Some(transmute::<*const (), unsafe extern "C" fn()>(
291                    event_any_trampoline::<Self, F> as *const (),
292                )),
293                Box::into_raw(f),
294            )
295        }
296    }
297
298    /// Queues an animation frame update and adds a callback to be called
299    /// before each frame. Until the tick callback is removed, it will be
300    /// called frequently (usually at the frame rate of the output device
301    /// or as quickly as the application can be repainted, whichever is
302    /// slower). For this reason, is most suitable for handling graphics
303    /// that change every frame or every few frames. The tick callback does
304    /// not automatically imply a relayout or repaint. If you want a
305    /// repaint or relayout, and aren’t changing widget properties that
306    /// would trigger that (for example, changing the text of a [`Label`][crate::Label]),
307    /// then you will have to call [`WidgetExt::queue_resize()`][crate::prelude::WidgetExt::queue_resize()] or
308    /// [`WidgetExt::queue_draw_area()`][crate::prelude::WidgetExt::queue_draw_area()] yourself.
309    ///
310    /// [`FrameClock::frame_time()`][crate::gdk::FrameClock::frame_time()] should generally be used for timing
311    /// continuous animations and
312    /// `gdk_frame_timings_get_predicted_presentation_time()` if you are
313    /// trying to display isolated frames at particular times.
314    ///
315    /// This is a more convenient alternative to connecting directly to the
316    /// [`update`][struct@crate::gdk::FrameClock#update] signal of [`gdk::FrameClock`][crate::gdk::FrameClock], since you don't
317    /// have to worry about when a [`gdk::FrameClock`][crate::gdk::FrameClock] is assigned to a widget.
318    /// ## `callback`
319    /// function to call for updating animations
320    /// ## `notify`
321    /// function to call to free `user_data` when the callback is removed.
322    ///
323    /// # Returns
324    ///
325    /// an id for the connection of this callback. Remove the callback
326    ///  by passing it to `gtk_widget_remove_tick_callback()`
327    #[doc(alias = "gtk_widget_add_tick_callback")]
328    fn add_tick_callback<P: Fn(&Self, &gdk::FrameClock) -> glib::ControlFlow + 'static>(
329        &self,
330        callback: P,
331    ) -> TickCallbackId {
332        let callback_data: Box<P> = Box::new(callback);
333
334        unsafe extern "C" fn callback_func<
335            O: IsA<Widget>,
336            P: Fn(&O, &gdk::FrameClock) -> glib::ControlFlow + 'static,
337        >(
338            widget: *mut ffi::GtkWidget,
339            frame_clock: *mut gdk::ffi::GdkFrameClock,
340            user_data: glib::ffi::gpointer,
341        ) -> glib::ffi::gboolean {
342            unsafe {
343                let widget: Borrowed<Widget> = from_glib_borrow(widget);
344                let frame_clock = from_glib_borrow(frame_clock);
345                let callback: &P = &*(user_data as *mut _);
346                let res = (*callback)(widget.unsafe_cast_ref(), &frame_clock);
347                res.into_glib()
348            }
349        }
350        let callback = Some(callback_func::<Self, P> as _);
351
352        unsafe extern "C" fn notify_func<
353            O: IsA<Widget>,
354            P: Fn(&O, &gdk::FrameClock) -> glib::ControlFlow + 'static,
355        >(
356            data: glib::ffi::gpointer,
357        ) {
358            unsafe {
359                let _callback: Box<P> = Box::from_raw(data as *mut _);
360            }
361        }
362        let destroy_call = Some(notify_func::<Self, P> as _);
363
364        let id = unsafe {
365            ffi::gtk_widget_add_tick_callback(
366                self.as_ref().to_glib_none().0,
367                callback,
368                Box::into_raw(callback_data) as *mut _,
369                destroy_call,
370            )
371        };
372        TickCallbackId {
373            id,
374            widget: self.upcast_ref().downgrade(),
375        }
376    }
377
378    /// Adds the events in the bitfield `events` to the event mask for
379    /// `self`. See [`WidgetExtManual::set_events()`][crate::prelude::WidgetExtManual::set_events()] and the
380    /// [input handling overview][event-masks] for details.
381    /// ## `events`
382    /// an event mask, see [`gdk::EventMask`][crate::gdk::EventMask]
383    #[doc(alias = "gtk_widget_add_events")]
384    fn add_events(&self, events: gdk::EventMask) {
385        unsafe {
386            ffi::gtk_widget_add_events(self.as_ref().to_glib_none().0, events.into_glib() as i32);
387        }
388    }
389
390    /// Returns the event mask (see [`gdk::EventMask`][crate::gdk::EventMask]) for the widget. These are the
391    /// events that the widget will receive.
392    ///
393    /// Note: Internally, the widget event mask will be the logical OR of the event
394    /// mask set through [`WidgetExtManual::set_events()`][crate::prelude::WidgetExtManual::set_events()] or [`WidgetExtManual::add_events()`][crate::prelude::WidgetExtManual::add_events()], and the
395    /// event mask necessary to cater for every [`EventController`][crate::EventController] created for the
396    /// widget.
397    ///
398    /// # Returns
399    ///
400    /// event mask for `self`
401    #[doc(alias = "gtk_widget_get_events")]
402    #[doc(alias = "get_events")]
403    fn events(&self) -> gdk::EventMask {
404        unsafe { from_glib(ffi::gtk_widget_get_events(self.as_ref().to_glib_none().0) as u32) }
405    }
406
407    /// Sets the event mask (see [`gdk::EventMask`][crate::gdk::EventMask]) for a widget. The event
408    /// mask determines which events a widget will receive. Keep in mind
409    /// that different widgets have different default event masks, and by
410    /// changing the event mask you may disrupt a widget’s functionality,
411    /// so be careful. This function must be called while a widget is
412    /// unrealized. Consider [`WidgetExtManual::add_events()`][crate::prelude::WidgetExtManual::add_events()] for widgets that are
413    /// already realized, or if you want to preserve the existing event
414    /// mask. This function can’t be used with widgets that have no window.
415    /// (See [`WidgetExt::has_window()`][crate::prelude::WidgetExt::has_window()]). To get events on those widgets,
416    /// place them inside a [`EventBox`][crate::EventBox] and receive events on the event
417    /// box.
418    /// ## `events`
419    /// event mask
420    #[doc(alias = "gtk_widget_set_events")]
421    fn set_events(&self, events: gdk::EventMask) {
422        unsafe {
423            ffi::gtk_widget_set_events(self.as_ref().to_glib_none().0, events.into_glib() as i32);
424        }
425    }
426
427    // rustdoc-stripper-ignore-next
428    /// Calls `gtk_widget_destroy()` on this widget.
429    ///
430    /// # Safety
431    ///
432    /// This will not necessarily entirely remove the widget from existence but
433    /// you must *NOT* query the widget's state subsequently.  Do not call this
434    /// yourself unless you really mean to.
435    #[doc(alias = "gtk_widget_destroy")]
436    unsafe fn destroy(&self) {
437        unsafe {
438            ffi::gtk_widget_destroy(self.as_ref().to_glib_none().0);
439        }
440    }
441
442    /// Utility function; intended to be connected to the [`delete-event`][struct@crate::Widget#delete-event]
443    /// signal on a [`Window`][crate::Window]. The function calls [`WidgetExt::hide()`][crate::prelude::WidgetExt::hide()] on its
444    /// argument, then returns [`true`]. If connected to ::delete-event, the
445    /// result is that clicking the close button for a window (on the
446    /// window frame, top right corner usually) will hide but not destroy
447    /// the window. By default, GTK+ destroys windows when ::delete-event
448    /// is received.
449    ///
450    /// # Returns
451    ///
452    /// [`true`]
453    #[doc(alias = "gtk_widget_hide_on_delete")]
454    fn hide_on_delete(&self) -> glib::Propagation {
455        unsafe {
456            glib::Propagation::from_glib(ffi::gtk_widget_hide_on_delete(
457                self.as_ref().to_glib_none().0,
458            ))
459        }
460    }
461}
462
463impl<O: IsA<Widget>> WidgetExtManual for O {}
464
465pub trait InitializingWidgetExt {
466    fn init_template(&self);
467}
468
469impl<T: crate::subclass::widget::WidgetImpl> InitializingWidgetExt
470    for glib::subclass::InitializingObject<T>
471{
472    fn init_template(&self) {
473        unsafe {
474            self.as_ref().unsafe_cast_ref::<Widget>().init_template();
475        }
476    }
477}