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