Hey there,
I've been following this tutorial on the GTK4-RS library.
On the template bellow they declared a tasks
attribute, and on the impl
block they create a method for accessing it that returns a clone the value inside the RefCell
. If I understood correctly, this object is a Model List, when a user submits an input the program will create a new model and store it in this list which will be used to update the view.
What I don't understand and will highlight in the code bellow is; whenever a new model is appended to the list, its actually appending to a clone of it, not a reference the actual list that is used to update the view, and yet the view gets updated. What is going on here, is it something particular to this library implementation ?
// Object holding the state
#[derive(CompositeTemplate, Default)]
#[template(resource = "/org/gtk_rs/Todo1/window.ui")]
pub struct Window {
#[template_child]
pub entry: TemplateChild<Entry>,
#[template_child]
pub tasks_list: TemplateChild<ListView>,
// This is the model list
pub tasks: RefCell<Option<gio::ListStore>>,
}
This is the method that returns the clone
fn tasks(&self) -> gio::ListStore {
// Get state
self.imp()
.tasks
.borrow()
.clone()
.expect("Could not get current tasks.")
}
This is where the model list is bound to the view
fn setup_tasks(&self) {
// Create new model
let model = gio::ListStore::new(TaskObject::static_type());
// Get state and set model
self.imp().tasks.replace(Some(model));
// Note that here they're using the `tasks()` method that returns a
// clone of the model.
let selection_model = NoSelection::new(Some(self.tasks()));
self.imp().tasks_list.set_model(Some(&selection_model));
}
The two methods bellow are used to update the model with the user input
// Listening for the signal
fn setup_callbacks(&self) {
// Setup callback for activation of the entry
self.imp()
.entry
.connect_activate(clone!(@weak self as window => move |_| {
window.new_task();
}));
// Setup callback for clicking (and the releasing) the icon of the entry
self.imp().entry.connect_icon_release(
clone!(@weak self as window => move |_,_| {
window.new_task();
}),
);
}
// Actually creating the new model
fn new_task(&self) {
// Get content from entry and clear it
let buffer = self.imp().entry.buffer();
let content = buffer.text().to_string();
if content.is_empty() {
return;
}
buffer.set_text("");
// Add new task to model
let task = TaskObject::new(false, content);
// Here it is, appending to a clone, doesn't update the list that's
// actually beeing watched anywhere
self.tasks().append(&task);
}
Also note that these setup methods only run when the window is created, essentially, they run when the program starts and never again.
// Trait shared by all GObjects
impl ObjectImpl for Window {
fn constructed(&self) {
// Call "constructed" on parent
self.parent_constructed();
// Setup
let obj = self.obj();
obj.setup_tasks();
obj.setup_callbacks();
obj.setup_factory();
}
}