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