From 35daaa61310a566527dad5524add34e0c7d93d16 Mon Sep 17 00:00:00 2001 From: Bilal Elmoussaoui Date: Tue, 23 Mar 2021 23:38:23 +0100 Subject: [PATCH 1/5] gtk: manually implement FileChooser::add_choice the API takes a list of option ids that can be None for boolean case (a checkbox) along with a list for the corresponding labels that can be Null as well. The change merges them to a single list to ensure both have the same size. --- gtk4/Gir.toml | 4 ++-- gtk4/src/file_chooser.rs | 41 ++++++++++++++++++++++++++++++++++++++++ gtk4/src/lib.rs | 1 + gtk4/src/prelude.rs | 1 + 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 gtk4/src/file_chooser.rs diff --git a/gtk4/Gir.toml b/gtk4/Gir.toml index 90d43e61f8d5..baeecd9adf08 100644 --- a/gtk4/Gir.toml +++ b/gtk4/Gir.toml @@ -269,7 +269,6 @@ ignore = [ ] manual = [ - # "GObject.ObjectClass", # TODO "GLib.Scanner", "cairo.Context", "cairo.FontOptions", @@ -1044,9 +1043,10 @@ final_type = true name = "Gtk.FileChooser" status = "generate" trust_return_value_nullability = false +manual_traits = ["FileChooserExtManual"] [[object.function]] name = "add_choice" - ignore = true # TODO: strange type of options and option_labels + manual = true # handle options_id & options_labels [[object]] name = "Gtk.FileChooserDialog" diff --git a/gtk4/src/file_chooser.rs b/gtk4/src/file_chooser.rs new file mode 100644 index 000000000000..2945f6ab357d --- /dev/null +++ b/gtk4/src/file_chooser.rs @@ -0,0 +1,41 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use crate::FileChooser; +use glib::translate::*; +use glib::IsA; + +pub trait FileChooserExtManual: 'static { + #[doc(alias = "gtk_file_chooser_add_choice")] + fn add_choice(&self, id: &str, label: &str, options: Option<&[(&str, &str)]>); +} + +impl> FileChooserExtManual for O { + fn add_choice(&self, id: &str, label: &str, options: Option<&[(&str, &str)]>) { + unsafe { + let (options_ids, options_labels) = if let Some(options) = options { + ( + options + .iter() + .map(|o| o.0.to_glib_none().0) + .collect::>() + .as_ptr(), + options + .iter() + .map(|o| o.1.to_glib_none().0) + .collect::>() + .as_ptr(), + ) + } else { + (std::ptr::null(), std::ptr::null()) + }; + + ffi::gtk_file_chooser_add_choice( + self.as_ref().to_glib_none().0, + id.to_glib_none().0, + label.to_glib_none().0, + mut_override(options_ids), + mut_override(options_labels), + ); + } + } +} diff --git a/gtk4/src/lib.rs b/gtk4/src/lib.rs index 7cd58a7c3fd3..133d6240cc71 100644 --- a/gtk4/src/lib.rs +++ b/gtk4/src/lib.rs @@ -267,6 +267,7 @@ mod entry_completion; mod enums; mod event_controller_key; mod expression_watch; +mod file_chooser; mod file_chooser_dialog; mod flags; mod flow_box; diff --git a/gtk4/src/prelude.rs b/gtk4/src/prelude.rs index 036f0f5cdd23..59385f905aa2 100644 --- a/gtk4/src/prelude.rs +++ b/gtk4/src/prelude.rs @@ -19,6 +19,7 @@ pub use crate::editable::EditableExtManual; pub use crate::entry::EntryExtManual; pub use crate::entry_buffer::EntryBufferExtManual; pub use crate::entry_completion::EntryCompletionExtManual; +pub use crate::file_chooser::FileChooserExtManual; pub use crate::font_chooser::FontChooserExtManual; pub use crate::im_context::IMContextExtManual; pub use crate::im_context_simple::IMContextSimpleExtManual; From 50e3139b1c5ff10c545643de91a96a25db52cc19 Mon Sep 17 00:00:00 2001 From: Bilal Elmoussaoui Date: Tue, 23 Mar 2021 23:44:56 +0100 Subject: [PATCH 2/5] gtk: manually implement PrintSettings::set_page_ranges --- gtk4/Gir.toml | 2 +- gtk4/src/lib.rs | 1 + gtk4/src/print_settings.rs | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 gtk4/src/print_settings.rs diff --git a/gtk4/Gir.toml b/gtk4/Gir.toml index baeecd9adf08..aa50ef42b1d1 100644 --- a/gtk4/Gir.toml +++ b/gtk4/Gir.toml @@ -1630,7 +1630,7 @@ trust_return_value_nullability = false nullable = false [[object.function]] name = "set_page_ranges" - ignore = true # TODO: implement me + manual = true # use &[&PageRange] instead of &[PageRange] [[object.function]] name = "set_paper_size" [[object.function.parameter]] diff --git a/gtk4/src/lib.rs b/gtk4/src/lib.rs index 133d6240cc71..762da6d0e740 100644 --- a/gtk4/src/lib.rs +++ b/gtk4/src/lib.rs @@ -297,6 +297,7 @@ mod page_range; #[cfg(any(target_os = "linux", feature = "dox"))] #[cfg_attr(feature = "dox", doc(cfg(target_os = "linux")))] mod print_job; +mod print_settings; mod property_expression; mod recent_data; mod requisition; diff --git a/gtk4/src/print_settings.rs b/gtk4/src/print_settings.rs new file mode 100644 index 000000000000..b2cbb585ca12 --- /dev/null +++ b/gtk4/src/print_settings.rs @@ -0,0 +1,18 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use crate::{PageRange, PrintSettings}; +use glib::translate::*; + +impl PrintSettings { + #[doc(alias = "gtk_print_settings_set_page_ranges")] + pub fn set_page_ranges(&self, page_ranges: &[PageRange]) { + let num_ranges = page_ranges.len() as i32; + unsafe { + ffi::gtk_print_settings_set_page_ranges( + self.to_glib_none().0, + mut_override(page_ranges.to_glib_none().0), + num_ranges, + ); + } + } +} From 2d18c815478cf12247fcfab7e6d91830ca637928 Mon Sep 17 00:00:00 2001 From: Bilal Elmoussaoui Date: Wed, 24 Mar 2021 00:12:54 +0100 Subject: [PATCH 3/5] gkt: manually implement EntryBuffer::[inserted|deleted]-text signals we need the conversion between u32 & u16 --- gtk4/Gir.toml | 12 +++- gtk4/src/auto/entry_buffer.rs | 27 ------- gtk4/src/entry_buffer.rs | 131 +++++++++++++++++++++++++++++++++- 3 files changed, 138 insertions(+), 32 deletions(-) diff --git a/gtk4/Gir.toml b/gtk4/Gir.toml index aa50ef42b1d1..d6355a0bf279 100644 --- a/gtk4/Gir.toml +++ b/gtk4/Gir.toml @@ -960,12 +960,20 @@ manual_traits = ["EntryBufferExtManual"] name = "set_text" manual = true doc_trait_name = "EntryBufferExtManual" + [[object.function]] + name = "emit_deleted_text" + manual = true + doc_trait_name = "EntryBufferExtManual" + [[object.function]] + name = "emit_inserted_text" + manual = true + doc_trait_name = "EntryBufferExtManual" [[object.signal]] name = "deleted-text" - ignore = true # TODO: implement me + ignore = true [[object.signal]] name = "inserted-text" - ignore = true # TODO: implement me + ignore = true [[object]] name = "Gtk.EntryCompletion" diff --git a/gtk4/src/auto/entry_buffer.rs b/gtk4/src/auto/entry_buffer.rs index f46f9f78b7a5..ff9aa2cff66a 100644 --- a/gtk4/src/auto/entry_buffer.rs +++ b/gtk4/src/auto/entry_buffer.rs @@ -58,12 +58,6 @@ impl EntryBufferBuilder { pub const NONE_ENTRY_BUFFER: Option<&EntryBuffer> = None; pub trait EntryBufferExt: 'static { - #[doc(alias = "gtk_entry_buffer_emit_deleted_text")] - fn emit_deleted_text(&self, position: u32, n_chars: u32); - - #[doc(alias = "gtk_entry_buffer_emit_inserted_text")] - fn emit_inserted_text(&self, position: u32, chars: &str, n_chars: u32); - #[doc(alias = "length")] fn connect_length_notify(&self, f: F) -> SignalHandlerId; @@ -75,27 +69,6 @@ pub trait EntryBufferExt: 'static { } impl> EntryBufferExt for O { - fn emit_deleted_text(&self, position: u32, n_chars: u32) { - unsafe { - ffi::gtk_entry_buffer_emit_deleted_text( - self.as_ref().to_glib_none().0, - position, - n_chars, - ); - } - } - - fn emit_inserted_text(&self, position: u32, chars: &str, n_chars: u32) { - unsafe { - ffi::gtk_entry_buffer_emit_inserted_text( - self.as_ref().to_glib_none().0, - position, - chars.to_glib_none().0, - n_chars, - ); - } - } - #[doc(alias = "length")] fn connect_length_notify(&self, f: F) -> SignalHandlerId { unsafe extern "C" fn notify_length_trampoline( diff --git a/gtk4/src/entry_buffer.rs b/gtk4/src/entry_buffer.rs index e77b7c43c380..8baa2db6f90c 100644 --- a/gtk4/src/entry_buffer.rs +++ b/gtk4/src/entry_buffer.rs @@ -1,9 +1,12 @@ // Take a look at the license at the top of the repository in the LICENSE file. use crate::EntryBuffer; -use glib::object::IsA; use glib::translate::*; +use glib::{object::IsA, SignalHandlerId}; +use glib::{signal::connect_raw, Cast}; use libc::{c_int, c_uint}; +use std::boxed::Box as Box_; +use std::mem::transmute; impl EntryBuffer { #[doc(alias = "gtk_entry_buffer_new")] @@ -19,6 +22,12 @@ impl EntryBuffer { } pub trait EntryBufferExtManual: 'static { + #[doc(alias = "gtk_entry_buffer_emit_deleted_text")] + fn emit_deleted_text(&self, position: u16, n_chars: u16); + + #[doc(alias = "gtk_entry_buffer_emit_inserted_text")] + fn emit_inserted_text(&self, position: u16, chars: &str, n_chars: u16); + #[doc(alias = "gtk_entry_buffer_delete_text")] fn delete_text(&self, position: u16, n_chars: Option) -> u16; @@ -36,7 +45,7 @@ pub trait EntryBufferExtManual: 'static { #[doc(alias = "gtk_entry_buffer_get_text")] #[doc(alias = "get_text")] - fn text(&self) -> String; + fn text(&self) -> glib::GString; #[doc(alias = "gtk_entry_buffer_insert_text")] fn insert_text(&self, position: u16, chars: &str) -> u16; @@ -46,6 +55,13 @@ pub trait EntryBufferExtManual: 'static { #[doc(alias = "gtk_entry_buffer_set_text")] fn set_text(&self, chars: &str); + + fn connect_deleted_text(&self, f: F) -> SignalHandlerId; + + fn connect_inserted_text( + &self, + f: F, + ) -> SignalHandlerId; } macro_rules! to_u16 { @@ -60,6 +76,27 @@ macro_rules! to_u16 { } impl> EntryBufferExtManual for O { + fn emit_deleted_text(&self, position: u16, n_chars: u16) { + unsafe { + ffi::gtk_entry_buffer_emit_deleted_text( + self.as_ref().to_glib_none().0, + position as c_uint, + n_chars as c_uint, + ); + } + } + + fn emit_inserted_text(&self, position: u16, chars: &str, n_chars: u16) { + unsafe { + ffi::gtk_entry_buffer_emit_inserted_text( + self.as_ref().to_glib_none().0, + position as c_uint, + chars.to_glib_none().0, + n_chars as c_uint, + ); + } + } + fn delete_text(&self, position: u16, n_chars: Option) -> u16 { unsafe { to_u16!(ffi::gtk_entry_buffer_delete_text( @@ -91,7 +128,7 @@ impl> EntryBufferExtManual for O { } } - fn text(&self) -> String { + fn text(&self) -> glib::GString { unsafe { from_glib_none(ffi::gtk_entry_buffer_get_text( self.as_ref().to_glib_none().0, @@ -129,4 +166,92 @@ impl> EntryBufferExtManual for O { ); } } + + fn connect_deleted_text(&self, f: F) -> SignalHandlerId { + unsafe extern "C" fn deleted_text_trampoline( + this: *mut ffi::GtkEntryBuffer, + position: libc::c_uint, + n_chars: libc::c_uint, + f: glib::ffi::gpointer, + ) where + P: IsA, + { + let f: &F = &*(f as *const F); + f( + &EntryBuffer::from_glib_borrow(this).unsafe_cast_ref(), + to_u16!(position), + to_u16!(n_chars), + ) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"deleted-text\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + deleted_text_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + fn connect_inserted_text( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn inserted_text_trampoline( + this: *mut ffi::GtkEntryBuffer, + position: libc::c_uint, + chars: *mut libc::c_char, + n_chars: libc::c_uint, + f: glib::ffi::gpointer, + ) where + P: IsA, + { + let f: &F = &*(f as *const F); + f( + &EntryBuffer::from_glib_borrow(this).unsafe_cast_ref(), + to_u16!(position), + &glib::GString::from_glib_borrow(chars), + to_u16!(n_chars), + ) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"inserted-text\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + inserted_text_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::prelude::EntryBufferExt; + use crate::TEST_THREAD_WORKER; + + #[test] + fn test_entry_buffer() { + TEST_THREAD_WORKER + .push(move || { + let text = crate::Text::new(); + let buffer = text.buffer().unwrap(); + buffer.insert_text(0, "Hello world"); + assert_eq!(buffer.text(), "Hello world"); + buffer.connect_inserted_text(move |buffer, pos, text, length| { + assert_eq!(length, 11); + assert_eq!(pos, 1); + }); + buffer.emit_inserted_text(0, "hello world", 11); + std::thread::sleep(std::time::Duration::from_secs(10)); + }) + .expect("Failed to schedule a test call"); + } } From c176b1a45155f0b3b66ace46dc38a4d278e65d97 Mon Sep 17 00:00:00 2001 From: Bilal Elmoussaoui Date: Sat, 8 May 2021 22:30:36 +0200 Subject: [PATCH 4/5] gtk: update EntryBuffer subclassing to use u16 --- gtk4/src/lib.rs | 5 +- gtk4/src/subclass/entry_buffer.rs | 119 +++++++++++++++--------------- 2 files changed, 63 insertions(+), 61 deletions(-) diff --git a/gtk4/src/lib.rs b/gtk4/src/lib.rs index 762da6d0e740..84e0f4364f32 100644 --- a/gtk4/src/lib.rs +++ b/gtk4/src/lib.rs @@ -226,9 +226,11 @@ pub(crate) static TEST_THREAD_WORKER: once_cell::sync::Lazy = mod auto; #[macro_use] -pub mod subclass; +mod entry_buffer; #[macro_use] mod expression; +#[macro_use] +pub mod subclass; pub mod prelude; @@ -262,7 +264,6 @@ mod drop_down; mod drop_target; mod editable; mod entry; -mod entry_buffer; mod entry_completion; mod enums; mod event_controller_key; diff --git a/gtk4/src/subclass/entry_buffer.rs b/gtk4/src/subclass/entry_buffer.rs index 77f0e2c501d7..537922c05409 100644 --- a/gtk4/src/subclass/entry_buffer.rs +++ b/gtk4/src/subclass/entry_buffer.rs @@ -4,31 +4,23 @@ use crate::subclass::prelude::*; use crate::EntryBuffer; use glib::translate::*; use glib::{Cast, GString, Object, ObjectExt}; +use libc::c_uint; use once_cell::sync::Lazy; pub trait EntryBufferImpl: EntryBufferImplExt + ObjectImpl { - fn delete_text(&self, entry_buffer: &Self::Type, position: u32, n_chars: Option) -> u32 { - self.parent_delete_text(entry_buffer, position, n_chars) - } + fn delete_text(&self, entry_buffer: &Self::Type, position: u16, n_chars: Option) -> u16; - fn deleted_text(&self, entry_buffer: &Self::Type, position: u32, n_chars: Option) { + fn deleted_text(&self, entry_buffer: &Self::Type, position: u16, n_chars: Option) { self.parent_deleted_text(entry_buffer, position, n_chars) } - #[doc(alias = "get_length")] - fn length(&self, entry_buffer: &Self::Type) -> u32 { - self.parent_length(entry_buffer) - } + fn length(&self, entry_buffer: &Self::Type) -> u16; - #[doc(alias = "get_text")] - fn text(&self, entry_buffer: &Self::Type) -> GString { - self.parent_text(entry_buffer) - } - fn insert_text(&self, entry_buffer: &Self::Type, position: u32, chars: &str) -> u32 { - self.parent_insert_text(entry_buffer, position, chars) - } + fn text(&self, entry_buffer: &Self::Type) -> GString; + + fn insert_text(&self, entry_buffer: &Self::Type, position: u16, chars: &str) -> u16; - fn inserted_text(&self, entry_buffer: &Self::Type, position: u32, chars: &str) { + fn inserted_text(&self, entry_buffer: &Self::Type, position: u16, chars: &str) { self.parent_inserted_text(entry_buffer, position, chars) } } @@ -37,41 +29,41 @@ pub trait EntryBufferImplExt: ObjectSubclass { fn parent_delete_text( &self, entry_buffer: &Self::Type, - position: u32, - n_chars: Option, - ) -> u32; - fn parent_deleted_text(&self, entry_buffer: &Self::Type, position: u32, n_chars: Option); - fn parent_length(&self, entry_buffer: &Self::Type) -> u32; + position: u16, + n_chars: Option, + ) -> u16; + fn parent_deleted_text(&self, entry_buffer: &Self::Type, position: u16, n_chars: Option); + fn parent_length(&self, entry_buffer: &Self::Type) -> u16; fn parent_text(&self, entry_buffer: &Self::Type) -> GString; - fn parent_insert_text(&self, entry_buffer: &Self::Type, position: u32, chars: &str) -> u32; - fn parent_inserted_text(&self, entry_buffer: &Self::Type, position: u32, chars: &str); + fn parent_insert_text(&self, entry_buffer: &Self::Type, position: u16, chars: &str) -> u16; + fn parent_inserted_text(&self, entry_buffer: &Self::Type, position: u16, chars: &str); } impl EntryBufferImplExt for T { fn parent_delete_text( &self, entry_buffer: &Self::Type, - position: u32, - n_chars: Option, - ) -> u32 { + position: u16, + n_chars: Option, + ) -> u16 { unsafe { let data = T::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( + to_u16!(f( entry_buffer .unsafe_cast_ref::() .to_glib_none() .0, - position, - n_chars.unwrap_or(u32::MAX), - ) + position as c_uint, + n_chars.unwrap_or(u16::MAX) as c_uint, + )) } } - fn parent_deleted_text(&self, entry_buffer: &Self::Type, position: u32, n_chars: Option) { + fn parent_deleted_text(&self, entry_buffer: &Self::Type, position: u16, n_chars: Option) { unsafe { let data = T::type_data(); let parent_class = data.as_ref().parent_class() as *mut ffi::GtkEntryBufferClass; @@ -81,24 +73,24 @@ impl EntryBufferImplExt for T { .unsafe_cast_ref::() .to_glib_none() .0, - position, - n_chars.unwrap_or(u32::MAX), + position as c_uint, + n_chars.unwrap_or(u16::MAX) as c_uint, ) } } } - fn parent_length(&self, entry_buffer: &Self::Type) -> u32 { + fn parent_length(&self, entry_buffer: &Self::Type) -> u16 { unsafe { let data = T::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(entry_buffer + to_u16!(f(entry_buffer .unsafe_cast_ref::() .to_glib_none() - .0) + .0)) } } @@ -121,7 +113,7 @@ impl EntryBufferImplExt for T { } } - fn parent_insert_text(&self, entry_buffer: &Self::Type, position: u32, text: &str) -> u32 { + fn parent_insert_text(&self, entry_buffer: &Self::Type, position: u16, text: &str) -> u16 { unsafe { let data = T::type_data(); let parent_class = data.as_ref().parent_class() as *mut ffi::GtkEntryBufferClass; @@ -129,19 +121,19 @@ impl EntryBufferImplExt for T { .insert_text .expect("No parent class impl for \"insert_text\""); - f( + to_u16!(f( entry_buffer .unsafe_cast_ref::() .to_glib_none() .0, - position, + position as c_uint, text.to_glib_none().0, - text.chars().count() as u32, - ) + text.chars().count() as c_uint, + )) } } - fn parent_inserted_text(&self, entry_buffer: &Self::Type, position: u32, text: &str) { + fn parent_inserted_text(&self, entry_buffer: &Self::Type, position: u16, text: &str) { unsafe { let data = T::type_data(); let parent_class = data.as_ref().parent_class() as *mut ffi::GtkEntryBufferClass; @@ -151,9 +143,9 @@ impl EntryBufferImplExt for T { .unsafe_cast_ref::() .to_glib_none() .0, - position, + position as c_uint, text.to_glib_none().0, - text.chars().count() as u32, + text.chars().count() as c_uint, ) } } @@ -187,13 +179,17 @@ unsafe extern "C" fn entry_buffer_delete_text( let imp = instance.impl_(); let wrap: Borrowed = from_glib_borrow(ptr); - let n_chars = if n_chars == u32::MAX { + let n_chars = if n_chars == u16::MAX as c_uint { None } else { Some(n_chars) }; - imp.delete_text(wrap.unsafe_cast_ref(), position, n_chars) + imp.delete_text( + wrap.unsafe_cast_ref(), + to_u16!(position), + n_chars.map(|c| to_u16!(c)), + ) as c_uint } unsafe extern "C" fn entry_buffer_deleted_text( @@ -205,13 +201,17 @@ unsafe extern "C" fn entry_buffer_deleted_text( let imp = instance.impl_(); let wrap: Borrowed = from_glib_borrow(ptr); - let n_chars = if n_chars == u32::MAX { + let n_chars = if n_chars == u16::MAX as c_uint { None } else { Some(n_chars) }; - imp.deleted_text(wrap.unsafe_cast_ref(), position, n_chars) + imp.deleted_text( + wrap.unsafe_cast_ref(), + to_u16!(position), + n_chars.map(|c| to_u16!(c)), + ) } static GET_TEXT_QUARK: Lazy = @@ -226,7 +226,9 @@ unsafe extern "C" fn entry_buffer_get_text( let wrap: Borrowed = from_glib_borrow(ptr); let ret = imp.text(wrap.unsafe_cast_ref()); - *n_bytes = ret.len(); + if !n_bytes.is_null() { + *n_bytes = ret.len(); + } // Ensures that the returned text stays alive for as long as // the entry buffer instance let fullptr = ret.to_glib_full(); @@ -241,7 +243,7 @@ unsafe extern "C" fn entry_buffer_get_length( let imp = instance.impl_(); let wrap: Borrowed = from_glib_borrow(ptr); - imp.length(wrap.unsafe_cast_ref()) + imp.length(wrap.unsafe_cast_ref()) as c_uint } unsafe extern "C" fn entry_buffer_insert_text( @@ -255,8 +257,8 @@ unsafe extern "C" fn entry_buffer_insert_text( let wrap: Borrowed = from_glib_borrow(ptr); let text: Borrowed = from_glib_borrow(charsptr); - let chars = text_n_chars(&text, n_chars); - imp.insert_text(wrap.unsafe_cast_ref(), position, chars) + let chars = text_n_chars(&text, to_u16!(n_chars)); + imp.insert_text(wrap.unsafe_cast_ref(), to_u16!(position), chars) as c_uint } unsafe extern "C" fn entry_buffer_inserted_text( @@ -270,13 +272,12 @@ unsafe extern "C" fn entry_buffer_inserted_text( let wrap: Borrowed = from_glib_borrow(ptr); let text: Borrowed = from_glib_borrow(charsptr); - let chars = text_n_chars(&text, length); - imp.inserted_text(wrap.unsafe_cast_ref(), position, &chars) + let chars = text_n_chars(&text, to_u16!(length)); + imp.inserted_text(wrap.unsafe_cast_ref(), to_u16!(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 { +fn text_n_chars(text: &str, n_chars: u16) -> &str { + if n_chars != u16::MAX && n_chars > 0 { let mut iter = text .char_indices() .skip((n_chars - 1) as usize) @@ -302,7 +303,7 @@ mod test { #[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"); + assert_eq!(text_n_chars("gtk-rs bindings", u16::MAX), "gtk-rs bindings"); } #[test] @@ -316,7 +317,7 @@ mod test { 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("👨👩👧👦", u16::MAX), "👨👩👧👦"); assert_eq!(text_n_chars("كتاب", 2), "كت"); } From 1098c1fc3370cb24e5d50289e99eeee7fee762dc Mon Sep 17 00:00:00 2001 From: Bilal Elmoussaoui Date: Sun, 9 May 2021 02:32:55 +0200 Subject: [PATCH 5/5] examples: add a custom Entry Buffer --- examples/Cargo.toml | 4 + examples/README.md | 1 + examples/custom_entry_buffer/README.md | 3 + .../custom_entry_buffer/entry_buffer/imp.rs | 128 ++++++++++++++++++ .../custom_entry_buffer/entry_buffer/mod.rs | 19 +++ examples/custom_entry_buffer/main.rs | 41 ++++++ 6 files changed, 196 insertions(+) create mode 100644 examples/custom_entry_buffer/README.md create mode 100644 examples/custom_entry_buffer/entry_buffer/imp.rs create mode 100644 examples/custom_entry_buffer/entry_buffer/mod.rs create mode 100644 examples/custom_entry_buffer/main.rs diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 6ea34bd524d6..7b1d7feb34a8 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -44,6 +44,10 @@ path = "custom_application/main.rs" name = "custom_editable" path = "custom_editable/main.rs" +[[bin]] +name = "custom_entry_buffer" +path = "custom_entry_buffer/main.rs" + [[bin]] name = "custom_layout_manager" path = "custom_layout_manager/main.rs" diff --git a/examples/README.md b/examples/README.md index 4de435458c3d..f136cd873532 100644 --- a/examples/README.md +++ b/examples/README.md @@ -9,6 +9,7 @@ Consists of various examples to get familiar with writing GTK applications using - [CSS](./css/) - [Custom Application](./custom_application/) - [Custom Editable](./custom_editable/) +- [Custom Entry Buffer](./custom_entry_buffer/) - [Custom Layout Manager](./custom_layout_manager/) - [Custom Orientable](./custom_orientable/) - [Custom Paintable](./custom_paintable/) diff --git a/examples/custom_entry_buffer/README.md b/examples/custom_entry_buffer/README.md new file mode 100644 index 000000000000..fd36e312b3e7 --- /dev/null +++ b/examples/custom_entry_buffer/README.md @@ -0,0 +1,3 @@ +# Custom Entry Buffer + +The example shows how to create a custom `gtk::EntryBuffer`. A `gtk::EntryBuffer` can be used to fed a `gtk::Text` for example. diff --git a/examples/custom_entry_buffer/entry_buffer/imp.rs b/examples/custom_entry_buffer/entry_buffer/imp.rs new file mode 100644 index 000000000000..776811223279 --- /dev/null +++ b/examples/custom_entry_buffer/entry_buffer/imp.rs @@ -0,0 +1,128 @@ +use glib::{ParamFlags, ParamSpec, Value}; +use gtk::glib; +use gtk::prelude::*; +use gtk::subclass::prelude::*; +use once_cell::sync::Lazy; +use std::cell::{Cell, RefCell}; +use std::rc::Rc; + +#[derive(Debug)] +pub struct CustomEntryBuffer { + text: Rc>, + length: Cell, + max_length: Cell, +} + +impl Default for CustomEntryBuffer { + fn default() -> Self { + Self { + text: Rc::new(RefCell::new(String::new())), + length: Cell::new(0), + max_length: Cell::new(0), + } + } +} + +#[glib::object_subclass] +impl ObjectSubclass for CustomEntryBuffer { + const NAME: &'static str = "CustomEntryBuffer"; + type Type = super::CustomEntryBuffer; + type ParentType = gtk::EntryBuffer; +} + +impl ObjectImpl for CustomEntryBuffer { + fn properties() -> &'static [ParamSpec] { + static PROPERTIES: Lazy> = Lazy::new(|| { + vec![ + ParamSpec::new_uint( + "length", + "length", + "Length", + 0, + u16::MAX as u32, + 0, + ParamFlags::READABLE, + ), + ParamSpec::new_uint( + "max-length", + "Maximum length", + "Maximum number of characters for this entry", + 0, + u16::MAX as u32, + 0, + ParamFlags::READWRITE | ParamFlags::EXPLICIT_NOTIFY, + ), + ParamSpec::new_string( + "text", + "Text", + "The contents of the buffer", + Some(""), + ParamFlags::READWRITE, + ), + ] + }); + PROPERTIES.as_ref() + } + + fn property(&self, _obj: &Self::Type, _id: usize, pspec: &ParamSpec) -> Value { + match pspec.name() { + "length" => (self.length.get() as u32).to_value(), + "max-length" => (self.max_length.get() as u32).to_value(), + "text" => self.text.borrow().to_value(), + _ => unreachable!(), + } + } + + fn set_property(&self, _obj: &Self::Type, _id: usize, value: &Value, pspec: &ParamSpec) { + match pspec.name() { + "length" => { + self.length.set(value.get::().unwrap() as u16); + } + "max-length" => { + self.max_length.set(value.get::().unwrap() as u16); + } + "text" => { + self.text.replace(value.get().unwrap()); + } + _ => unreachable!(), + } + } +} + +impl EntryBufferImpl for CustomEntryBuffer { + fn text(&self, _entry_buffer: &Self::Type) -> glib::GString { + self.text.borrow().clone().into() + } + + fn length(&self, _entry_buffer: &Self::Type) -> u16 { + self.text.borrow().chars().count() as u16 + } + + fn insert_text(&self, entry_buffer: &Self::Type, _position: u16, chars: &str) -> u16 { + self.text.borrow_mut().insert_str(0, chars); + let n_chars = chars.chars().count() as u16; + let new_length = self.length.get() + n_chars; + self.length.set(new_length); + + entry_buffer.notify("text"); + entry_buffer.notify("length"); + n_chars + } + + fn delete_text(&self, entry_buffer: &Self::Type, position: u16, n_chars: Option) -> u16 { + let deleted_chars = n_chars.unwrap_or(u16::MAX); + println!("{}", position); + println!("{:#?}", self.text.borrow()); + let text = self.text.borrow().chars().skip(position as usize).collect(); + println!("{}", text); + self.text.replace(text); + + let length = (self.length.get() - deleted_chars).max(0); + self.length.set(length); + + entry_buffer.notify("text"); + entry_buffer.notify("length"); + + deleted_chars + } +} diff --git a/examples/custom_entry_buffer/entry_buffer/mod.rs b/examples/custom_entry_buffer/entry_buffer/mod.rs new file mode 100644 index 000000000000..4dcaa1c57432 --- /dev/null +++ b/examples/custom_entry_buffer/entry_buffer/mod.rs @@ -0,0 +1,19 @@ +mod imp; + +use gtk::glib; + +glib::wrapper! { + pub struct CustomEntryBuffer(ObjectSubclass) @extends gtk::EntryBuffer; +} + +impl Default for CustomEntryBuffer { + fn default() -> Self { + Self::new() + } +} + +impl CustomEntryBuffer { + pub fn new() -> Self { + glib::Object::new(&[]).expect("Failed to create a CustomEntryBuffer") + } +} diff --git a/examples/custom_entry_buffer/main.rs b/examples/custom_entry_buffer/main.rs new file mode 100644 index 000000000000..5fac8a8ee3db --- /dev/null +++ b/examples/custom_entry_buffer/main.rs @@ -0,0 +1,41 @@ +mod entry_buffer; + +use entry_buffer::CustomEntryBuffer; +use gtk::prelude::*; + +fn main() { + let application = gtk::Application::new( + Some("com.github.gtk-rs.examples.entry-buffer"), + Default::default(), + ); + + application.connect_activate(build_ui); + application.run(); +} + +fn build_ui(application: >k::Application) { + let window = gtk::ApplicationWindow::new(application); + window.set_title(Some("Custom Entry Buffer")); + window.set_default_size(500, 500); + + let container = gtk::Box::new(gtk::Orientation::Vertical, 12); + container.set_valign(gtk::Align::Center); + container.set_halign(gtk::Align::Center); + + let buffer = CustomEntryBuffer::new(); + + let text1 = gtk::Text::new(); + text1.set_buffer(&buffer); + container.append(&text1); + + let text2 = gtk::Text::new(); + text2.set_buffer(&buffer); + container.append(&text2); + + let entry = gtk::Entry::new(); + entry.set_buffer(&buffer); + container.append(&entry); + + window.set_child(Some(&container)); + window.show(); +}