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}