Skip to main content

gtk/
signal.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use gdk::Rectangle;
4use glib::signal::SignalHandlerId;
5
6use crate::{ScrollType, Widget};
7
8pub trait EditableSignals: 'static {
9    /// The ::changed signal is emitted at the end of a single
10    /// user-visible operation on the contents of the [`Editable`][crate::Editable].
11    ///
12    /// E.g., a paste operation that replaces the contents of the
13    /// selection will cause only one signal emission (even though it
14    /// is implemented by first deleting the selection, then inserting
15    /// the new content, and may cause multiple ::notify::text signals
16    /// to be emitted).
17    fn connect_changed<F>(&self, changed_func: F) -> SignalHandlerId
18    where
19        F: Fn(&Self) + 'static;
20    /// This signal is emitted when text is deleted from
21    /// the widget by the user. The default handler for
22    /// this signal will normally be responsible for deleting
23    /// the text, so by connecting to this signal and then
24    /// stopping the signal with `g_signal_stop_emission()`, it
25    /// is possible to modify the range of deleted text, or
26    /// prevent it from being deleted entirely. The `start_pos`
27    /// and `end_pos` parameters are interpreted as for
28    /// [`EditableExt::delete_text()`][crate::prelude::EditableExt::delete_text()].
29    /// ## `start_pos`
30    /// the starting position
31    /// ## `end_pos`
32    /// the end position
33    fn connect_delete_text<F>(&self, delete_text_func: F) -> SignalHandlerId
34    where
35        F: Fn(&Self, i32, i32) + 'static;
36    /// This signal is emitted when text is inserted into
37    /// the widget by the user. The default handler for
38    /// this signal will normally be responsible for inserting
39    /// the text, so by connecting to this signal and then
40    /// stopping the signal with `g_signal_stop_emission()`, it
41    /// is possible to modify the inserted text, or prevent
42    /// it from being inserted entirely.
43    /// ## `new_text`
44    /// the new text to insert
45    /// ## `new_text_length`
46    /// the length of the new text, in bytes,
47    ///  or -1 if new_text is nul-terminated
48    ///
49    /// # Returns
50    ///
51    ///
52    /// ## `position`
53    /// the position, in characters,
54    ///  at which to insert the new text. this is an in-out
55    ///  parameter. After the signal emission is finished, it
56    ///  should point after the newly inserted text.
57    fn connect_insert_text<F>(&self, insert_text_func: F) -> SignalHandlerId
58    where
59        F: Fn(&Self, &str, &mut i32) + 'static;
60}
61
62mod editable {
63    use crate::Editable;
64    use crate::ffi::GtkEditable;
65    use glib::object::Cast;
66    use glib::object::IsA;
67    use glib::signal::{SignalHandlerId, connect_raw};
68    use glib::translate::*;
69    use libc::{c_char, c_int, c_uchar};
70    use std::ffi::CStr;
71    use std::mem::transmute;
72    use std::slice;
73    use std::str;
74
75    impl<T: IsA<Editable>> super::EditableSignals for T {
76        fn connect_changed<F>(&self, changed_func: F) -> SignalHandlerId
77        where
78            F: Fn(&Self) + 'static,
79        {
80            unsafe {
81                let f: Box<F> = Box::new(changed_func);
82                connect_raw(
83                    self.to_glib_none().0 as *mut _,
84                    c"changed".as_ptr() as *mut _,
85                    Some(transmute::<*const (), unsafe extern "C" fn()>(
86                        trampoline::<Self, F> as *const (),
87                    )),
88                    Box::into_raw(f),
89                )
90            }
91        }
92
93        fn connect_delete_text<F>(&self, delete_text_func: F) -> SignalHandlerId
94        where
95            F: Fn(&Self, i32, i32) + 'static,
96        {
97            unsafe {
98                let f: Box<F> = Box::new(delete_text_func);
99                connect_raw(
100                    self.to_glib_none().0 as *mut _,
101                    c"delete-text".as_ptr() as *mut _,
102                    Some(transmute::<*const (), unsafe extern "C" fn()>(
103                        delete_trampoline::<Self, F> as *const (),
104                    )),
105                    Box::into_raw(f),
106                )
107            }
108        }
109
110        fn connect_insert_text<F>(&self, insert_text_func: F) -> SignalHandlerId
111        where
112            F: Fn(&Self, &str, &mut i32) + 'static,
113        {
114            unsafe {
115                let f: Box<F> = Box::new(insert_text_func);
116                connect_raw(
117                    self.to_glib_none().0 as *mut _,
118                    c"insert-text".as_ptr() as *mut _,
119                    Some(transmute::<*const (), unsafe extern "C" fn()>(
120                        insert_trampoline::<Self, F> as *const (),
121                    )),
122                    Box::into_raw(f),
123                )
124            }
125        }
126    }
127
128    unsafe extern "C" fn trampoline<T, F: Fn(&T) + 'static>(this: *mut GtkEditable, f: &F)
129    where
130        T: IsA<Editable>,
131    {
132        unsafe {
133            f(Editable::from_glib_borrow(this).unsafe_cast_ref());
134        }
135    }
136
137    unsafe extern "C" fn delete_trampoline<T, F: Fn(&T, i32, i32) + 'static>(
138        this: *mut GtkEditable,
139        start_pos: c_int,
140        end_pos: c_int,
141        f: &F,
142    ) where
143        T: IsA<Editable>,
144    {
145        unsafe {
146            f(
147                Editable::from_glib_borrow(this).unsafe_cast_ref(),
148                start_pos,
149                end_pos,
150            );
151        }
152    }
153
154    unsafe extern "C" fn insert_trampoline<T, F: Fn(&T, &str, &mut i32) + 'static>(
155        this: *mut GtkEditable,
156        new_text: *mut c_char,
157        new_text_length: c_int,
158        position: *mut c_int,
159        f: &F,
160    ) where
161        T: IsA<Editable>,
162    {
163        unsafe {
164            let buf = if new_text_length == 0 {
165                &[]
166            } else if new_text_length != -1 {
167                slice::from_raw_parts(new_text as *mut c_uchar, new_text_length as usize)
168            } else {
169                CStr::from_ptr(new_text).to_bytes()
170            };
171            let string = str::from_utf8(buf).unwrap();
172            f(
173                Editable::from_glib_borrow(this).unsafe_cast_ref(),
174                string,
175                // To cast a mutable pointer into a mutable reference.
176                &mut *position,
177            );
178        }
179    }
180}
181
182pub trait SpinButtonSignals: 'static {
183    fn connect_change_value<F>(&self, change_value_func: F) -> SignalHandlerId
184    where
185        F: Fn(&Self, ScrollType) + 'static;
186    fn connect_input<F>(&self, input_func: F) -> SignalHandlerId
187    where
188        F: Fn(&Self) -> Option<Result<f64, ()>> + 'static;
189    fn connect_output<F>(&self, output_func: F) -> SignalHandlerId
190    where
191        F: Fn(&Self) -> glib::Propagation + 'static;
192    fn connect_value_changed<F>(&self, value_changed_func: F) -> SignalHandlerId
193    where
194        F: Fn(&Self) + 'static;
195    fn connect_wrapped<F>(&self, wrapped_func: F) -> SignalHandlerId
196    where
197        F: Fn(&Self) + 'static;
198}
199
200mod spin_button {
201    use crate::ScrollType;
202    use crate::SpinButton;
203    use crate::ffi::{GTK_INPUT_ERROR, GtkScrollType, GtkSpinButton};
204    use glib::ffi::gboolean;
205    use glib::ffi::{GFALSE, GTRUE};
206    use glib::object::Cast;
207    use glib::object::IsA;
208    use glib::signal::{SignalHandlerId, connect_raw};
209    use glib::translate::*;
210    use libc::{c_double, c_int};
211    use std::boxed::Box as Box_;
212    use std::mem::transmute;
213
214    impl<T: IsA<SpinButton>> crate::SpinButtonSignals for T {
215        fn connect_change_value<F>(&self, change_value_func: F) -> SignalHandlerId
216        where
217            F: Fn(&Self, ScrollType) + 'static,
218        {
219            unsafe {
220                let f: Box<F> = Box::new(change_value_func);
221                connect_raw(
222                    self.to_glib_none().0 as *mut _,
223                    c"change_value".as_ptr() as *mut _,
224                    Some(transmute::<*const (), unsafe extern "C" fn()>(
225                        change_trampoline::<Self, F> as *const (),
226                    )),
227                    Box::into_raw(f),
228                )
229            }
230        }
231
232        fn connect_input<F>(&self, f: F) -> SignalHandlerId
233        where
234            F: Fn(&Self) -> Option<Result<f64, ()>> + 'static,
235        {
236            unsafe {
237                let f: Box_<F> = Box_::new(f);
238                connect_raw(
239                    self.to_glib_none().0 as *mut _,
240                    c"input".as_ptr() as *mut _,
241                    Some(transmute::<*const (), unsafe extern "C" fn()>(
242                        input_trampoline::<Self, F> as *const (),
243                    )),
244                    Box_::into_raw(f),
245                )
246            }
247        }
248
249        fn connect_output<F>(&self, output_func: F) -> SignalHandlerId
250        where
251            F: Fn(&Self) -> glib::Propagation + 'static,
252        {
253            unsafe {
254                let f: Box<F> = Box::new(output_func);
255                connect_raw(
256                    self.to_glib_none().0 as *mut _,
257                    c"output".as_ptr() as *mut _,
258                    Some(transmute::<*const (), unsafe extern "C" fn()>(
259                        output_trampoline::<Self, F> as *const (),
260                    )),
261                    Box::into_raw(f),
262                )
263            }
264        }
265
266        fn connect_value_changed<F>(&self, value_changed_func: F) -> SignalHandlerId
267        where
268            F: Fn(&Self) + 'static,
269        {
270            unsafe {
271                let f: Box<F> = Box::new(value_changed_func);
272                connect_raw(
273                    self.to_glib_none().0 as *mut _,
274                    c"value-changed".as_ptr() as *mut _,
275                    Some(transmute::<*const (), unsafe extern "C" fn()>(
276                        trampoline::<Self, F> as *const (),
277                    )),
278                    Box::into_raw(f),
279                )
280            }
281        }
282
283        fn connect_wrapped<F>(&self, wrapped_func: F) -> SignalHandlerId
284        where
285            F: Fn(&Self) + 'static,
286        {
287            unsafe {
288                let f: Box<F> = Box::new(wrapped_func);
289                connect_raw(
290                    self.to_glib_none().0 as *mut _,
291                    c"wrapped".as_ptr() as *mut _,
292                    Some(transmute::<*const (), unsafe extern "C" fn()>(
293                        trampoline::<Self, F> as *const (),
294                    )),
295                    Box::into_raw(f),
296                )
297            }
298        }
299    }
300
301    unsafe extern "C" fn change_trampoline<T, F: Fn(&T, ScrollType) + 'static>(
302        this: *mut GtkSpinButton,
303        scroll: GtkScrollType,
304        f: &F,
305    ) where
306        T: IsA<SpinButton>,
307    {
308        unsafe {
309            f(
310                SpinButton::from_glib_borrow(this).unsafe_cast_ref(),
311                from_glib(scroll),
312            )
313        }
314    }
315
316    unsafe extern "C" fn input_trampoline<T, F: Fn(&T) -> Option<Result<f64, ()>> + 'static>(
317        this: *mut GtkSpinButton,
318        new_value: *mut c_double,
319        f: &F,
320    ) -> c_int
321    where
322        T: IsA<SpinButton>,
323    {
324        unsafe {
325            match f(SpinButton::from_glib_borrow(this).unsafe_cast_ref()) {
326                Some(Ok(v)) => {
327                    *new_value = v;
328                    GTRUE
329                }
330                Some(Err(_)) => GTK_INPUT_ERROR,
331                None => GFALSE,
332            }
333        }
334    }
335
336    unsafe extern "C" fn output_trampoline<T, F: Fn(&T) -> glib::Propagation + 'static>(
337        this: *mut GtkSpinButton,
338        f: &F,
339    ) -> gboolean
340    where
341        T: IsA<SpinButton>,
342    {
343        unsafe { f(SpinButton::from_glib_borrow(this).unsafe_cast_ref()).into_glib() }
344    }
345
346    unsafe extern "C" fn trampoline<T, F: Fn(&T) + 'static>(this: *mut GtkSpinButton, f: &F)
347    where
348        T: IsA<SpinButton>,
349    {
350        unsafe { f(SpinButton::from_glib_borrow(this).unsafe_cast_ref()) }
351    }
352}
353
354pub trait OverlaySignals: 'static {
355    fn connect_get_child_position<F>(&self, f: F) -> SignalHandlerId
356    where
357        F: Fn(&Self, &Widget) -> Option<Rectangle> + 'static;
358}
359
360mod overlay {
361    use crate::Overlay;
362    use crate::Widget;
363    use crate::ffi::{GtkOverlay, GtkWidget};
364    use gdk::Rectangle;
365    use gdk::ffi::GdkRectangle;
366    use glib::ffi::{gboolean, gpointer};
367    use glib::object::Cast;
368    use glib::object::IsA;
369    use glib::signal::{SignalHandlerId, connect_raw};
370    use glib::translate::*;
371    use std::mem::transmute;
372    use std::ptr;
373
374    impl<O: IsA<Overlay>> crate::OverlaySignals for O {
375        fn connect_get_child_position<F>(&self, f: F) -> SignalHandlerId
376        where
377            F: Fn(&Self, &Widget) -> Option<Rectangle> + 'static,
378        {
379            unsafe {
380                let f: Box<F> = Box::new(f);
381                connect_raw(
382                    self.to_glib_none().0 as *mut _,
383                    c"get-child-position".as_ptr() as *mut _,
384                    Some(transmute::<*const (), unsafe extern "C" fn()>(
385                        child_position_trampoline::<Self, F> as *const (),
386                    )),
387                    Box::into_raw(f),
388                )
389            }
390        }
391    }
392
393    #[doc(alias = "get_child_position_trampoline")]
394    unsafe extern "C" fn child_position_trampoline<
395        T,
396        F: Fn(&T, &Widget) -> Option<Rectangle> + 'static,
397    >(
398        this: *mut GtkOverlay,
399        widget: *mut GtkWidget,
400        allocation: *mut GdkRectangle,
401        f: gpointer,
402    ) -> gboolean
403    where
404        T: IsA<Overlay>,
405    {
406        unsafe {
407            let f: &F = &*(f as *const F);
408            match f(
409                Overlay::from_glib_borrow(this).unsafe_cast_ref(),
410                &from_glib_borrow(widget),
411            ) {
412                Some(rect) => {
413                    ptr::write(allocation, ptr::read(rect.to_glib_none().0));
414                    true
415                }
416                None => false,
417            }
418            .into_glib()
419        }
420    }
421}