gtk/
signal.rs

1// Copyright 2015, The Gtk-rs Project Developers.
2// See the COPYRIGHT file at the top-level directory of this distribution.
3// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>
4
5use gdk::Rectangle;
6use glib;
7pub use glib::signal::Inhibit;
8use glib::signal::SignalHandlerId;
9use glib::SourceId;
10
11use {Continue, ScrollType, Widget};
12
13/// Adds a closure to be called by the default main loop when it's idle.
14///
15/// `func` will be called repeatedly until it returns `Continue(false)`.
16///
17/// Similar to `glib::idle_add` but only callable from the main thread and
18/// doesn't require `Send`. It is the same as `glib::idle_add_local`.
19pub fn idle_add<F>(func: F) -> SourceId
20where
21    F: FnMut() -> Continue + 'static,
22{
23    assert_initialized_main_thread!();
24    glib::idle_add_local(func)
25}
26
27/// Adds a closure to be called by the default main loop at regular intervals
28/// with millisecond granularity.
29///
30/// `func` will be called repeatedly every `interval` milliseconds until it
31/// returns `Continue(false)`. Precise timing is not guaranteed, the timeout may
32/// be delayed by other events. Prefer `timeout_add_seconds` when millisecond
33/// precision is not necessary.
34///
35/// Similar to `glib::timeout_add` but only callable from the main thread and
36/// doesn't require `Send`. It is the same as `glib::timeout_add_local`.
37pub fn timeout_add<F>(interval: u32, func: F) -> SourceId
38where
39    F: FnMut() -> Continue + 'static,
40{
41    assert_initialized_main_thread!();
42    glib::timeout_add_local(interval, func)
43}
44
45/// Adds a closure to be called by the default main loop at regular intervals
46/// with second granularity.
47///
48/// `func` will be called repeatedly every `interval` seconds until it
49/// returns `Continue(false)`. Precise timing is not guaranteed, the timeout may
50/// be delayed by other events.
51///
52/// Similar to `glib::timeout_add_seconds` but only callable from the main thread and
53/// doesn't require `Send`. It is the same as `glib::timeout_add_seconds_local`.
54pub fn timeout_add_seconds<F>(interval: u32, func: F) -> SourceId
55where
56    F: FnMut() -> Continue + 'static,
57{
58    assert_initialized_main_thread!();
59    glib::timeout_add_seconds_local(interval, func)
60}
61
62pub trait EditableSignals: 'static {
63    fn connect_changed<F>(&self, changed_func: F) -> SignalHandlerId
64    where
65        F: Fn(&Self) + 'static;
66    fn connect_delete_text<F>(&self, delete_text_func: F) -> SignalHandlerId
67    where
68        F: Fn(&Self, i32, i32) + 'static;
69    fn connect_insert_text<F>(&self, insert_text_func: F) -> SignalHandlerId
70    where
71        F: Fn(&Self, &str, &mut i32) + 'static;
72}
73
74mod editable {
75    use glib::object::Cast;
76    use glib::signal::{connect_raw, SignalHandlerId};
77    use glib::translate::*;
78    use gtk_sys::GtkEditable;
79    use libc::{c_char, c_int, c_uchar};
80    use std::ffi::CStr;
81    use std::mem::transmute;
82    use std::slice;
83    use std::str;
84    use Editable;
85    use IsA;
86
87    impl<T: IsA<Editable>> super::EditableSignals for T {
88        fn connect_changed<F>(&self, changed_func: F) -> SignalHandlerId
89        where
90            F: Fn(&Self) + 'static,
91        {
92            unsafe {
93                let f: Box<F> = Box::new(changed_func);
94                connect_raw(
95                    self.to_glib_none().0 as *mut _,
96                    b"changed\0".as_ptr() as *mut _,
97                    Some(transmute(trampoline::<Self, F> as usize)),
98                    Box::into_raw(f),
99                )
100            }
101        }
102
103        fn connect_delete_text<F>(&self, delete_text_func: F) -> SignalHandlerId
104        where
105            F: Fn(&Self, i32, i32) + 'static,
106        {
107            unsafe {
108                let f: Box<F> = Box::new(delete_text_func);
109                connect_raw(
110                    self.to_glib_none().0 as *mut _,
111                    b"delete-text\0".as_ptr() as *mut _,
112                    Some(transmute(delete_trampoline::<Self, F> as usize)),
113                    Box::into_raw(f),
114                )
115            }
116        }
117
118        fn connect_insert_text<F>(&self, insert_text_func: F) -> SignalHandlerId
119        where
120            F: Fn(&Self, &str, &mut i32) + 'static,
121        {
122            unsafe {
123                let f: Box<F> = Box::new(insert_text_func);
124                connect_raw(
125                    self.to_glib_none().0 as *mut _,
126                    b"insert-text\0".as_ptr() as *mut _,
127                    Some(transmute(insert_trampoline::<Self, F> as usize)),
128                    Box::into_raw(f),
129                )
130            }
131        }
132    }
133
134    unsafe extern "C" fn trampoline<T, F: Fn(&T) + 'static>(this: *mut GtkEditable, f: &F)
135    where
136        T: IsA<Editable>,
137    {
138        f(&Editable::from_glib_borrow(this).unsafe_cast());
139    }
140
141    unsafe extern "C" fn delete_trampoline<T, F: Fn(&T, i32, i32) + 'static>(
142        this: *mut GtkEditable,
143        start_pos: c_int,
144        end_pos: c_int,
145        f: &F,
146    ) where
147        T: IsA<Editable>,
148    {
149        f(
150            &Editable::from_glib_borrow(this).unsafe_cast(),
151            start_pos,
152            end_pos,
153        );
154    }
155
156    unsafe extern "C" fn insert_trampoline<T, F: Fn(&T, &str, &mut i32) + 'static>(
157        this: *mut GtkEditable,
158        new_text: *mut c_char,
159        new_text_length: c_int,
160        position: *mut c_int,
161        f: &F,
162    ) where
163        T: IsA<Editable>,
164    {
165        let buf = if new_text_length != -1 {
166            slice::from_raw_parts(new_text as *mut c_uchar, new_text_length as usize)
167        } else {
168            CStr::from_ptr(new_text).to_bytes()
169        };
170        let string = str::from_utf8(buf).unwrap();
171        f(
172            &Editable::from_glib_borrow(this).unsafe_cast(),
173            string,
174            transmute(position),
175        );
176    }
177}
178
179pub trait SpinButtonSignals: 'static {
180    fn connect_change_value<F>(&self, change_value_func: F) -> SignalHandlerId
181    where
182        F: Fn(&Self, ScrollType) + 'static;
183    fn connect_input<F>(&self, input_func: F) -> SignalHandlerId
184    where
185        F: Fn(&Self) -> Option<Result<f64, ()>> + 'static;
186    fn connect_output<F>(&self, output_func: F) -> SignalHandlerId
187    where
188        F: Fn(&Self) -> Inhibit + 'static;
189    fn connect_value_changed<F>(&self, value_changed_func: F) -> SignalHandlerId
190    where
191        F: Fn(&Self) + 'static;
192    fn connect_wrapped<F>(&self, wrapped_func: F) -> SignalHandlerId
193    where
194        F: Fn(&Self) + 'static;
195}
196
197mod spin_button {
198    use glib::object::Cast;
199    use glib::signal::{connect_raw, SignalHandlerId};
200    use glib::translate::*;
201    use glib::IsA;
202    use glib_sys::gboolean;
203    use glib_sys::{GFALSE, GTRUE};
204    use gtk_sys::{GtkSpinButton, GTK_INPUT_ERROR};
205    use libc::{c_double, c_int};
206    use std::boxed::Box as Box_;
207    use std::mem::transmute;
208    use Inhibit;
209    use ScrollType;
210    use SpinButton;
211
212    impl<T: IsA<SpinButton>> ::SpinButtonSignals for T {
213        fn connect_change_value<F>(&self, change_value_func: F) -> SignalHandlerId
214        where
215            F: Fn(&Self, ScrollType) + 'static,
216        {
217            unsafe {
218                let f: Box<F> = Box::new(change_value_func);
219                connect_raw(
220                    self.to_glib_none().0 as *mut _,
221                    b"change_value\0".as_ptr() as *mut _,
222                    Some(transmute(change_trampoline::<Self, F> as usize)),
223                    Box::into_raw(f),
224                )
225            }
226        }
227
228        fn connect_input<F>(&self, f: F) -> SignalHandlerId
229        where
230            F: Fn(&Self) -> Option<Result<f64, ()>> + 'static,
231        {
232            unsafe {
233                let f: Box_<F> = Box_::new(f);
234                connect_raw(
235                    self.to_glib_none().0 as *mut _,
236                    b"input\0".as_ptr() as *mut _,
237                    Some(transmute(input_trampoline::<Self, F> as usize)),
238                    Box_::into_raw(f),
239                )
240            }
241        }
242
243        fn connect_output<F>(&self, output_func: F) -> SignalHandlerId
244        where
245            F: Fn(&Self) -> Inhibit + 'static,
246        {
247            unsafe {
248                let f: Box<F> = Box::new(output_func);
249                connect_raw(
250                    self.to_glib_none().0 as *mut _,
251                    b"output\0".as_ptr() as *mut _,
252                    Some(transmute(output_trampoline::<Self, F> as usize)),
253                    Box::into_raw(f),
254                )
255            }
256        }
257
258        fn connect_value_changed<F>(&self, value_changed_func: F) -> SignalHandlerId
259        where
260            F: Fn(&Self) + 'static,
261        {
262            unsafe {
263                let f: Box<F> = Box::new(value_changed_func);
264                connect_raw(
265                    self.to_glib_none().0 as *mut _,
266                    b"value-changed\0".as_ptr() as *mut _,
267                    Some(transmute(trampoline::<Self, F> as usize)),
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(trampoline::<Self, F> as usize)),
283                    Box::into_raw(f),
284                )
285            }
286        }
287    }
288
289    unsafe extern "C" fn change_trampoline<T, F: Fn(&T, ScrollType) + 'static>(
290        this: *mut GtkSpinButton,
291        scroll: ScrollType,
292        f: &F,
293    ) where
294        T: IsA<SpinButton>,
295    {
296        f(&SpinButton::from_glib_borrow(this).unsafe_cast(), scroll)
297    }
298
299    unsafe extern "C" fn input_trampoline<T, F: Fn(&T) -> Option<Result<f64, ()>> + 'static>(
300        this: *mut GtkSpinButton,
301        new_value: *mut c_double,
302        f: &F,
303    ) -> c_int
304    where
305        T: IsA<SpinButton>,
306    {
307        match f(&SpinButton::from_glib_borrow(this).unsafe_cast()) {
308            Some(Ok(v)) => {
309                *new_value = v;
310                GTRUE
311            }
312            Some(Err(_)) => GTK_INPUT_ERROR,
313            None => GFALSE,
314        }
315    }
316
317    unsafe extern "C" fn output_trampoline<T, F: Fn(&T) -> Inhibit + 'static>(
318        this: *mut GtkSpinButton,
319        f: &F,
320    ) -> gboolean
321    where
322        T: IsA<SpinButton>,
323    {
324        f(&SpinButton::from_glib_borrow(this).unsafe_cast()).to_glib()
325    }
326
327    unsafe extern "C" fn trampoline<T, F: Fn(&T) + 'static>(this: *mut GtkSpinButton, f: &F)
328    where
329        T: IsA<SpinButton>,
330    {
331        f(&SpinButton::from_glib_borrow(this).unsafe_cast())
332    }
333}
334
335pub trait OverlaySignals: 'static {
336    fn connect_get_child_position<F>(&self, f: F) -> SignalHandlerId
337    where
338        F: Fn(&Self, &Widget) -> Option<Rectangle> + 'static;
339}
340
341mod overlay {
342    use gdk::Rectangle;
343    use gdk_sys::GdkRectangle;
344    use glib::object::Cast;
345    use glib::signal::{connect_raw, SignalHandlerId};
346    use glib::translate::*;
347    use glib_sys::{gboolean, gpointer};
348    use gtk_sys::{GtkOverlay, GtkWidget};
349    use std::mem::transmute;
350    use std::ptr;
351    use IsA;
352    use Overlay;
353    use Widget;
354
355    impl<O: IsA<Overlay>> ::OverlaySignals for O {
356        fn connect_get_child_position<F>(&self, f: F) -> SignalHandlerId
357        where
358            F: Fn(&Self, &Widget) -> Option<Rectangle> + 'static,
359        {
360            unsafe {
361                let f: Box<F> = Box::new(f);
362                connect_raw(
363                    self.to_glib_none().0 as *mut _,
364                    b"get-child-position\0".as_ptr() as *mut _,
365                    Some(transmute(get_child_position_trampoline::<Self, F> as usize)),
366                    Box::into_raw(f),
367                )
368            }
369        }
370    }
371
372    unsafe extern "C" fn get_child_position_trampoline<
373        T,
374        F: Fn(&T, &Widget) -> Option<Rectangle> + 'static,
375    >(
376        this: *mut GtkOverlay,
377        widget: *mut GtkWidget,
378        allocation: *mut GdkRectangle,
379        f: gpointer,
380    ) -> gboolean
381    where
382        T: IsA<Overlay>,
383    {
384        let f: &F = &*(f as *const F);
385        match f(
386            &Overlay::from_glib_borrow(this).unsafe_cast(),
387            &from_glib_borrow(widget),
388        ) {
389            Some(rect) => {
390                ptr::write(allocation, ptr::read(rect.to_glib_none().0));
391                true
392            }
393            None => false,
394        }
395        .to_glib()
396    }
397}