gtk4/subclass/
editable.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3// rustdoc-stripper-ignore-next
4//! Traits intended for implementing the [`Editable`](crate::Editable)
5//! interface.
6use std::sync::OnceLock;
7
8use glib::{translate::*, GString, Quark};
9use libc::{c_char, c_int};
10
11use crate::{ffi, prelude::*, subclass::prelude::*, Editable};
12
13pub trait EditableImpl: WidgetImpl {
14    fn insert_text(&self, text: &str, length: i32, position: &mut i32) {
15        self.parent_insert_text(text, length, position);
16    }
17
18    fn delete_text(&self, start_position: i32, end_position: i32) {
19        self.parent_delete_text(start_position, end_position)
20    }
21
22    fn changed(&self) {
23        self.parent_changed()
24    }
25
26    #[doc(alias = "get_text")]
27    fn text(&self) -> GString {
28        self.parent_text()
29    }
30
31    #[doc(alias = "get_delegate")]
32    fn delegate(&self) -> Option<Editable> {
33        self.parent_delegate()
34    }
35
36    fn do_insert_text(&self, text: &str, length: i32, position: &mut i32) {
37        self.parent_do_insert_text(text, length, position)
38    }
39
40    fn do_delete_text(&self, start_position: i32, end_position: i32) {
41        self.parent_do_delete_text(start_position, end_position)
42    }
43
44    #[doc(alias = "get_selection_bounds")]
45    fn selection_bounds(&self) -> Option<(i32, i32)> {
46        self.parent_selection_bounds()
47    }
48
49    fn set_selection_bounds(&self, start_position: i32, end_position: i32) {
50        self.parent_set_selection_bounds(start_position, end_position)
51    }
52}
53
54mod sealed {
55    pub trait Sealed {}
56    impl<T: super::EditableImplExt> Sealed for T {}
57}
58
59pub trait EditableImplExt: sealed::Sealed + ObjectSubclass {
60    #[doc(alias = "gtk_editable_delegate_get_property")]
61    fn delegate_get_property(
62        &self,
63        prop_id: usize,
64        pspec: &glib::ParamSpec,
65    ) -> Option<glib::Value> {
66        unsafe {
67            let mut value = glib::Value::from_type(pspec.value_type());
68
69            if from_glib(ffi::gtk_editable_delegate_get_property(
70                self.obj()
71                    .unsafe_cast_ref::<glib::Object>()
72                    .to_glib_none()
73                    .0,
74                prop_id as u32,
75                value.to_glib_none_mut().0,
76                pspec.to_glib_none().0,
77            )) {
78                Some(value)
79            } else {
80                None
81            }
82        }
83    }
84
85    #[doc(alias = "gtk_editable_delegate_set_property")]
86    fn delegate_set_property(
87        &self,
88        prop_id: usize,
89        value: &glib::Value,
90        pspec: &glib::ParamSpec,
91    ) -> bool {
92        unsafe {
93            from_glib(ffi::gtk_editable_delegate_set_property(
94                self.obj()
95                    .unsafe_cast_ref::<glib::Object>()
96                    .to_glib_none()
97                    .0,
98                prop_id as u32,
99                value.to_glib_none().0,
100                pspec.to_glib_none().0,
101            ))
102        }
103    }
104
105    fn parent_insert_text(&self, text: &str, length: i32, position: &mut i32) {
106        unsafe {
107            let type_data = Self::type_data();
108            let parent_iface = type_data.as_ref().parent_interface::<Editable>()
109                as *const ffi::GtkEditableInterface;
110
111            if let Some(func) = (*parent_iface).insert_text {
112                func(
113                    self.obj().unsafe_cast_ref::<Editable>().to_glib_none().0,
114                    text.to_glib_none().0,
115                    length,
116                    position,
117                );
118            }
119        }
120    }
121
122    fn parent_delete_text(&self, start_position: i32, end_position: i32) {
123        unsafe {
124            let type_data = Self::type_data();
125            let parent_iface = type_data.as_ref().parent_interface::<Editable>()
126                as *const ffi::GtkEditableInterface;
127
128            if let Some(func) = (*parent_iface).delete_text {
129                func(
130                    self.obj().unsafe_cast_ref::<Editable>().to_glib_none().0,
131                    start_position,
132                    end_position,
133                );
134            }
135        }
136    }
137
138    fn parent_text(&self) -> GString {
139        unsafe {
140            let type_data = Self::type_data();
141            let parent_iface = type_data.as_ref().parent_interface::<Editable>()
142                as *const ffi::GtkEditableInterface;
143            let func = (*parent_iface)
144                .get_text
145                .expect("no parent \"get_text\" implementation");
146
147            from_glib_none(func(
148                self.obj().unsafe_cast_ref::<Editable>().to_glib_none().0,
149            ))
150        }
151    }
152
153    fn parent_delegate(&self) -> Option<Editable> {
154        unsafe {
155            let type_data = Self::type_data();
156            let parent_iface = type_data.as_ref().parent_interface::<Editable>()
157                as *const ffi::GtkEditableInterface;
158            let func = (*parent_iface)
159                .get_delegate
160                .expect("no parent \"get_delegate\" implementation");
161            from_glib_none(func(
162                self.obj().unsafe_cast_ref::<Editable>().to_glib_none().0,
163            ))
164        }
165    }
166
167    fn parent_changed(&self) {
168        unsafe {
169            let type_data = Self::type_data();
170            let parent_iface = type_data.as_ref().parent_interface::<Editable>()
171                as *const ffi::GtkEditableInterface;
172
173            if let Some(func) = (*parent_iface).changed {
174                func(self.obj().unsafe_cast_ref::<Editable>().to_glib_none().0);
175            }
176        }
177    }
178
179    fn parent_do_insert_text(&self, text: &str, length: i32, position: &mut i32) {
180        unsafe {
181            let type_data = Self::type_data();
182            let parent_iface = type_data.as_ref().parent_interface::<Editable>()
183                as *const ffi::GtkEditableInterface;
184
185            if let Some(func) = (*parent_iface).do_insert_text {
186                func(
187                    self.obj().unsafe_cast_ref::<Editable>().to_glib_none().0,
188                    text.to_glib_none().0,
189                    length,
190                    position,
191                );
192            }
193        }
194    }
195
196    fn parent_do_delete_text(&self, start_position: i32, end_position: i32) {
197        unsafe {
198            let type_data = Self::type_data();
199            let parent_iface = type_data.as_ref().parent_interface::<Editable>()
200                as *const ffi::GtkEditableInterface;
201
202            if let Some(func) = (*parent_iface).do_delete_text {
203                func(
204                    self.obj().unsafe_cast_ref::<Editable>().to_glib_none().0,
205                    start_position,
206                    end_position,
207                );
208            }
209        }
210    }
211
212    fn parent_selection_bounds(&self) -> Option<(i32, i32)> {
213        unsafe {
214            let type_data = Self::type_data();
215            let parent_iface = type_data.as_ref().parent_interface::<Editable>()
216                as *const ffi::GtkEditableInterface;
217
218            if let Some(func) = (*parent_iface).get_selection_bounds {
219                let mut start_position = std::mem::MaybeUninit::uninit();
220                let mut end_position = std::mem::MaybeUninit::uninit();
221                if from_glib(func(
222                    self.obj().unsafe_cast_ref::<Editable>().to_glib_none().0,
223                    start_position.as_mut_ptr(),
224                    end_position.as_mut_ptr(),
225                )) {
226                    return Some((start_position.assume_init(), end_position.assume_init()));
227                }
228            }
229            None
230        }
231    }
232
233    fn parent_set_selection_bounds(&self, start_position: i32, end_position: i32) {
234        unsafe {
235            let type_data = Self::type_data();
236            let parent_iface = type_data.as_ref().parent_interface::<Editable>()
237                as *const ffi::GtkEditableInterface;
238
239            if let Some(func) = (*parent_iface).set_selection_bounds {
240                func(
241                    self.obj().unsafe_cast_ref::<Editable>().to_glib_none().0,
242                    start_position,
243                    end_position,
244                );
245            }
246        }
247    }
248}
249
250impl<T: EditableImpl> EditableImplExt for T {}
251
252unsafe impl<T: EditableImpl + ObjectSubclass> IsImplementable<T> for Editable {
253    fn interface_init(iface: &mut glib::Interface<Self>) {
254        let instance_type = iface.instance_type();
255        let iface = iface.as_mut();
256
257        iface.insert_text = Some(editable_insert_text::<T>);
258        iface.delete_text = Some(editable_delete_text::<T>);
259        iface.changed = Some(editable_changed::<T>);
260        iface.get_text = Some(editable_get_text::<T>);
261        iface.get_delegate = Some(editable_get_delegate::<T>);
262        iface.do_insert_text = Some(editable_do_insert_text::<T>);
263        iface.do_delete_text = Some(editable_do_delete_text::<T>);
264        iface.get_selection_bounds = Some(editable_get_selection_bounds::<T>);
265        iface.set_selection_bounds = Some(editable_set_selection_bounds::<T>);
266
267        unsafe {
268            let class_ref = glib::object::Class::<glib::Object>::from_type(instance_type).unwrap();
269            let object_class =
270                class_ref.as_ref() as *const _ as *mut glib::gobject_ffi::GObjectClass;
271
272            let mut first_prop = std::mem::MaybeUninit::uninit();
273            let properties = glib::gobject_ffi::g_object_class_list_properties(
274                object_class,
275                first_prop.as_mut_ptr(),
276            );
277            glib::ffi::g_free(properties as *mut _);
278            let first_prop = first_prop.assume_init();
279            ffi::gtk_editable_install_properties(object_class, first_prop);
280        }
281    }
282}
283
284unsafe extern "C" fn editable_insert_text<T: EditableImpl>(
285    editable: *mut ffi::GtkEditable,
286    text: *const c_char,
287    length: c_int,
288    position: *mut c_int,
289) {
290    let instance = &*(editable as *mut T::Instance);
291    let imp = instance.imp();
292
293    imp.insert_text(&GString::from_glib_borrow(text), length, &mut *position)
294}
295
296unsafe extern "C" fn editable_delete_text<T: EditableImpl>(
297    editable: *mut ffi::GtkEditable,
298    start_position: c_int,
299    end_position: c_int,
300) {
301    let instance = &*(editable as *mut T::Instance);
302    let imp = instance.imp();
303
304    imp.delete_text(start_position, end_position)
305}
306
307unsafe extern "C" fn editable_changed<T: EditableImpl>(editable: *mut ffi::GtkEditable) {
308    let instance = &*(editable as *mut T::Instance);
309    let imp = instance.imp();
310
311    imp.changed()
312}
313
314unsafe extern "C" fn editable_get_text<T: EditableImpl>(
315    editable: *mut ffi::GtkEditable,
316) -> *const c_char {
317    let instance = &*(editable as *mut T::Instance);
318    let imp = instance.imp();
319
320    imp.text().into_glib_ptr()
321}
322
323unsafe extern "C" fn editable_get_delegate<T: EditableImpl>(
324    editable: *mut ffi::GtkEditable,
325) -> *mut ffi::GtkEditable {
326    let instance = &*(editable as *mut T::Instance);
327    let imp = instance.imp();
328
329    let delegate = imp.delegate();
330
331    static QUARK: OnceLock<Quark> = OnceLock::new();
332    let quark = *QUARK.get_or_init(|| Quark::from_str("gtk-rs-subclass-editable-get-delegate"));
333
334    match imp.obj().qdata::<Option<Editable>>(quark) {
335        Some(delegate_data) => {
336            assert_eq!(
337                delegate_data.as_ref(),
338                &delegate,
339                "The Editable delegate must not change"
340            );
341        }
342        None => {
343            imp.obj().set_qdata(quark, delegate.clone());
344        }
345    };
346    delegate.to_glib_none().0
347}
348
349unsafe extern "C" fn editable_do_insert_text<T: EditableImpl>(
350    editable: *mut ffi::GtkEditable,
351    text: *const c_char,
352    length: i32,
353    position: *mut i32,
354) {
355    let instance = &*(editable as *mut T::Instance);
356    let imp = instance.imp();
357
358    imp.do_insert_text(&GString::from_glib_borrow(text), length, &mut *position)
359}
360
361unsafe extern "C" fn editable_do_delete_text<T: EditableImpl>(
362    editable: *mut ffi::GtkEditable,
363    start_position: i32,
364    end_position: i32,
365) {
366    let instance = &*(editable as *mut T::Instance);
367    let imp = instance.imp();
368
369    imp.do_delete_text(start_position, end_position)
370}
371
372unsafe extern "C" fn editable_get_selection_bounds<T: EditableImpl>(
373    editable: *mut ffi::GtkEditable,
374    start_position: *mut i32,
375    end_position: *mut i32,
376) -> glib::ffi::gboolean {
377    let instance = &*(editable as *mut T::Instance);
378    let imp = instance.imp();
379
380    if let Some((start_pos, end_pos)) = imp.selection_bounds() {
381        if !start_position.is_null() {
382            *start_position = start_pos;
383        }
384
385        if !end_position.is_null() {
386            *end_position = end_pos;
387        }
388        true.into_glib()
389    } else {
390        *start_position = 0;
391        *end_position = 0;
392        false.into_glib()
393    }
394}
395
396unsafe extern "C" fn editable_set_selection_bounds<T: EditableImpl>(
397    editable: *mut ffi::GtkEditable,
398    start_position: i32,
399    end_position: i32,
400) {
401    let instance = &*(editable as *mut T::Instance);
402    let imp = instance.imp();
403
404    imp.set_selection_bounds(start_position, end_position)
405}