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