Re: Model/view ideas for GtkListBox/GtkFlowBox
To get some idea what we are talking about here, I've started by writing a toy implementation of a list that reuses rows. You can find it here: https://github.com/matthiasclasen/list The example binary can scroll a 50.000 row list with labels or entries without noticeable problems. Lessons learned so far: - Average row heights are a little tricky. If you scroll into a region where your rows happen to be 2 lines instead of one, your list jumps to twice the size. I ended up using a very slow-changing average to counteract this. - Scrolling with average heights needs to be done carefully. Otherwise, you'll never reach 'the far end'. I ended up doing size allocation from the end that we're closer to. I was afraid that this would make the 'jump in the middle', but it seems to work mostly ok. There's of course a lot of stuff missing here: - no keynav - no sorting - no headers - no caching beyond the visible range - no pixelcache (do we need it ?) - no attempt to keep a stable row-item relation while an item is visible. I just always reconnect all rows But I found this exercise pretty enlightening. Matthias ___ gtk-devel-list mailing list gtk-devel-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-devel-list
Re: Model/view ideas for GtkListBox/GtkFlowBox
On ons, 2013-10-23 at 13:16 +0200, Alberto Ruiz wrote: Hey Alex, I've been playing with this idea myself, have a look at this github repo[0], don't pay too much attention to the ListView widget, I'm pretty much replicating Gtk.ListBox for the sake of understanding how to implement such widget. I am trying to experiement with a scalable approach where you can have a large data set and a bound set of allocated widgets necessary to display in the scrolledwindow (and also to avoid new widget allocations while you scroll). Some people think this is a bit of an overkill so I don't want to get too deep into this discussion while it's still an experiment. Its not overkill at all. It is exactly what you want, and something e.g. Android always does for its views (its called view recycling there, google for it [1]). Basically you will have a set of row widgets created which are enough to fill the screen and then some. And when they go offscreen we reuse them (with new data) when scrolling in new rows. This means we can reuse widgets, avoiding using lots of memory for objects, and avoiding constant creation/destruction of objects. There is one thing that I'd like to discuss about the API that I designed there and it is the notion of the RowDelegate, which is an interface that requires GtkBin and points to a specific index (or row) of a GObjectSet (Data.List in the source). I don't think forcing each row to implement a specific interface (or even have a custom type) is the right thing. I mean, I think it will often be the case that you'd do this, but I also think something simple like a list with text should be able to just have a GtkLabel in the row and binding e.g. the name property on the model to the label property on the GtkLabel. I.e. first make the simple case simple (but efficient/smart) and then the complex possible. In a more complex situation you can create a custom row type with its own properties you can bind. Or, you can use class templates and bind model properties to child name (i.e. gtk_widget_get_template_child) + a property name on the child. Or in the most complex cases you can use GGBindingTransformFuncs or a raw callback that applies the model to the row. This kind of setup allows the view itself to handle a lot of the code that otherwise each RowDelegate would have to do, like track minimal model changes, only modifying the row widget properties that changed, queue changes updates to one-per-frame, etc. Also, the view needs to do part of this anyway for view-internal work like sorting and filtering based on the model and its changes. [1] Interesting links: http://stackoverflow.com/questions/15912999/recycling-views-in-custom-array-adapter-how-exactly-is-it-handled http://www.google.com/events/io/2009/sessions/TurboChargeUiAndroidFast.html ___ gtk-devel-list mailing list gtk-devel-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-devel-list
Re: Model/view ideas for GtkListBox/GtkFlowBox
I definitely second that. Empathy/Contacts can easily have between 1000 to 5000 rows and I've measured that widget creation (and destruction) in GtkListBoxRow is a real bottleneck. It's a bit hidden by folks having even worse performances, though (but it improved recently and I did not re-test). I've opened https://bugzilla.gnome.org/show_bug.cgi?id=710414 for this recently. Another thing I would like to suggest for the model is advanced filtering. I've implemented it on GtkListBox but it could be a model thing instead. Dunno. See https://bugzilla.gnome.org/show_bug.cgi?id=710204 On mer., 2013-10-23 at 11:57 +0200, Alexander Larsson wrote: More and more gnome apps are migrating to GtkListBox rather than GtkTreeView for lists, and we now have GtkFlowBox that replaces GtkIconView. These are nice for smaller lists, but with larger lists they are a bit heavy. We may want to look at optimizing whatever is possible, but at some point it makes sense to have a model/view split that allows us to have large models without having each item in the model be instantiated as a widget, both for performance reasons and ease of use. I've been thinking of ways to do this, and had plans to implement this, but atm I'm busy working on a side project, so I don't have time for this atm. I thought I'd write up a braindump of my ideas so that maybe someone else can look at it, or at least it won't be losts. So, the general idea is that we have a model, and we create and update row widgets from the model based on some kind of template. The new GtkBuilder class templates is an excellent example of how this could work. We then create widgets as needed as they are scrolled into (or near) view. One obvious problem with this is that we don't know the height of the rows until we have widgets for them, so the listbox probably has to be changed to implement GtkScrollable and do scrolling based on row-nr and average height rather than exact offsets. The model itself is a set of GObjects, where the data in the model is stored as GObject properties. This is very flexible, in that we have names (no more column nr shit) for the data that is easy to map to properties (like GBinding, possibly with some transform function) in the row widget template, as well as to generic sorting/filtering functions. Property notification makes incremental updates possible. The GProperty work being done will make it very simple to create such model objects, and make property lookups very efficient. Then we need to add a GObjectSet interface that has signals for when objects are added and removed to the set, which would be used as the model itself, but also as a property type for recursive models (i.e. trees). View updates on a model like this can be pretty efficient. We connect to the added/removed signals on the set and keep track of the items (and per-view info like selection status) in the view in a sorted filtered GSequence, then we connect to notify on all the model elements and whenever we get it we look up the GParamSpec to see how it affects the model (i.e. are we filtering/sorting/showing the changed property). If anything is affected we flag things in the view and request and update on the frame clock, so that we can minimally update the view structure and any visible widgets at most once per frame. I believe we should also have some sort property caches in the view objects. For instance any sort by string we should be monitoring changes to the corresponding property and keep a g_utf8_collate_key() or g_utf8_collate_key_for_filename() key up to date for fast comparisons. That should easily integrate with the update cycle above. We should also allow sorting based on object relationships. For instance, if we had a GObject *parent property that could be used to create a tree view if the view supported specifying that a child should be sorted directly after its parent. You can event do more complex structures like the twitter-style expand in-reply-to/replies before/after a tweet. ___ gtk-devel-list mailing list gtk-devel-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-devel-list ___ gtk-devel-list mailing list gtk-devel-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-devel-list
Re: Model/view ideas for GtkListBox/GtkFlowBox
Shotwell uses a layered model approach for this problem. There's a signalled SourceCollection which contains all the objects known to the application, i.e. every photo in the library. Each page of the application (i.e. Events, Tags, Last Imported, etc.) has a signalled ViewCollection which subscribes to the SourceCollection's signals and populates itself with whatever objects it wants to show. That is, the ViewCollection is where filtering, sorting, even aggregation of multiple SourceCollections (i.e. display photos alongside videos) occurs. The UI layer subscribes to the ViewCollection's signals, populates the screen with widgets, and reacts to signals being fired to update itself. I don't know this is the approach everyone should use, or even if it's appropriate here, but we've had great success with this inside of Shotwell. At one point I thought about adapting and simplifying it into a library that built upon Gee (a signalled collections library) but never got that going. I spoke about this with Lars Uebernickel at Montreal, and would be happy to discuss it further with anyone else interested as well. -- Jim On Thu, Oct 24, 2013 at 8:45 AM, Xavier Claessens xclae...@gmail.com wrote: I definitely second that. Empathy/Contacts can easily have between 1000 to 5000 rows and I've measured that widget creation (and destruction) in GtkListBoxRow is a real bottleneck. It's a bit hidden by folks having even worse performances, though (but it improved recently and I did not re-test). I've opened https://bugzilla.gnome.org/show_bug.cgi?id=710414 for this recently. Another thing I would like to suggest for the model is advanced filtering. I've implemented it on GtkListBox but it could be a model thing instead. Dunno. See https://bugzilla.gnome.org/show_bug.cgi?id=710204 On mer., 2013-10-23 at 11:57 +0200, Alexander Larsson wrote: More and more gnome apps are migrating to GtkListBox rather than GtkTreeView for lists, and we now have GtkFlowBox that replaces GtkIconView. These are nice for smaller lists, but with larger lists they are a bit heavy. We may want to look at optimizing whatever is possible, but at some point it makes sense to have a model/view split that allows us to have large models without having each item in the model be instantiated as a widget, both for performance reasons and ease of use. I've been thinking of ways to do this, and had plans to implement this, but atm I'm busy working on a side project, so I don't have time for this atm. I thought I'd write up a braindump of my ideas so that maybe someone else can look at it, or at least it won't be losts. So, the general idea is that we have a model, and we create and update row widgets from the model based on some kind of template. The new GtkBuilder class templates is an excellent example of how this could work. We then create widgets as needed as they are scrolled into (or near) view. One obvious problem with this is that we don't know the height of the rows until we have widgets for them, so the listbox probably has to be changed to implement GtkScrollable and do scrolling based on row-nr and average height rather than exact offsets. The model itself is a set of GObjects, where the data in the model is stored as GObject properties. This is very flexible, in that we have names (no more column nr shit) for the data that is easy to map to properties (like GBinding, possibly with some transform function) in the row widget template, as well as to generic sorting/filtering functions. Property notification makes incremental updates possible. The GProperty work being done will make it very simple to create such model objects, and make property lookups very efficient. Then we need to add a GObjectSet interface that has signals for when objects are added and removed to the set, which would be used as the model itself, but also as a property type for recursive models (i.e. trees). View updates on a model like this can be pretty efficient. We connect to the added/removed signals on the set and keep track of the items (and per-view info like selection status) in the view in a sorted filtered GSequence, then we connect to notify on all the model elements and whenever we get it we look up the GParamSpec to see how it affects the model (i.e. are we filtering/sorting/showing the changed property). If anything is affected we flag things in the view and request and update on the frame clock, so that we can minimally update the view structure and any visible widgets at most once per frame. I believe we should also have some sort property caches in the view objects. For instance any sort by string we should be monitoring changes to the corresponding property and keep a g_utf8_collate_key() or g_utf8_collate_key_for_filename() key up to date for fast comparisons. That should easily integrate with the update cycle above. We should also allow
Model/view ideas for GtkListBox/GtkFlowBox
More and more gnome apps are migrating to GtkListBox rather than GtkTreeView for lists, and we now have GtkFlowBox that replaces GtkIconView. These are nice for smaller lists, but with larger lists they are a bit heavy. We may want to look at optimizing whatever is possible, but at some point it makes sense to have a model/view split that allows us to have large models without having each item in the model be instantiated as a widget, both for performance reasons and ease of use. I've been thinking of ways to do this, and had plans to implement this, but atm I'm busy working on a side project, so I don't have time for this atm. I thought I'd write up a braindump of my ideas so that maybe someone else can look at it, or at least it won't be losts. So, the general idea is that we have a model, and we create and update row widgets from the model based on some kind of template. The new GtkBuilder class templates is an excellent example of how this could work. We then create widgets as needed as they are scrolled into (or near) view. One obvious problem with this is that we don't know the height of the rows until we have widgets for them, so the listbox probably has to be changed to implement GtkScrollable and do scrolling based on row-nr and average height rather than exact offsets. The model itself is a set of GObjects, where the data in the model is stored as GObject properties. This is very flexible, in that we have names (no more column nr shit) for the data that is easy to map to properties (like GBinding, possibly with some transform function) in the row widget template, as well as to generic sorting/filtering functions. Property notification makes incremental updates possible. The GProperty work being done will make it very simple to create such model objects, and make property lookups very efficient. Then we need to add a GObjectSet interface that has signals for when objects are added and removed to the set, which would be used as the model itself, but also as a property type for recursive models (i.e. trees). View updates on a model like this can be pretty efficient. We connect to the added/removed signals on the set and keep track of the items (and per-view info like selection status) in the view in a sorted filtered GSequence, then we connect to notify on all the model elements and whenever we get it we look up the GParamSpec to see how it affects the model (i.e. are we filtering/sorting/showing the changed property). If anything is affected we flag things in the view and request and update on the frame clock, so that we can minimally update the view structure and any visible widgets at most once per frame. I believe we should also have some sort property caches in the view objects. For instance any sort by string we should be monitoring changes to the corresponding property and keep a g_utf8_collate_key() or g_utf8_collate_key_for_filename() key up to date for fast comparisons. That should easily integrate with the update cycle above. We should also allow sorting based on object relationships. For instance, if we had a GObject *parent property that could be used to create a tree view if the view supported specifying that a child should be sorted directly after its parent. You can event do more complex structures like the twitter-style expand in-reply-to/replies before/after a tweet. ___ gtk-devel-list mailing list gtk-devel-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-devel-list