Bonus Icon View refactor

This is one of the final installments on the GtkCellArea TreeView refactoring saga, we’re having a GTK+ team on Tuesday where we’ll discuss this and other various things and finally, land the beast that is GtkCellArea.

Actually as a passing note, the GTK+ team meeting this week is potentially the last one we’re going to have before freezing GTK+ 3.0 apis. If there are glitches, problems, polishing work for GTK+ that you know is important and requires breaking API/ABI, it would be a good idea to present your issues at this meeting.

This is a bit of a surprise post I was not planning on writing but I was surprised by the success of the icon view refactoring work so I thought it would be a good idea to share this here.

Bonus GtkIconView Refactor

I’m pleased to say that the refactored GtkIconView actually works much better than the old icon view, all in ~1700 less lines of code.

A small example of this (without showing off configurable cell alignments and the potential of adding some custom GtkCellArea to your icon view to lay cells out in more custom ways) is already viewable in the tests/testiconview test.

It seems that the former icon view did not handle horizontal layout of cells very well:

This shot shows the GtkIconView with a horizontal "item orientation" before refactoring.

As you can see the cells in this icon view are not aligned, the effect is “yuck”. However this was not noticeable when using a typical GtkIconView with only a constant size pixbuf on the left and variable length text on the right, the “yuck” starts to show through when using variably sized cell renderers on the left followed by more, unaligned renderers.

The new Icon View test in the same configuration:

This shot shows the same icon view test configured horizontally using the new refactored code using GtkCellArea.

Now all cells are correctly aligned horizontally no matter the size of leading cells.

You’ll have to just trust and forgive me for “Icon 5” and “Icon 6”, it actually does not appear that way. Only when using the screenshot tool and releasing the selected area it causes a rerender of the GtkIconView… it seems the screen capture is performed before the widget has time to finish re-rendering itself.

And that’s not all

The GtkIconView still doesn’t handle progressive calculation of rows like GtkTreeView does (it calculates the sizes of all icons in one go, sorry this refactor did not change that, we still have to take care of that).

However I was curious about the performance difference so I inserted a GTimer into the GtkIconView layouting procedure, which was pretty easy to analyze (since it does everything in one pass, it was pretty easy to compare).

Before refactoring (that is, requesting the size of each GtkCellRenderer individually and doing some complex calculations about where each renderer is placed inside the “icon” or “item”) I got these results:

  • After adding 10,000 items to the same test (that’s what the “Add Many” button does), the former icon view took 6.225222 seconds to initially layout the newly added items.
  • With the new icon view (after refactoring), adding the same 10,000 rows took 3.492693 seconds.

Once the sizes are initially requested for newly added rows processing is a bit cheaper, however the whole icon view needs to be relayed out every time it gets a new allocation.

With the old code, in my console after resizing the window pane (with > 10,000 items) I got:

relayout took 3.142941 seconds
relayout took 3.150152 seconds
relayout took 3.149025 seconds

With the new code for the same items I got:

relayout took 1.252735 seconds
relayout took 1.339084 seconds
relayout took 1.313939 seconds
relayout took 1.287726 seconds

So in conclusion, not only does the new icon view render cells with better alignments, not only does it do so in 1700 less lines of code… it also calculates the cell positions and lays out the icons in literally half the time it took with the old icon view code.

The icon view refactor work is now ready after ~2 days of work and available on the icon-view-refactor branch.

Enjoy !

GtkCellArea continued…

This is another installment on the GtkTreeView refactoring saga. We’ve come a really long way with this and have the first phase of GtkTreeView rework almost 100% complete.

I’ve been working around the clock with our treeview master Kristian Rietveld, who worked out the initial integration of GtkCellArea into GtkTreeViewColumn and has been reviewing my patches against the treeview code every chance he gets… I know it’s been hard to keep up between meetings and other obligations but really, thanks a lot for your help on this Kris !

Some of the good news is:

  • GtkCellArea as an abstract class opens up the road for eventually more implementations, for instance tabular cell areas or areas that embed widgets can be developed and then used directly by GtkIconView, GtkTreeView, GtkComboBox classes without any need to change code in those classes.
  • GtkCellLayout widgets only need to interface with a single GtkCellArea instead of manage a list of cells, GtkCellLayout only requires that a layouting widget implement the GtkCellLayoutIface->get_area() virtual method and then does everything under the hood on the layouting widget’s behalf (this helps to remove lots of code from lots of classes).
  • GtkCellArea provides “cell properties” to define the relationship of GtkCellRenderers inside their area. Using this generic interface (just like GtkContainer “child properties”) GtkCellLayout’s GtkBuildable implementation implements a <cell-packing> custom tag and allows cell properties to be defined from the GtkBuilder UI format. (something we’ve been missing for a long time is a way to specify which cells “expand” in a GtkTreeViewColumn from GtkBuilder format).
  • Since GtkCellArea provides a generic way for handling events, and since recent developments of GtkWidget (i.e. widgets can be “drawn” and can be rendered ofscreen and such), its possible to implement a GtkCellArea subclass that contains and renders widgets (which can even handle events).
  • Also, GtkCellArea handles the driving of focus from renderer to renderer inside an area (as well as the painting of focus on the renderers), further reducing the workload of cell layouting widgets.
  • Finally, GtkTreeViewColumn has been reworked to replace it’s management of GtkCellRenderers with a single abstract GtkCellArea (not to mention, GtkTreeViewColumn struct members are now safely in a private data structure and GtkTreeView and Column code have a much cleaner “split”).

