Skip to content
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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/)
Expand Down
3 changes: 3 additions & 0 deletions examples/custom_entry_buffer/README.md
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.
128 changes: 128 additions & 0 deletions examples/custom_entry_buffer/entry_buffer/imp.rs
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()
Copy link
Member Author

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?

Copy link
Member

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.

}

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 {
Copy link
Member Author

Choose a reason for hiding this comment

The 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?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The std API is not enough? How do actual implementors of this in C handle it?

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
}
}
19 changes: 19 additions & 0 deletions examples/custom_entry_buffer/entry_buffer/mod.rs
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")
}
}
41 changes: 41 additions & 0 deletions examples/custom_entry_buffer/main.rs
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: &gtk::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();
}
18 changes: 13 additions & 5 deletions gtk4/Gir.toml
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,6 @@ ignore = [
]

manual = [
# "GObject.ObjectClass",
# TODO "GLib.Scanner",
"cairo.Context",
"cairo.FontOptions",
Expand Down Expand Up @@ -961,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"
Expand Down Expand Up @@ -1044,9 +1051,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"
Expand Down Expand Up @@ -1630,7 +1638,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]]
Expand Down
27 changes: 0 additions & 27 deletions gtk4/src/auto/entry_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<F: Fn(&Self) + 'static>(&self, f: F) -> SignalHandlerId;

Expand All @@ -75,27 +69,6 @@ pub trait EntryBufferExt: 'static {
}

impl<O: IsA<EntryBuffer>> EntryBufferExt for O {
fn emit_deleted_text(&self, position: u32, n_chars: u32) {
bilelmoussaoui marked this conversation as resolved.
Show resolved Hide resolved
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<F: Fn(&Self) + 'static>(&self, f: F) -> SignalHandlerId {
unsafe extern "C" fn notify_length_trampoline<P, F: Fn(&P) + 'static>(
Expand Down
Loading