1use 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}