Skip to main content

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