Some of the bad news is:

  • GtkCellArea replicates lots of api. For instance “cell properties” would really be GContainer child properties if we were to have a GContainer interface, “focus navigation” could really be implemented as a GtkFocusable interface that might not be a GtkWidget and finally even event handling could be implemented as a GtkEventable interface of sorts. However, after doing all of this and actually getting it to work, I dont think this api replication is such a bad deal. A GContainer interface providing child packing properties and object hierarchy outside of the GtkWidget hierarchy is something I’ve wanted for a long time however it’s just not exactly what I’ve been working on these days.
  • Another disappointment is that GtkTreeView has not been fully reworked to harness the power of the new height-for-width apis yet. We preferred a safer and more conservative development procedure for GtkTreeView: dump all the cell renderer man-handling in favor of an abstract GtkCellArea first and then leverage the height-for-width apis afterwards once we know everything is working.

Ideally I wanted GtkTreeView to actually calculate and allocate rows “height-for-width” with wrapping text and the works, all in one go but I also think it’s wise to do this stuff in comprehensible iterations… at least the framework is here and we don’t need to break any more api/abi at this point.

However, that being said, here’s a peek at some of the new things that are already possible with the new and improved GtkTreeView code without adding any fancy GtkCellArea subclasses or actually doing the height for width geometry.

Configurable Cell Alignments:

To show off cell alignments (and test some inner workings) I’ve updated a test that we already have in gtk+, here’s a look at the good old ./tests/testtreeedit in tact and as we are used to seeing it:

testtreeedit updated with some alignment controls, still appearing in the way we are used to seeing it.

Ok nothing new here, moving along, for completeness sake (and just to express what exactly is “configurable” in terms of cell alignments), let’s look at an ugly configuration that no-one is likely to use:

The "ugly" configuration here shows the same test case with the 3rd cell "unaligned", everything looks jumbled up and it's probably desirable to align the 3rd cell in this case.

Well, that’s an ugly configuration sure, but sometimes unaligned cells can be useful.

Here’s an example of how an unaligned cell can be more useful:

This shot shows the configuration with the fourth (icon) cell "unaligned".

Ahh this is much better, I’ve always been annoyed at the alignment space between a variable length text renderer and an icon that logically belongs with the text. In this case we can have an icon that is “related” to the variable length text placed nicely to the right of it without any undesired space.

Vertically Oriented GtkCellAreaBox:

Here we see a GtkTreeViewColumn on the left using the normal horizontal orientation along side a GtkTreeViewColumn on the right using a vertically oriented GtkCellAreaBox to render the cells.

That’s right, vertically stacked cells inside a GtkTreeViewColumn was not possible before this. If you’ve used the popular bit torrent client “transmission”, you will find that they’ve achieved a similar look to the above image by way of implementing a custom cell renderer for their needs. With GtkCellAreaBox we should be able to cover most any desirable layout of cells inside an area without any custom code needed (with a GtkCellAreaTable, we can probably cover any conceivable layout that the user might desire without the need for any custom code).

Documentation:

Now that I’m tidying up and trying to close all of this work I’ve been doing over the past 2 months I’m doing the polish work and writing loads of documentation. The bulk of the documentation is not pertinent to users as it tries to discuss how GtkCellLayout widgets actually work (unless GTK+ users are into writing custom treeviews and the like, of course). Nevertheless having the documentation is probably a good step to help us keep track of how things are done inside GTK+.

  • Documentation for the GtkCellArea itself (the bulk of the documentation).
  • Documentation for GtkCellAreaContext (a context to store geometrical details for a collection of rows).
  • Documentation for GtkCellAreaBox (the first cell area implementation, an orientable box area).
  • Documentation for the GtkTreeMenu (GtkComboBox now delegates it’s menu related work to this new class, which I showed off already in previous posts).

Since the deadline for landing these new apis is getting dangerously close, I hope to land them very soon and then move on to giving GtkTreeView (and GtkIconView) real height-for-width capabilities afterwards, as this part wont need to entail any API breakage.

Coming up up next: Episode “lets land this code” and then the season finale, Episode “Height for width icon views and treeviews”.

Stay tuned 😉