Trying to understand GTK4-RS model binding

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 {
    pub entry: TemplateChild<Entry>,
    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
        .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

      // Note that here they're using the `tasks()` method that returns a
     // clone of the model.
      let selection_model = NoSelection::new(Some(self.tasks()));

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
            .connect_activate(clone!(@weak self as window => move |_| {

        // Setup callback for clicking (and the releasing) the icon of the entry
            clone!(@weak self as window => move |_,_| {
// 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() {

        // 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

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

        // Setup
        let obj = self.obj();

Most of the types in GTK, GLib, and Gio are wrappers around pointers obtained from their respective C libraries. The actual objects in those libraries are reference counted, so you can think of gio::ListStore as being an Arc<gio::ListStore> where calling Clone increments the reference count rather than creating a new object


This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.