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}