-
-
Notifications
You must be signed in to change notification settings - Fork 175
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gtk: more manual stuff 4 #229
base: main
Are you sure you want to change the base?
Changes from all commits
35daaa6
50e3139
2d18c81
c176b1a
1098c1f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<RefCell<String>>, | ||
length: Cell<u16>, | ||
max_length: Cell<u16>, | ||
} | ||
|
||
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<Vec<ParamSpec>> = 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::<u32>().unwrap() as u16); | ||
} | ||
"max-length" => { | ||
self.max_length.set(value.get::<u32>().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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Both the current insert_text & delete_text methods are wrong due to their complexity on figuring out the bytes position back from the chars position where to insert/delete the string. I was wondering if there might some kind of API/external crate that helps for such situations? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
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>) -> 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 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
mod imp; | ||
|
||
use gtk::glib; | ||
|
||
glib::wrapper! { | ||
pub struct CustomEntryBuffer(ObjectSubclass<imp::CustomEntryBuffer>) @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") | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is all not really great to use, is there a better way to handle this than having to clone the string? or is it fine to do so as the string can't be that huge due to the EntryBuffer limitation of being at max of a
u16::MAX
length?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know. You could in theory just return a reference here if it isn't required to be NUL-terminated.