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    /// ## `position`
49    /// the position, in characters,
50    ///  at which to insert the new text. this is an in-out
51    ///  parameter. After the signal emission is finished, it
52    ///  should point after the newly inserted text.
53    fn connect_insert_text<F>(&self, insert_text_func: F) -> SignalHandlerId
54    where
55        F: Fn(&Self, &str, &mut i32) + 'static;
56}
57
58mod editable {
59    use crate::Editable;
60    use ffi::GtkEditable;
61    use glib::object::Cast;
62    use glib::signal::{connect_raw, SignalHandlerId};
63    use glib::translate::*;
64    use glib::IsA;
65    use libc::{c_char, c_int, c_uchar};
66    use std::ffi::CStr;
67    use std::mem::transmute;
68    use std::slice;
69    use std::str;
70
71    impl<T: IsA<Editable>> super::EditableSignals for T {
72        fn connect_changed<F>(&self, changed_func: F) -> SignalHandlerId
73        where
74            F: Fn(&Self) + 'static,
75        {
76            unsafe {
77                let f: Box<F> = Box::new(changed_func);
78                connect_raw(
79                    self.to_glib_none().0 as *mut _,
80                    b"changed\0".as_ptr() as *mut _,
81                    Some(transmute::<_, unsafe extern "C" fn()>(
82                        trampoline::<Self, F> as *const (),
83                    )),
84                    Box::into_raw(f),
85                )
86            }
87        }
88
89        fn connect_delete_text<F>(&self, delete_text_func: F) -> SignalHandlerId
90        where
91            F: Fn(&Self, i32, i32) + 'static,
92        {
93            unsafe {
94                let f: Box<F> = Box::new(delete_text_func);
95                connect_raw(
96                    self.to_glib_none().0 as *mut _,
97                    b"delete-text\0".as_ptr() as *mut _,
98                    Some(transmute::<_, unsafe extern "C" fn()>(
99                        delete_trampoline::<Self, F> as *const (),
100                    )),
101                    Box::into_raw(f),
102                )
103            }
104        }
105
106        fn connect_insert_text<F>(&self, insert_text_func: F) -> SignalHandlerId
107        where
108            F: Fn(&Self, &str, &mut i32) + 'static,
109        {
110            unsafe {
111                let f: Box<F> = Box::new(insert_text_func);
112                connect_raw(
113                    self.to_glib_none().0 as *mut _,
114                    b"insert-text\0".as_ptr() as *mut _,
115                    Some(transmute::<_, unsafe extern "C" fn()>(
116                        insert_trampoline::<Self, F> as *const (),
117                    )),
118                    Box::into_raw(f),
119                )
120            }
121        }
122    }
123
124    unsafe extern "C" fn trampoline<T, F: Fn(&T) + 'static>(this: *mut GtkEditable, f: &F)
125    where
126        T: IsA<Editable>,
127    {
128        f(Editable::from_glib_borrow(this).unsafe_cast_ref());
129    }
130
131    unsafe extern "C" fn delete_trampoline<T, F: Fn(&T, i32, i32) + 'static>(
132        this: *mut GtkEditable,
133        start_pos: c_int,
134        end_pos: c_int,
135        f: &F,
136    ) where
137        T: IsA<Editable>,
138    {
139        f(
140            Editable::from_glib_borrow(this).unsafe_cast_ref(),
141            start_pos,
142            end_pos,
143        );
144    }
145
146    unsafe extern "C" fn insert_trampoline<T, F: Fn(&T, &str, &mut i32) + 'static>(
147        this: *mut GtkEditable,
148        new_text: *mut c_char,
149        new_text_length: c_int,
150        position: *mut c_int,
151        f: &F,
152    ) where
153        T: IsA<Editable>,
154    {
155        let buf = if new_text_length == 0 {
156            &[]
157        } else if new_text_length != -1 {
158            slice::from_raw_parts(new_text as *mut c_uchar, new_text_length as usize)
159        } else {
160            CStr::from_ptr(new_text).to_bytes()
161        };
162        let string = str::from_utf8(buf).unwrap();
163        f(
164            Editable::from_glib_borrow(this).unsafe_cast_ref(),
165            string,
166            // To cast a mutable pointer into a mutable reference.
167            &mut *position,
168        );
169    }
170}
171
172pub trait SpinButtonSignals: 'static {
173    fn connect_change_value<F>(&self, change_value_func: F) -> SignalHandlerId
174    where
175        F: Fn(&Self, ScrollType) + 'static;
176    fn connect_input<F>(&self, input_func: F) -> SignalHandlerId
177    where
178        F: Fn(&Self) -> Option<Result<f64, ()>> + 'static;
179    fn connect_output<F>(&self, output_func: F) -> SignalHandlerId
180    where
181        F: Fn(&Self) -> glib::Propagation + 'static;
182    fn connect_value_changed<F>(&self, value_changed_func: F) -> SignalHandlerId
183    where
184        F: Fn(&Self) + 'static;
185    fn connect_wrapped<F>(&self, wrapped_func: F) -> SignalHandlerId
186    where
187        F: Fn(&Self) + 'static;
188}
189
190mod spin_button {
191    use crate::ScrollType;
192    use crate::SpinButton;
193    use ffi::{GtkScrollType, GtkSpinButton, GTK_INPUT_ERROR};
194    use glib::ffi::gboolean;
195    use glib::ffi::{GFALSE, GTRUE};
196    use glib::object::Cast;
197    use glib::signal::{connect_raw, SignalHandlerId};
198    use glib::translate::*;
199    use glib::IsA;
200    use libc::{c_double, c_int};
201    use std::boxed::Box as Box_;
202    use std::mem::transmute;
203
204    impl<T: IsA<SpinButton>> crate::SpinButtonSignals for T {
205        fn connect_change_value<F>(&self, change_value_func: F) -> SignalHandlerId
206        where
207            F: Fn(&Self, ScrollType) + 'static,
208        {
209            unsafe {
210                let f: Box<F> = Box::new(change_value_func);
211                connect_raw(
212                    self.to_glib_none().0 as *mut _,
213                    b"change_value\0".as_ptr() as *mut _,
214                    Some(transmute::<_, unsafe extern "C" fn()>(
215                        change_trampoline::<Self, F> as *const (),
216                    )),
217                    Box::into_raw(f),
218                )
219            }
220        }
221
222        fn connect_input<F>(&self, f: F) -> SignalHandlerId
223        where
224            F: Fn(&Self) -> Option<Result<f64, ()>> + 'static,
225        {
226            unsafe {
227                let f: Box_<F> = Box_::new(f);
228                connect_raw(
229                    self.to_glib_none().0 as *mut _,
230                    b"input\0".as_ptr() as *mut _,
231                    Some(transmute::<_, unsafe extern "C" fn()>(
232                        input_trampoline::<Self, F> as *const (),
233                    )),
234                    Box_::into_raw(f),
235                )
236            }
237        }
238
239        fn connect_output<F>(&self, output_func: F) -> SignalHandlerId
240        where
241            F: Fn(&Self) -> glib::Propagation + 'static,
242        {
243            unsafe {
244                let f: Box<F> = Box::new(output_func);
245                connect_raw(
246                    self.to_glib_none().0 as *mut _,
247                    b"output\0".as_ptr() as *mut _,
248                    Some(transmute::<_, unsafe extern "C" fn()>(
249                        output_trampoline::<Self, F> as *const (),
250                    )),
251                    Box::into_raw(f),
252                )
253            }
254        }
255
256        fn connect_value_changed<F>(&self, value_changed_func: F) -> SignalHandlerId
257        where
258            F: Fn(&Self) + 'static,
259        {
260            unsafe {
261                let f: Box<F> = Box::new(value_changed_func);
262                connect_raw(
263                    self.to_glib_none().0 as *mut _,
264                    b"value-changed\0".as_ptr() as *mut _,
265                    Some(transmute::<_, unsafe extern "C" fn()>(
266                        trampoline::<Self, F> as *const (),
267                    )),
268                    Box::into_raw(f),
269                )
270            }
271        }
272
273        fn connect_wrapped<F>(&self, wrapped_func: F) -> SignalHandlerId
274        where
275            F: Fn(&Self) + 'static,
276        {
277            unsafe {
278                let f: Box<F> = Box::new(wrapped_func);
279                connect_raw(
280                    self.to_glib_none().0 as *mut _,
281                    b"wrapped\0".as_ptr() as *mut _,
282                    Some(transmute::<_, unsafe extern "C" fn()>(
283                        trampoline::<Self, F> as *const (),
284                    )),
285                    Box::into_raw(f),
286                )
287            }
288        }
289    }
290
291    unsafe extern "C" fn change_trampoline<T, F: Fn(&T, ScrollType) + 'static>(
292        this: *mut GtkSpinButton,
293        scroll: GtkScrollType,
294        f: &F,
295    ) where
296        T: IsA<SpinButton>,
297    {
298        f(
299            SpinButton::from_glib_borrow(this).unsafe_cast_ref(),
300            from_glib(scroll),
301        )
302    }
303
304    unsafe extern "C" fn input_trampoline<T, F: Fn(&T) -> Option<Result<f64, ()>> + 'static>(
305        this: *mut GtkSpinButton,
306        new_value: *mut c_double,
307        f: &F,
308    ) -> c_int
309    where
310        T: IsA<SpinButton>,
311    {
312        match f(SpinButton::from_glib_borrow(this).unsafe_cast_ref()) {
313            Some(Ok(v)) => {
314                *new_value = v;
315                GTRUE
316            }
317            Some(Err(_)) => GTK_INPUT_ERROR,
318            None => GFALSE,
319        }
320    }
321
322    unsafe extern "C" fn output_trampoline<T, F: Fn(&T) -> glib::Propagation + 'static>(
323        this: *mut GtkSpinButton,
324        f: &F,
325    ) -> gboolean
326    where
327        T: IsA<SpinButton>,
328    {
329        f(SpinButton::from_glib_borrow(this).unsafe_cast_ref()).into_glib()
330    }
331
332    unsafe extern "C" fn trampoline<T, F: Fn(&T) + 'static>(this: *mut GtkSpinButton, f: &F)
333    where
334        T: IsA<SpinButton>,
335    {
336        f(SpinButton::from_glib_borrow(this).unsafe_cast_ref())
337    }
338}
339
340pub trait OverlaySignals: 'static {
341    fn connect_get_child_position<F>(&self, f: F) -> SignalHandlerId
342    where
343        F: Fn(&Self, &Widget) -> Option<Rectangle> + 'static;
344}
345
346mod overlay {
347    use crate::Overlay;
348    use crate::Widget;
349    use ffi::{GtkOverlay, GtkWidget};
350    use gdk::ffi::GdkRectangle;
351    use gdk::Rectangle;
352    use glib::ffi::{gboolean, gpointer};
353    use glib::object::Cast;
354    use glib::signal::{connect_raw, SignalHandlerId};
355    use glib::translate::*;
356    use glib::IsA;
357    use std::mem::transmute;
358    use std::ptr;
359
360    impl<O: IsA<Overlay>> crate::OverlaySignals for O {
361        fn connect_get_child_position<F>(&self, f: F) -> SignalHandlerId
362        where
363            F: Fn(&Self, &Widget) -> Option<Rectangle> + 'static,
364        {
365            unsafe {
366                let f: Box<F> = Box::new(f);
367                connect_raw(
368                    self.to_glib_none().0 as *mut _,
369                    b"get-child-position\0".as_ptr() as *mut _,
370                    Some(transmute::<_, unsafe extern "C" fn()>(
371                        child_position_trampoline::<Self, F> as *const (),
372                    )),
373                    Box::into_raw(f),
374                )
375            }
376        }
377    }
378
379    #[doc(alias = "get_child_position_trampoline")]
380    unsafe extern "C" fn child_position_trampoline<
381        T,
382        F: Fn(&T, &Widget) -> Option<Rectangle> + 'static,
383    >(
384        this: *mut GtkOverlay,
385        widget: *mut GtkWidget,
386        allocation: *mut GdkRectangle,
387        f: gpointer,
388    ) -> gboolean
389    where
390        T: IsA<Overlay>,
391    {
392        let f: &F = &*(f as *const F);
393        match f(
394            Overlay::from_glib_borrow(this).unsafe_cast_ref(),
395            &from_glib_borrow(widget),
396        ) {
397            Some(rect) => {
398                ptr::write(allocation, ptr::read(rect.to_glib_none().0));
399                true
400            }
401            None => false,
402        }
403        .into_glib()
404    }
405}