If bugmasters are allowed to blog wishlists, then developers should also be allowed to write them! Which is why I wrote my wishlist!
Gtk.TreeModel was in my humble opinion designed wrong. In API design should an interface be just one thing.
A little bit of history
Many framework designers have repeated this in the past. Two of the best framework designers that we have on this planet, Krysztof Cwalina and Brad Abrams from Microsoft, added the meme to one of their books. It would be unfair to only mention those two guys and not the other people at Microsoft, and before that at the Delphi team at Borland. Brian Pepin notes at page 83 of Framework Design Guidelines: “Another sign that you’ve got a well defined interface is that the interface does exactly one thing. If you have an interface that has a grab bag of functionality, that’s a warning sign.”
The problem
What are the things a Gtk.TreeModel are or represent?
- It’s something that is iterable
- It’s something that is an iterator
- It’s apparently something that has columns, which should have been at the View’s side of the story
- It’s something that can be a tree
- It’s something that emits row changes
That’s not one thing, and therefore we have a warning sign. If I count it correctly that’s at least five things, so that’s a big warning sign.
I’m sure I can come up with a few other things that a Gtk.TreeModel actually represents. For example its unref_node and ref_node make me think that it’s a garbage collector or something, too.
This is absolutely not good. I believe it is what makes the interface shockingly complicated. Because none of those five things can be made reusable this way.
What I think would be the right way
A prerequisite for this, and presumably also the reason why Gtk+ developers decided to do Gtk.TreeModel the way they did it a few years ago, is a collection framework.
Sadly is this proposal being ~ignored by the current GLib maintainers. Understandable because everybody is overloaded and busy, but in my opinion it’s nonetheless blocking us from heading in the right direction.
There are by the way quite a lot of other reasons mentioned on the proposal. This is just one of them.
interface GLib.Iterable {
Iterator iterator();
}
interface GLib.Iterator {
bool next ();
object current;
}
Next would be recursive iterators or trees. There are many ways to represent these, but I’ll just take a simple route. Remember that when picking an API design, the most simple idea is often the most right one. But yeah, you can probably improve this.
interface GLib.TreeIterable : GLib.Iterable {
GLib.TreeIterable get_children ();
int n_children;
bool has_child (GLib.TreeIterable e);
GLib.TreeIterable parent;
}
In Gtk+ we would have the view, of course. It would hold the columns, as it should be.
class Gtk.TreeView {
int n_columns;
GLib.Type get_column_type (int n);
GLib.TreeModel model;
Gtk.ColumnBinding binding;
Gtk.TreeView (GLib.TreeModel m);
GLib.ColumnBinding column_binding;
}
We don’t have guaranteed introspection in Gtk+. To do the binding between a column in the view and a property of an instance in the model we need some code. In Gtk.TreeModel this is the get_value function.
It shouldn’t be part of the Gtk.TreeModel: That way it ain’t reusable and will it require each person implementing a Gtk.TreeModel to reinvent the code.
abstract class GLib.ColumnBinding {
abstract GLib.Value get_value (GLib.TreeModel model,
GLib.TreeIterable e,
int column);
}
Let’s have some concrete column bindings:
class Gtk.TreeStoreColumnBinding : GLib.ColumnBinding {
}
class Gtk.ListStoreColumnBinding : Gtk.TreeStoreColumnBinding {
}
If we do have introspection we can do the same thing .NET offers: Link up the column number with a property name that can be found in the type of the instances that the model holds.
class GI.IntrospectColumnBinding : GLib.ColumnBinding {
void add_column (int column, string prop_name);
}
These wouldn’t change at all, except that they implement GLib.TreeModel instead of Gtk.TreeModel
class Gtk.TreeStore : GLib.TreeModel {
}
class Gtk.ListStore : GLib.TreeModel {
}
And then we are at Gtk.TreeModel, of course. Well just take everything that we don’t do yet. That’s the row change emissions, right? Personally I think rows are too specific. A model is something that can be iterated. Being iterable doesn’t mean that you have rows, it just means that you have things that the consumer, the view in a model’s case, can iterate to. Let’s call them nodes.
Gtk.TreePath sounds to me like serializing and deserializing a location. It’s nothing special, just a way to formulate pointing to a node in the tree. It’s the model that exposes this capability.
I’m not sure about flags. Maybe it should just be moved to Gtk.TreeView. I don’t get the point of the flags anyway. Both ITERS_PERSIST and LIST_ONLY sound like an implementation detail to me: not something you want to expose to the API anyway. But fine, for sake of completeness I’ll put it here.
interface GLib.TreeModel : GLib.TreeIterable {
signal node_changed (GLib.TreeIterable e);
signal node_inserted (GLib.TreeIterable e);
signal node_deleted (GLib.TreeIterable e);
signal node_reordered (GLib.TreeIterable e);
GLib.TreeModelFlags flags;
GLib.TreePath get_path (GLib.TreeIterable e);
GLib.TreeIterable get_node (GLib.TreePath p);
}
Who’ll start GLib 4.0? Let’s do this stuff while the desktop guys play with GNOME 3.0? Why not?