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