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