1use std::sync::OnceLock;
6
7use glib::{translate::*, GString};
8
9use super::PtrHolder;
10use crate::{ffi, prelude::*, subclass::prelude::*, EntryBuffer};
11
12pub trait EntryBufferImpl: ObjectImpl + ObjectSubclass<Type: IsA<EntryBuffer>> {
13 fn delete_text(&self, position: u32, n_chars: Option<u32>) -> u32 {
33 self.parent_delete_text(position, n_chars)
34 }
35
36 fn deleted_text(&self, position: u32, n_chars: Option<u32>) {
37 self.parent_deleted_text(position, n_chars)
38 }
39
40 #[doc(alias = "get_length")]
46 fn length(&self) -> u32 {
47 self.parent_length()
48 }
49
50 #[doc(alias = "get_text")]
51 fn text(&self) -> GString {
52 self.parent_text()
53 }
54 fn insert_text(&self, position: u32, chars: &str) -> u32 {
74 self.parent_insert_text(position, chars)
75 }
76
77 fn inserted_text(&self, position: u32, chars: &str) {
78 self.parent_inserted_text(position, chars)
79 }
80}
81
82pub trait EntryBufferImplExt: EntryBufferImpl {
83 fn parent_delete_text(&self, position: u32, n_chars: Option<u32>) -> u32 {
84 unsafe {
85 let data = Self::type_data();
86 let parent_class = data.as_ref().parent_class() as *mut ffi::GtkEntryBufferClass;
87 let f = (*parent_class)
88 .delete_text
89 .expect("No parent class impl for \"delete_text\"");
90 f(
91 self.obj().unsafe_cast_ref::<EntryBuffer>().to_glib_none().0,
92 position,
93 n_chars.unwrap_or(u32::MAX),
94 )
95 }
96 }
97
98 fn parent_deleted_text(&self, position: u32, n_chars: Option<u32>) {
99 unsafe {
100 let data = Self::type_data();
101 let parent_class = data.as_ref().parent_class() as *mut ffi::GtkEntryBufferClass;
102 if let Some(f) = (*parent_class).deleted_text {
103 f(
104 self.obj().unsafe_cast_ref::<EntryBuffer>().to_glib_none().0,
105 position,
106 n_chars.unwrap_or(u32::MAX),
107 )
108 }
109 }
110 }
111
112 fn parent_length(&self) -> u32 {
113 unsafe {
114 let data = Self::type_data();
115 let parent_class = data.as_ref().parent_class() as *mut ffi::GtkEntryBufferClass;
116 let f = (*parent_class)
117 .get_length
118 .expect("No parent class impl for \"get_length\"");
119 f(self.obj().unsafe_cast_ref::<EntryBuffer>().to_glib_none().0)
120 }
121 }
122
123 fn parent_text(&self) -> GString {
124 unsafe {
125 let data = Self::type_data();
126 let parent_class = data.as_ref().parent_class() as *mut ffi::GtkEntryBufferClass;
127 let f = (*parent_class)
128 .get_text
129 .expect("No parent class impl for \"get_text\"");
130 let mut n_bytes = 0;
131 let res = f(
132 self.obj().unsafe_cast_ref::<EntryBuffer>().to_glib_none().0,
133 &mut n_bytes,
134 );
135 FromGlibContainer::from_glib_none_num(res, n_bytes as _)
136 }
137 }
138
139 fn parent_insert_text(&self, position: u32, text: &str) -> u32 {
140 unsafe {
141 let data = Self::type_data();
142 let parent_class = data.as_ref().parent_class() as *mut ffi::GtkEntryBufferClass;
143 let f = (*parent_class)
144 .insert_text
145 .expect("No parent class impl for \"insert_text\"");
146
147 f(
148 self.obj().unsafe_cast_ref::<EntryBuffer>().to_glib_none().0,
149 position,
150 text.to_glib_none().0,
151 text.chars().count() as u32,
152 )
153 }
154 }
155
156 fn parent_inserted_text(&self, position: u32, text: &str) {
157 unsafe {
158 let data = Self::type_data();
159 let parent_class = data.as_ref().parent_class() as *mut ffi::GtkEntryBufferClass;
160 if let Some(f) = (*parent_class).inserted_text {
161 f(
162 self.obj().unsafe_cast_ref::<EntryBuffer>().to_glib_none().0,
163 position,
164 text.to_glib_none().0,
165 text.chars().count() as u32,
166 )
167 }
168 }
169 }
170}
171
172impl<T: EntryBufferImpl> EntryBufferImplExt for T {}
173
174unsafe impl<T: EntryBufferImpl> IsSubclassable<T> for EntryBuffer {
175 fn class_init(class: &mut glib::Class<Self>) {
176 Self::parent_class_init::<T>(class);
177
178 assert_initialized_main_thread!();
179
180 let klass = class.as_mut();
181 klass.delete_text = Some(entry_buffer_delete_text::<T>);
182 klass.deleted_text = Some(entry_buffer_deleted_text::<T>);
183 klass.get_length = Some(entry_buffer_get_length::<T>);
184 klass.get_text = Some(entry_buffer_get_text::<T>);
185 klass.insert_text = Some(entry_buffer_insert_text::<T>);
186 klass.inserted_text = Some(entry_buffer_inserted_text::<T>);
187 }
188}
189
190unsafe extern "C" fn entry_buffer_delete_text<T: EntryBufferImpl>(
191 ptr: *mut ffi::GtkEntryBuffer,
192 position: u32,
193 n_chars: u32,
194) -> u32 {
195 let instance = &*(ptr as *mut T::Instance);
196 let imp = instance.imp();
197
198 let n_chars = if n_chars == u32::MAX {
199 None
200 } else {
201 Some(n_chars)
202 };
203
204 imp.delete_text(position, n_chars)
205}
206
207unsafe extern "C" fn entry_buffer_deleted_text<T: EntryBufferImpl>(
208 ptr: *mut ffi::GtkEntryBuffer,
209 position: u32,
210 n_chars: u32,
211) {
212 let instance = &*(ptr as *mut T::Instance);
213 let imp = instance.imp();
214
215 let n_chars = if n_chars == u32::MAX {
216 None
217 } else {
218 Some(n_chars)
219 };
220
221 imp.deleted_text(position, n_chars)
222}
223
224unsafe extern "C" fn entry_buffer_get_text<T: EntryBufferImpl>(
225 ptr: *mut ffi::GtkEntryBuffer,
226 n_bytes: *mut usize,
227) -> *const libc::c_char {
228 let instance = &*(ptr as *mut T::Instance);
229 let imp = instance.imp();
230
231 let ret = imp.text();
232 if !n_bytes.is_null() {
233 *n_bytes = ret.len();
234 }
235 static QUARK: OnceLock<glib::Quark> = OnceLock::new();
239 let quark = *QUARK.get_or_init(|| glib::Quark::from_str("gtk4-rs-subclass-entry-buffer-text"));
240
241 let fullptr = ret.into_glib_ptr();
242 imp.obj().set_qdata(
243 quark,
244 PtrHolder(fullptr, |ptr| {
245 glib::ffi::g_free(ptr as *mut _);
246 }),
247 );
248 fullptr
249}
250
251unsafe extern "C" fn entry_buffer_get_length<T: EntryBufferImpl>(
252 ptr: *mut ffi::GtkEntryBuffer,
253) -> u32 {
254 let instance = &*(ptr as *mut T::Instance);
255 let imp = instance.imp();
256
257 imp.length()
258}
259
260unsafe extern "C" fn entry_buffer_insert_text<T: EntryBufferImpl>(
261 ptr: *mut ffi::GtkEntryBuffer,
262 position: u32,
263 charsptr: *const libc::c_char,
264 n_chars: u32,
265) -> u32 {
266 let instance = &*(ptr as *mut T::Instance);
267 let imp = instance.imp();
268 let text: Borrowed<GString> = from_glib_borrow(charsptr);
269
270 let chars = text_n_chars(&text, n_chars);
271 imp.insert_text(position, chars)
272}
273
274unsafe extern "C" fn entry_buffer_inserted_text<T: EntryBufferImpl>(
275 ptr: *mut ffi::GtkEntryBuffer,
276 position: u32,
277 charsptr: *const libc::c_char,
278 length: u32,
279) {
280 let instance = &*(ptr as *mut T::Instance);
281 let imp = instance.imp();
282 let text: Borrowed<GString> = from_glib_borrow(charsptr);
283
284 let chars = text_n_chars(&text, length);
285 imp.inserted_text(position, chars)
286}
287
288#[doc(alias = "get_text_n_chars")]
289fn text_n_chars(text: &str, n_chars: u32) -> &str {
290 if n_chars != u32::MAX && n_chars > 0 {
291 let mut iter = text
292 .char_indices()
293 .skip((n_chars - 1) as _)
294 .map(|(pos, _)| pos);
295 iter
296 .next()
297 .expect(
298 "The passed text to EntryBuffer contains fewer characters than what's passed as a length",
299 );
300 let pos_end = iter.next().unwrap_or(text.len());
301 &text[..pos_end]
302 } else if n_chars == 0 {
303 ""
305 } else {
306 text
307 }
308}
309
310#[cfg(test)]
311mod test {
312 use super::text_n_chars;
313 #[std::prelude::v1::test]
314 fn n_chars_max_length_ascii() {
315 assert_eq!(text_n_chars("gtk-rs bindings", 6), "gtk-rs");
316 assert_eq!(text_n_chars("gtk-rs bindings", u32::MAX), "gtk-rs bindings");
317 }
318
319 #[std::prelude::v1::test]
320 #[should_panic]
321 fn n_chars_max_length_ascii_panic() {
322 assert_eq!(text_n_chars("gtk-rs", 7), "gtk-rs");
323 }
324
325 #[std::prelude::v1::test]
326 fn n_chars_max_length_utf8() {
327 assert_eq!(text_n_chars("👨👩👧👦", 2), "👨👩");
328 assert_eq!(text_n_chars("👨👩👧👦", 0), "");
329 assert_eq!(text_n_chars("👨👩👧👦", 4), "👨👩👧👦");
330 assert_eq!(text_n_chars("👨👩👧👦", u32::MAX), "👨👩👧👦");
331 assert_eq!(text_n_chars("كتاب", 2), "كت");
332 }
333
334 #[std::prelude::v1::test]
335 fn n_chars_max_length_utf8_ascii() {
336 assert_eq!(text_n_chars("👨g👩t👧k👦", 2), "👨g");
337 assert_eq!(text_n_chars("👨g👩t👧k👦", 5), "👨g👩t👧");
338 assert_eq!(text_n_chars("كaتاب", 3), "كaت");
339 }
340
341 #[std::prelude::v1::test]
342 #[should_panic]
343 fn n_chars_max_length_utf8_panic() {
344 assert_eq!(text_n_chars("👨👩👧👦", 5), "👨👩");
345 }
346}