Subclass ListStore in Rust

I’m trying to subclass ListStore in Rust, but get the error:

error[E0277]: the trait bound `gtk::ListStore: IsSubclassable<document::imp::Document>` is not satisfied
   --> src/document/imp.rs:15:23
    |
15  |     type ParentType = gtk::ListStore;
    |                       ^^^^^^^^^^^^^^ the trait `IsSubclassable<document::imp::Document>` is not implemented for `gtk::ListStore`
    |
    = help: the following other types implement trait `IsSubclassable<T>`:
              ApplicationWindow
              Bin
              CellRenderer
              CellRendererAccel
              CellRendererCombo
              CellRendererPixbuf
              CellRendererProgress
              CellRendererSpin
            and 31 others
note: required by a bound in `gtk::subclass::prelude::ObjectSubclass::ParentType`
   --> /home/jeff/.cargo/registry/src/github.com-1ecc6299db9ec823/glib-0.16.7/src/subclass/types.rs:552:22
    |
552 |     type ParentType: IsSubclassable<Self>
    |                      ^^^^^^^^^^^^^^^^^^^^ required by this bound in `gtk::subclass::prelude::ObjectSubclass::ParentType`

am I missing an @implements or have I got something else wrong?

The snippet is:

#[glib::object_subclass]
impl ObjectSubclass for Document {
    const NAME: &'static str = "Document";
    type Type = super::Document;
    type ParentType = gtk::ListStore;
}

I’d be grateful for any insight.

ListStore is a final type: you cannot subclass it.

1 Like

Thanks for the reply.

I wanted to subclass ListStore because I wanted a model that had features in ListStore:

  • a couple of columns to pass to a TreeView
  • a extra column of data to act as an identifier, hidden from the TreeView

plus some extra functionality:

  • mirror the above columns to an sqlite table
  • manage other sqlite tables when inserting/changing/deleting rows

Is my best option then to subclass a ListModel? Or do you see a simpler possibility?

You could implement a ListModel and inside it store a ListStore, and then proxy all calls accordingly to/from that.

But it would probably be better to fully implement your own model and directly map that to the sqlite table.

OK. Thanks for the suggestion.

Presumably, I can use the list_box_model example as a basis for how to subclass the ListModel?

And if I do so, and want one of the columns to contain Pixbufs, what ParamSpec do I use, as there isn’t a ParamSpecPixbuf?

Edit: Sorry for the noise, it seems that ParamSpecObject works with type Pixbuf::static_type().

But the bindings panic at my_tree_view.set_model(Some(&my_subclassed_model));:

thread 'main' panicked at 'assertion failed: self.is::<T>()', /home/jeff/.cargo/registry/src/github.com-1ecc6299db9ec823/glib-0.16.7/src/object.rs:125:23
stack backtrace:
   0: rust_begin_unwind
             at /usr/src/rustc-1.63.0/library/std/src/panicking.rs:584:5
   1: core::panicking::panic_fmt
             at /usr/src/rustc-1.63.0/library/core/src/panicking.rs:142:14
   2: core::panicking::panic
             at /usr/src/rustc-1.63.0/library/core/src/panicking.rs:48:5
   3: glib::object::Cast::unsafe_cast_ref
             at /home/jeff/.cargo/registry/src/github.com-1ecc6299db9ec823/glib-0.16.7/src/object.rs:280:9
   4: glib::object::Cast::upcast_ref
             at /home/jeff/.cargo/registry/src/github.com-1ecc6299db9ec823/glib-0.16.7/src/object.rs:125:18
   5: <scantpaper::document::Document as core::convert::AsRef<gtk::auto::tree_model::TreeModel>>::as_ref
             at /home/jeff/.cargo/registry/src/github.com-1ecc6299db9ec823/glib-0.16.7/src/object.rs:1290:17
   6: <O as gtk::auto::tree_view::TreeViewExt>::set_model::{{closure}}
             at /home/jeff/.cargo/registry/src/github.com-1ecc6299db9ec823/gtk-0.16.2/src/auto/tree_view.rs:2061:31
   7: core::option::Option<T>::map
             at /usr/src/rustc-1.63.0/library/core/src/option.rs:929:29
   8: <O as gtk::auto::tree_view::TreeViewExt>::set_model
             at /home/jeff/.cargo/registry/src/github.com-1ecc6299db9ec823/gtk-0.16.2/src/auto/tree_view.rs:2061:17

I assume that @implements gio::ListModel, gtk::TreeModel is not enough.

I can subclass ListStore with Python with no problem. So Is your statement above just true for Rust?

No, it’s a bug in the Python bindings that you can do that there :slight_smile:

Given how well the bug works in Python, it would be cool to have it in Rust, too!

That’s not going to happen, but that will sooner or later get fixed in the Python bindings. Creating a subclass of the ListStore is not supported and can break in unexpected ways. Use composition instead of subclassing for what you want to do here, or even better implement the model yourself around the sqlite API instead of having effectively two models in place.

Oh, wait: I misread, and I thought you were trying to subclass gio::ListStore. Sorry.

gtk::ListStore is derivable, but in order to derive it you need to do a bunch of stuff like setting up the columns inside the instance initialization function. In practice, it’s very messy and there’s no real point in doing so.

Additionally, writing new code in GTK3—especially with the Rust bindings—isn’t really recommended; The GtkTreeView and friends API has just been deprecated in the main development branch (which will be released as GTK 4.10). For newly written code, the recommendation is to use GTK 4, the list view widgets, and the list model API from GIO.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.