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 {
14 self.parent_delete_text(position, n_chars)
15 }
16
17 fn deleted_text(&self, position: u32, n_chars: Option<u32>) {
18 self.parent_deleted_text(position, n_chars)
19 }
20
21 #[doc(alias = "get_length")]
22 fn length(&self) -> u32 {
23 self.parent_length()
24 }
25
26 #[doc(alias = "get_text")]
27 fn text(&self) -> GString {
28 self.parent_text()
29 }
30 fn insert_text(&self, position: u32, chars: &str) -> u32 {
31 self.parent_insert_text(position, chars)
32 }
33
34 fn inserted_text(&self, position: u32, chars: &str) {
35 self.parent_inserted_text(position, chars)
36 }
37}
38
39pub trait EntryBufferImplExt: EntryBufferImpl {
40 fn parent_delete_text(&self, position: u32, n_chars: Option<u32>) -> u32 {
41 unsafe {
42 let data = Self::type_data();
43 let parent_class = data.as_ref().parent_class() as *mut ffi::GtkEntryBufferClass;
44 let f = (*parent_class)
45 .delete_text
46 .expect("No parent class impl for \"delete_text\"");
47 f(
48 self.obj().unsafe_cast_ref::<EntryBuffer>().to_glib_none().0,
49 position,
50 n_chars.unwrap_or(u32::MAX),
51 )
52 }
53 }
54
55 fn parent_deleted_text(&self, position: u32, n_chars: Option<u32>) {
56 unsafe {
57 let data = Self::type_data();
58 let parent_class = data.as_ref().parent_class() as *mut ffi::GtkEntryBufferClass;
59 if let Some(f) = (*parent_class).deleted_text {
60 f(
61 self.obj().unsafe_cast_ref::<EntryBuffer>().to_glib_none().0,
62 position,
63 n_chars.unwrap_or(u32::MAX),
64 )
65 }
66 }
67 }
68
69 fn parent_length(&self) -> u32 {
70 unsafe {
71 let data = Self::type_data();
72 let parent_class = data.as_ref().parent_class() as *mut ffi::GtkEntryBufferClass;
73 let f = (*parent_class)
74 .get_length
75 .expect("No parent class impl for \"get_length\"");
76 f(self.obj().unsafe_cast_ref::<EntryBuffer>().to_glib_none().0)
77 }
78 }
79
80 fn parent_text(&self) -> GString {
81 unsafe {
82 let data = Self::type_data();
83 let parent_class = data.as_ref().parent_class() as *mut ffi::GtkEntryBufferClass;
84 let f = (*parent_class)
85 .get_text
86 .expect("No parent class impl for \"get_text\"");
87 let mut n_bytes = 0;
88 let res = f(
89 self.obj().unsafe_cast_ref::<EntryBuffer>().to_glib_none().0,
90 &mut n_bytes,
91 );
92 FromGlibContainer::from_glib_none_num(res, n_bytes as _)
93 }
94 }
95
96 fn parent_insert_text(&self, position: u32, text: &str) -> u32 {
97 unsafe {
98 let data = Self::type_data();
99 let parent_class = data.as_ref().parent_class() as *mut ffi::GtkEntryBufferClass;
100 let f = (*parent_class)
101 .insert_text
102 .expect("No parent class impl for \"insert_text\"");
103
104 f(
105 self.obj().unsafe_cast_ref::<EntryBuffer>().to_glib_none().0,
106 position,
107 text.to_glib_none().0,
108 text.chars().count() as u32,
109 )
110 }
111 }
112
113 fn parent_inserted_text(&self, position: u32, text: &str) {
114 unsafe {
115 let data = Self::type_data();
116 let parent_class = data.as_ref().parent_class() as *mut ffi::GtkEntryBufferClass;
117 if let Some(f) = (*parent_class).inserted_text {
118 f(
119 self.obj().unsafe_cast_ref::<EntryBuffer>().to_glib_none().0,
120 position,
121 text.to_glib_none().0,
122 text.chars().count() as u32,
123 )
124 }
125 }
126 }
127}
128
129impl<T: EntryBufferImpl> EntryBufferImplExt for T {}
130
131unsafe impl<T: EntryBufferImpl> IsSubclassable<T> for EntryBuffer {
132 fn class_init(class: &mut glib::Class<Self>) {
133 Self::parent_class_init::<T>(class);
134
135 assert_initialized_main_thread!();
136
137 let klass = class.as_mut();
138 klass.delete_text = Some(entry_buffer_delete_text::<T>);
139 klass.deleted_text = Some(entry_buffer_deleted_text::<T>);
140 klass.get_length = Some(entry_buffer_get_length::<T>);
141 klass.get_text = Some(entry_buffer_get_text::<T>);
142 klass.insert_text = Some(entry_buffer_insert_text::<T>);
143 klass.inserted_text = Some(entry_buffer_inserted_text::<T>);
144 }
145}
146
147unsafe extern "C" fn entry_buffer_delete_text<T: EntryBufferImpl>(
148 ptr: *mut ffi::GtkEntryBuffer,
149 position: u32,
150 n_chars: u32,
151) -> u32 {
152 let instance = &*(ptr as *mut T::Instance);
153 let imp = instance.imp();
154
155 let n_chars = if n_chars == u32::MAX {
156 None
157 } else {
158 Some(n_chars)
159 };
160
161 imp.delete_text(position, n_chars)
162}
163
164unsafe extern "C" fn entry_buffer_deleted_text<T: EntryBufferImpl>(
165 ptr: *mut ffi::GtkEntryBuffer,
166 position: u32,
167 n_chars: u32,
168) {
169 let instance = &*(ptr as *mut T::Instance);
170 let imp = instance.imp();
171
172 let n_chars = if n_chars == u32::MAX {
173 None
174 } else {
175 Some(n_chars)
176 };
177
178 imp.deleted_text(position, n_chars)
179}
180
181unsafe extern "C" fn entry_buffer_get_text<T: EntryBufferImpl>(
182 ptr: *mut ffi::GtkEntryBuffer,
183 n_bytes: *mut usize,
184) -> *const libc::c_char {
185 let instance = &*(ptr as *mut T::Instance);
186 let imp = instance.imp();
187
188 let ret = imp.text();
189 if !n_bytes.is_null() {
190 *n_bytes = ret.len();
191 }
192 static QUARK: OnceLock<glib::Quark> = OnceLock::new();
196 let quark = *QUARK.get_or_init(|| glib::Quark::from_str("gtk4-rs-subclass-entry-buffer-text"));
197
198 let fullptr = ret.into_glib_ptr();
199 imp.obj().set_qdata(
200 quark,
201 PtrHolder(fullptr, |ptr| {
202 glib::ffi::g_free(ptr as *mut _);
203 }),
204 );
205 fullptr
206}
207
208unsafe extern "C" fn entry_buffer_get_length<T: EntryBufferImpl>(
209 ptr: *mut ffi::GtkEntryBuffer,
210) -> u32 {
211 let instance = &*(ptr as *mut T::Instance);
212 let imp = instance.imp();
213
214 imp.length()
215}
216
217unsafe extern "C" fn entry_buffer_insert_text<T: EntryBufferImpl>(
218 ptr: *mut ffi::GtkEntryBuffer,
219 position: u32,
220 charsptr: *const libc::c_char,
221 n_chars: u32,
222) -> u32 {
223 let instance = &*(ptr as *mut T::Instance);
224 let imp = instance.imp();
225 let text: Borrowed<GString> = from_glib_borrow(charsptr);
226
227 let chars = text_n_chars(&text, n_chars);
228 imp.insert_text(position, chars)
229}
230
231unsafe extern "C" fn entry_buffer_inserted_text<T: EntryBufferImpl>(
232 ptr: *mut ffi::GtkEntryBuffer,
233 position: u32,
234 charsptr: *const libc::c_char,
235 length: u32,
236) {
237 let instance = &*(ptr as *mut T::Instance);
238 let imp = instance.imp();
239 let text: Borrowed<GString> = from_glib_borrow(charsptr);
240
241 let chars = text_n_chars(&text, length);
242 imp.inserted_text(position, chars)
243}
244
245#[doc(alias = "get_text_n_chars")]
246fn text_n_chars(text: &str, n_chars: u32) -> &str {
247 if n_chars != u32::MAX && n_chars > 0 {
248 let mut iter = text
249 .char_indices()
250 .skip((n_chars - 1) as _)
251 .map(|(pos, _)| pos);
252 iter
253 .next()
254 .expect(
255 "The passed text to EntryBuffer contains fewer characters than what's passed as a length",
256 );
257 let pos_end = iter.next().unwrap_or(text.len());
258 &text[..pos_end]
259 } else if n_chars == 0 {
260 ""
262 } else {
263 text
264 }
265}
266
267#[cfg(test)]
268mod test {
269 use super::text_n_chars;
270 #[std::prelude::v1::test]
271 fn n_chars_max_length_ascii() {
272 assert_eq!(text_n_chars("gtk-rs bindings", 6), "gtk-rs");
273 assert_eq!(text_n_chars("gtk-rs bindings", u32::MAX), "gtk-rs bindings");
274 }
275
276 #[std::prelude::v1::test]
277 #[should_panic]
278 fn n_chars_max_length_ascii_panic() {
279 assert_eq!(text_n_chars("gtk-rs", 7), "gtk-rs");
280 }
281
282 #[std::prelude::v1::test]
283 fn n_chars_max_length_utf8() {
284 assert_eq!(text_n_chars("šØš©š§š¦", 2), "šØš©");
285 assert_eq!(text_n_chars("šØš©š§š¦", 0), "");
286 assert_eq!(text_n_chars("šØš©š§š¦", 4), "šØš©š§š¦");
287 assert_eq!(text_n_chars("šØš©š§š¦", u32::MAX), "šØš©š§š¦");
288 assert_eq!(text_n_chars("ŁŲŖŲ§ŲØ", 2), "ŁŲŖ");
289 }
290
291 #[std::prelude::v1::test]
292 fn n_chars_max_length_utf8_ascii() {
293 assert_eq!(text_n_chars("šØgš©tš§kš¦", 2), "šØg");
294 assert_eq!(text_n_chars("šØgš©tš§kš¦", 5), "šØgš©tš§");
295 assert_eq!(text_n_chars("ŁaŲŖŲ§ŲØ", 3), "ŁaŲŖ");
296 }
297
298 #[std::prelude::v1::test]
299 #[should_panic]
300 fn n_chars_max_length_utf8_panic() {
301 assert_eq!(text_n_chars("šØš©š§š¦", 5), "šØš©");
302 }
303}