use std::sync::OnceLock;
use glib::{translate::*, GString};
use super::PtrHolder;
use crate::{ffi, prelude::*, subclass::prelude::*, EntryBuffer};
pub trait EntryBufferImpl: ObjectImpl + ObjectSubclass<Type: IsA<EntryBuffer>> {
fn delete_text(&self, position: u32, n_chars: Option<u32>) -> u32 {
self.parent_delete_text(position, n_chars)
}
fn deleted_text(&self, position: u32, n_chars: Option<u32>) {
self.parent_deleted_text(position, n_chars)
}
#[doc(alias = "get_length")]
fn length(&self) -> u32 {
self.parent_length()
}
#[doc(alias = "get_text")]
fn text(&self) -> GString {
self.parent_text()
}
fn insert_text(&self, position: u32, chars: &str) -> u32 {
self.parent_insert_text(position, chars)
}
fn inserted_text(&self, position: u32, chars: &str) {
self.parent_inserted_text(position, chars)
}
}
pub trait EntryBufferImplExt: EntryBufferImpl {
fn parent_delete_text(&self, position: u32, n_chars: Option<u32>) -> u32 {
unsafe {
let data = Self::type_data();
let parent_class = data.as_ref().parent_class() as *mut ffi::GtkEntryBufferClass;
let f = (*parent_class)
.delete_text
.expect("No parent class impl for \"delete_text\"");
f(
self.obj().unsafe_cast_ref::<EntryBuffer>().to_glib_none().0,
position,
n_chars.unwrap_or(u32::MAX),
)
}
}
fn parent_deleted_text(&self, position: u32, n_chars: Option<u32>) {
unsafe {
let data = Self::type_data();
let parent_class = data.as_ref().parent_class() as *mut ffi::GtkEntryBufferClass;
if let Some(f) = (*parent_class).deleted_text {
f(
self.obj().unsafe_cast_ref::<EntryBuffer>().to_glib_none().0,
position,
n_chars.unwrap_or(u32::MAX),
)
}
}
}
fn parent_length(&self) -> u32 {
unsafe {
let data = Self::type_data();
let parent_class = data.as_ref().parent_class() as *mut ffi::GtkEntryBufferClass;
let f = (*parent_class)
.get_length
.expect("No parent class impl for \"get_length\"");
f(self.obj().unsafe_cast_ref::<EntryBuffer>().to_glib_none().0)
}
}
fn parent_text(&self) -> GString {
unsafe {
let data = Self::type_data();
let parent_class = data.as_ref().parent_class() as *mut ffi::GtkEntryBufferClass;
let f = (*parent_class)
.get_text
.expect("No parent class impl for \"get_text\"");
let mut n_bytes = 0;
let res = f(
self.obj().unsafe_cast_ref::<EntryBuffer>().to_glib_none().0,
&mut n_bytes,
);
FromGlibContainer::from_glib_none_num(res, n_bytes as _)
}
}
fn parent_insert_text(&self, position: u32, text: &str) -> u32 {
unsafe {
let data = Self::type_data();
let parent_class = data.as_ref().parent_class() as *mut ffi::GtkEntryBufferClass;
let f = (*parent_class)
.insert_text
.expect("No parent class impl for \"insert_text\"");
f(
self.obj().unsafe_cast_ref::<EntryBuffer>().to_glib_none().0,
position,
text.to_glib_none().0,
text.chars().count() as u32,
)
}
}
fn parent_inserted_text(&self, position: u32, text: &str) {
unsafe {
let data = Self::type_data();
let parent_class = data.as_ref().parent_class() as *mut ffi::GtkEntryBufferClass;
if let Some(f) = (*parent_class).inserted_text {
f(
self.obj().unsafe_cast_ref::<EntryBuffer>().to_glib_none().0,
position,
text.to_glib_none().0,
text.chars().count() as u32,
)
}
}
}
}
impl<T: EntryBufferImpl> EntryBufferImplExt for T {}
unsafe impl<T: EntryBufferImpl> IsSubclassable<T> for EntryBuffer {
fn class_init(class: &mut glib::Class<Self>) {
Self::parent_class_init::<T>(class);
assert_initialized_main_thread!();
let klass = class.as_mut();
klass.delete_text = Some(entry_buffer_delete_text::<T>);
klass.deleted_text = Some(entry_buffer_deleted_text::<T>);
klass.get_length = Some(entry_buffer_get_length::<T>);
klass.get_text = Some(entry_buffer_get_text::<T>);
klass.insert_text = Some(entry_buffer_insert_text::<T>);
klass.inserted_text = Some(entry_buffer_inserted_text::<T>);
}
}
unsafe extern "C" fn entry_buffer_delete_text<T: EntryBufferImpl>(
ptr: *mut ffi::GtkEntryBuffer,
position: u32,
n_chars: u32,
) -> u32 {
let instance = &*(ptr as *mut T::Instance);
let imp = instance.imp();
let n_chars = if n_chars == u32::MAX {
None
} else {
Some(n_chars)
};
imp.delete_text(position, n_chars)
}
unsafe extern "C" fn entry_buffer_deleted_text<T: EntryBufferImpl>(
ptr: *mut ffi::GtkEntryBuffer,
position: u32,
n_chars: u32,
) {
let instance = &*(ptr as *mut T::Instance);
let imp = instance.imp();
let n_chars = if n_chars == u32::MAX {
None
} else {
Some(n_chars)
};
imp.deleted_text(position, n_chars)
}
unsafe extern "C" fn entry_buffer_get_text<T: EntryBufferImpl>(
ptr: *mut ffi::GtkEntryBuffer,
n_bytes: *mut usize,
) -> *const libc::c_char {
let instance = &*(ptr as *mut T::Instance);
let imp = instance.imp();
let ret = imp.text();
if !n_bytes.is_null() {
*n_bytes = ret.len();
}
static QUARK: OnceLock<glib::Quark> = OnceLock::new();
let quark = *QUARK.get_or_init(|| glib::Quark::from_str("gtk4-rs-subclass-entry-buffer-text"));
let fullptr = ret.into_glib_ptr();
imp.obj().set_qdata(
quark,
PtrHolder(fullptr, |ptr| {
glib::ffi::g_free(ptr as *mut _);
}),
);
fullptr
}
unsafe extern "C" fn entry_buffer_get_length<T: EntryBufferImpl>(
ptr: *mut ffi::GtkEntryBuffer,
) -> u32 {
let instance = &*(ptr as *mut T::Instance);
let imp = instance.imp();
imp.length()
}
unsafe extern "C" fn entry_buffer_insert_text<T: EntryBufferImpl>(
ptr: *mut ffi::GtkEntryBuffer,
position: u32,
charsptr: *const libc::c_char,
n_chars: u32,
) -> u32 {
let instance = &*(ptr as *mut T::Instance);
let imp = instance.imp();
let text: Borrowed<GString> = from_glib_borrow(charsptr);
let chars = text_n_chars(&text, n_chars);
imp.insert_text(position, chars)
}
unsafe extern "C" fn entry_buffer_inserted_text<T: EntryBufferImpl>(
ptr: *mut ffi::GtkEntryBuffer,
position: u32,
charsptr: *const libc::c_char,
length: u32,
) {
let instance = &*(ptr as *mut T::Instance);
let imp = instance.imp();
let text: Borrowed<GString> = from_glib_borrow(charsptr);
let chars = text_n_chars(&text, length);
imp.inserted_text(position, chars)
}
#[doc(alias = "get_text_n_chars")]
fn text_n_chars(text: &str, n_chars: u32) -> &str {
if n_chars != u32::MAX && n_chars > 0 {
let mut iter = text
.char_indices()
.skip((n_chars - 1) as _)
.map(|(pos, _)| pos);
iter
.next()
.expect(
"The passed text to EntryBuffer contains fewer characters than what's passed as a length",
);
let pos_end = iter.next().unwrap_or(text.len());
&text[..pos_end]
} else if n_chars == 0 {
""
} else {
text
}
}
#[cfg(test)]
mod test {
use super::text_n_chars;
#[std::prelude::v1::test]
fn n_chars_max_length_ascii() {
assert_eq!(text_n_chars("gtk-rs bindings", 6), "gtk-rs");
assert_eq!(text_n_chars("gtk-rs bindings", u32::MAX), "gtk-rs bindings");
}
#[std::prelude::v1::test]
#[should_panic]
fn n_chars_max_length_ascii_panic() {
assert_eq!(text_n_chars("gtk-rs", 7), "gtk-rs");
}
#[std::prelude::v1::test]
fn n_chars_max_length_utf8() {
assert_eq!(text_n_chars("šØš©š§š¦", 2), "šØš©");
assert_eq!(text_n_chars("šØš©š§š¦", 0), "");
assert_eq!(text_n_chars("šØš©š§š¦", 4), "šØš©š§š¦");
assert_eq!(text_n_chars("šØš©š§š¦", u32::MAX), "šØš©š§š¦");
assert_eq!(text_n_chars("ŁŲŖŲ§ŲØ", 2), "ŁŲŖ");
}
#[std::prelude::v1::test]
fn n_chars_max_length_utf8_ascii() {
assert_eq!(text_n_chars("šØgš©tš§kš¦", 2), "šØg");
assert_eq!(text_n_chars("šØgš©tš§kš¦", 5), "šØgš©tš§");
assert_eq!(text_n_chars("ŁaŲŖŲ§ŲØ", 3), "ŁaŲŖ");
}
#[std::prelude::v1::test]
#[should_panic]
fn n_chars_max_length_utf8_panic() {
assert_eq!(text_n_chars("šØš©š§š¦", 5), "šØš©");
}
}