Introducing GtkCellArea

Good morning, afternoon, night Planet… I haven’t been regularly blogging and so sorry to start off with a weekend post but surely I will have some more posts following in the week.

This post is something of a follow up on this post, the height-for-width GtkTreeViews which never landed in GTK+ last summer are now on the verge of landing… after a lot of work internally refactoring GTK+ cell layouting algorithms, here’s a peek at what we’ve been doing at Openismus for GTK+.

What is GtkCellArea ?

You can read a detailed thread on gtk-devel-list where I originally proposed the new API, however I’ll include a brief overview here in this post.

Essentially GtkCellArea is an abstract container class for rendering GtkCellRenderers and is meant to abstract the layouting of cells in an area to be rendered onto a widget… one of the goals here is to completely centralize the cell layouting code in GTK+ so that widgets like GtkTreeView and GtkIconView don’t have to do the work of rendering individual cells manually. Another main goal is to provide an abstract coding interface to an area of cells; this opens up the road for cells to be rendered in different ways than your typical horizontally oriented list of cells that GtkTreeViewColumn currently does.

GtkCellArea “stuff” is composed of the following classes:

  • GtkCellArea: an abstract class to render cells in an area
  • GtkCellAreaContext: an abstract class to hold geometrical context of size requests and allocations over a series of GtkTreeModel rows.
  • GtkCellAreaBox: the first concrete GtkCellArea which is an orientable ‘box’, when oriented horizontally it behaves like a GtkTreeViewColumn
  • GtkCellAreaBoxContext: the GtkCellAreaContext created and used by GtkCellAreaBox.

Some things the GtkCellArea currently does:

  • Implements packing “cell properties” for GtkCellRenderers. GtkCellArea subclasses can declare packing properties to define how a cell is to be layed out in the area (this is analogous to how GtkContainer subclasses define child placement with its packing properties).
  • Provides GtkBuilder support to setup packing “cell properties” when <cell-packing> tags are specified in the children of a GtkCellLayout widget (also in the same way GtkContainer does it).
  • Provides a focus navigation interface analogous to GtkWidgetClass->focus so that cell area can navigate focus in a semantically similar way that GtkWidget does (this of course simplifies the work for widgets that render cells when navigating focus internally from cell to cell and area to area).
  • Provides cell-editing logic to bookkeep what is the currently edited cell and editing widget and a method to cancel the current edit, essentially a widget that renders cells only has to handle the GtkCellArea::add_editable/GtkCellArea::remove_editable signals.
  • Provides a method to handle events, currently this is only used to activate/start editing cells inside an area where only the area itself can know where the cell is positioned, but potentially it can provide a way for GtkCellArea to also render widgets into a treeview or another cell layouting widget (provided that the widget sends all the important events to the cell area in focus or at the pointer position).

So, enough of the terse technical jargon, lets move on to some snapshots of the GtkCellArea in action.

GtkCellArea in action

The following screen shots were taken from the treeview-refactor branch which includes the initial GtkCellArea work. The initial test case for GtkCellArea is tests/testcellarea; it’s compiled with a ‘mock treeview’ called cellareascaffold.c, without being scrollable or doing any of the fancy treeview features such as rubberband selction or drag-n-drop, I’m happy to say it gets the treeview basics done in less than 1500 lines of code.

Here is the first shot of what you see when you fire up testcellarea:

The "testcellarea" test at its smallest width

An interesting thing to note here is that the second GtkCellRendererPixbuf cell  (the icons) is not “aligned” while the third cell with the wrapping text is aligned, alignments with the GtkCellAreaBox are optional and implemented as a packing property of the area.

As the CellAreaScaffold testing widget does not scroll, I’ve just packed it into a GtkFrame to show how vertical space can be relinquished when given a wider allocation:

Stretched test case, note the entire widget does height-for-width geometry by calculating the height-for-width of the are for every row.

The GtkCellArea of course also properly handles configuration of expanding cells (also via a child “cell packing” property):

In this test case the second cell "expands", however the test program allows configuration of the cell properties so you can see what are the effects of aligning and expanding individual cells for yourself.

And of course, the alignment of every cell can also be configured:

In this case we've "aligned" both the second and third cells while the second cell is still receiving some expand space.

The GtkCellAreaBox is also GtkOrientable:

This case has the box area vertically oriented, the scaffold currently draws them in a row from left to right in this case but that is just a detail. Whether the area is oriented vertically or horizontally the layouting widget can decide to render the rows however it chooses.

Moving on to another test case, the GtkCellArea also implements internal keyboard navigation and the painting of focus on cells:

The editable text renderer now has focus, note that more than one cell in a given area can receive focus.

There is also the concept of “focus siblings” implemented in GtkCellArea, this lets you place focus on a cell’s neighbor while a given cell is in focus:

The toggle renderer has focus all alone

This lets you decide which cells in the area should also have focus when a given cell has focus… siblings of focusable cells also cause the focusable cell to activate when they are “clicked”:

Now we can clearly see that the static text beside the toggle renderer is associated with the toggle renderer when focus is painted.

The GtkCellArea as I mentioned above also makes cell editing much easier, the CellAreaScaffold only has to recieve the ::add-editable and ::remove-editable signals, when ::add-editable is invoked, it comes with a brand new editable widget and a GdkRectangle dictating the area which the editable widget should be placed, the editable widget needs to be a GtkContainer of sorts and just add the editable widget in that size and position at the right time and then remove it when ::remove-editable is invoked.

Shows a GtkCellRendererText being edited by the CellAreaScaffold test

That pretty much concludes chapter one in the GtkCellArea saga, Kristian Rietveld, our GtkTreeView master has been working with me in parallel over the past weeks to get GtkCellArea integrated as the rendering delegate of GtkTreeViewColumn.

But that’s not all ! while I’ve been waiting on GtkTreeView integration I’ve got a head start on refactoring GtkComboBox to render it’s menus with the new framework, so far I’ve come up with GtkTreeMenu which will reduce the huge mess of a codebase that is GtkComboBox considerably… and also open up many new avenues by making it possible to generate menus from GtkTreeModel. The GtkTreeMenu code is available in the combo-refactor branch.

GtkTreeMenu Basics

GtkTreeMenu starts off with all of the rich benefits of GtkCellArea, areas can be rendered with various cells, various alignments and expand configurations, in various orientations etc. Here are some shots from tests/testtreemenu from the afore mentioned combo-refactor branch:

GtkTreeMenu rendering a similar model with the second cell unaligned and the third cell aligned.

Now also we can align all the cells:

GtkTreeMenu with all cells aligned.

In the above screen shot we can observe the usefulness of GtkCellAreaContext, note here that every GtkTreeMenu (each submenu is itself a GtkTreeMenu) share the same GtkCellArea, however they each have a private GtkCellAreaContext which is used to request / align / allocate the size to use when rendering the area. In this way leafs of each tree get the same cell alignments however the overall width of each submenu can be different (cells in submenus are aligned with cells in the same submenu but not aligned with cells in parent menus).

Another detail of GtkTreeMenu is that it uses a GtkTreeMenuHeaderFunc to decide whether it should include a menu header for the parent item which had a submenu. This is important for combo-boxes since combo boxes need to be able to actually select every row in the GtkTreeModel including rows which have children:

This shot shows a combo-box style menu where every submenu includes a selectable "header" item to allow selection of the GtkTreeModel row that has children.

Pie in the Sky

Whether or not GtkTreeMenu should be an exposed class or only an internal detail of implementing GtkComboBox is a question that we’ve been tossing around on irc these past days. Interestingly, with some more work in this area GtkMenuBar and GtkToolBar could also be rendered using cellviews and GtkCellArea… in other words the main application toolbar or menubar could be simply built off of a GtkTreeModel. Even fancier still, would be to have GtkApplication actually implement GtkTreeModel for its menu and toolbar actions and just build the application menus automagically, possibly easing the integration of menubars on OSX and making things make alot more sense in general. Ofcourse this kind of approach has a lot of details that need consideration, menu accelerator key and the like are one of them… but it does sound like a tempting approach to just get rid of most of the hand-built GtkMenuItem API (and also finally drop GtkUIManager in favor of using GtkTreeMenus…).

Those are just some ideas… that might inspire one of you hackers… to come and make GTK+ 3.0 more rocking than ever 🙂

Stay tuned for more news on GtkTreeView refactoring !

15 thoughts on “Introducing GtkCellArea”

  1. Great work. I look forward to more usable multi-column GtkComboBoxes.

    Isn’t GtkCellAreaClass a bad class name. Isn’t *Class reserved for the struct that holds the vfuncs? Doesn’t that mean that you have a GtkCellAreaClassClass or GtkCellAreaIface?

    Also, I guess that as much as possible of this big new API should be kept private (no installed headers) for a while, so we can fix it if necessary even after a stable release.

  2. Yes the type is called GtkCellArea, not GtkCellAreaClass.

    Sorry for the place I said GtkCellAreaClass I’ll edit that out (was just me casually saying something like GtkWidgetClass and expecting people to understand that it means the class which is GtkWidget).

    As for public release of headers, I do expect to expose GtkCellArea/GtkCellAreaContext and GtkCellAreaBox class headers, but not GtkCellAreaBoxContext as that’s an implementation detail of GtkCellAreaBox.

    But I havent written all the extensive gtk-doc statements yet as it’s true the api is a moving target, however I dont expect to see much changing from this point on. I do expect to slightly change the GtkCellAreaContext apis (or rather, unify some apis, remove some apis).

    Once we finish with GtkTreeViewColumn/GtkComboBox and GtkIconView, it should be safe to say the CellArea apis will be stable.

  3. Interesting approach – building menus from GtkTreeModel. Could be useful as public API. Definitly will be useful to drop lots of code from Gtk.

    Still I wonder what’s the issue with GtkUIManager? IMHO it has the very advantage of being an API optimized for some certain use, which makes it a somewhat good API.

    Im stark contrast to GtkTreeModel, which is very flexible but those days mixes serveral different interfaces, which actually makes it a quite horrible API. I’d very much prefer GtkTreeModel being replaced by a proper collection framework. No, the current collections on glib are no such thing.

  4. The tree menus are looking good! So I guess the GtkComboBox refactoring is also on the right track. I was actually about to get started with some more hacking on the GtkTreeViewColumn integration the minute I read your blog post.

    While it does sound interesting to render menus from a GtkTreeModel, I am in agreement with Mathias that GtkTreeModel might not be the right API for this. GtkTreeModel was initially designed just for GtkTreeView, but is now also being used for GtkIconView (more or less a proper fit) and GtkComboBox (I think this might already be on the border of being a good use of GtkTreeModel). GtkTreeModel was certainly not designed to be a generic container for all kinds of things in GTK+ — so I think we would really want to have a generic model interface and perhaps a few specializations (or additional interfaces). The generic model could of course be based on some kind of collection framework, like Mathias also mentions.

  5. The only thing missing is a standard for being able to specify the contents of the TreeModel with some xml-like syntax…

  6. You are aware that I consider a treeview that does not handle a roughly infinite number of rows (think a database viewer on Google’s “webpage” table) in animatable time (read: <10ms per frame) broken? Yes, I'm aware the current treeview is very broken (already starts choking on multiple thousands of text cells), but please keep that in mind while doing all this cell layout stuff.

    Cell renderers are used to display _lots_ of data. If you don't want that, using a GtkTable and normal widgets works fine.

  7. @Mathias, About UIManager in particular… on it’s own it’s not such a horrible api, actually it’s quite nice. However it’s been my personal goal to kill the UIManager ever since GtkBuilder was introduced to GTK+. Basically the only problem I really have with UIManager is that it treats menus and toolbars as a special case and repeats the work that is quite nicely done by GtkBuilder.

    I’ve went to great lengths to try and drop the UIManager already, first I went and cleaned up the menu code/properties so that GtkMenu/GtkMenuItem was properly buildable from GtkBuilder and we could restore our beloved menu editor in Glade, then I went ahead and created GtkActivatableIface which allows us to associate actions to widgets in Glade as well… the only thing that we still don’t have with GtkBuilder that is available with UIManager is the “merge ui” feature, which is kindof corner case but many apps have been using it when loading/unloading plugins.

    While implementing application menus based off of treemodels is still just an idea… I think that the merge feature might make more sense at the datamodel level than at the GtkBuilder level. In other words maybe merging actions in and out of a GtkApplication implementing the data model being reflected in menu items… makes more sense than doing the merging directly at the GtkBuilder/GtkWidget level.

    In any case, my only real beef with GtkUIManager is that it exists inside GTK+ alongside GtkBuilder.

    Also agree that GtkTreeModel is an awkward interface (apps usually have their own way of representing/fetching their data internally, the right way to do things seems obviously not to duplicate the data into a GtkTreeStore for the purpose of showing it in a treeview but to implement the data model interface in the application and have the view fetch the data directly on demand… the wide usage of GtkTreeStore in applications is probably proof of where the GtkTreeModel interface fails).

  8. @Luca Bruno, GtkCellView is a widget that renders a single row of data. GtkCellArea[Context] is a framework for rendering cells onto a widget and in this way can be used in GtkIconView, GtkTreeModel and any widget that wants to render multiple rows of data.

    FWIW, I refactored the GtkCellView inside the ‘combo-refactor’ branch and in doing so removed most of it’s internal code, requesting size and rendering is now just a matter of delegating the work to a GtkCellArea object (which is not a widget).

  9. @dbdoskey: Well the GtkTreeModel is really just an interface, but GtkListStore does support GtkBuilder xml to populate with data.

    Extending that to GtkTreeStore is not hard work, you generally would only have to reuse code from GtkListStore and let
    tags have child tags and there you have it (feel free to send patches for that).

    However it strays from normal usage of the TreeModel, i.e. TreeModel is generally used to adapt some data that is already loaded in some complex application and then interact with it in a view (perhaps a log parser/viewer, DB application or even Glade itself implements the TreeModel to show the widget hierarchy that it loads by itself from the builder xml)… in other words, the data you want in a TreeModel is almost always not something static that you want defined in an constant xml file, the good reason why GtkListStore supports this is that it’s needed for populating things like combo-boxes which usually do have a constant and limited amount of options specified by the programmer.

  10. Benjamin Otte: Yes of course. And I’ve done my best to keep rendering of staticly aligned horizontal rows at least as snappy at render time as they would have been with previous GtkTreeViewColumn.

    However some more dynamic content layouting comes at a cost of render time computing, for instance if every cell in the area is configured to be “aligned” with adjacent rows… then once the GtkTreeView gets an allocated width, the width of every cell in the column is predetermined. However if for instance the second cell out of 3 cells in a column is not configured to be aligned with cells in adjacent rows, this comes at the cost of calculating the size of the first cell for every visible row at render time. However I don’t think this is really the bottle neck (Kris could tell more as he has more experience dealing with treeview in particular), I think that calculating the size of only the visible cells at render time while already knowing the width and height of every row should not hinder rendering so much. Rather the tricky part of height for width is about properly dropping off into the background to recalculate all the row heights when the allocated width changes.. for the invisible rows.

    Of course, the real time it takes to render the treeview is still mostly left the the programmer, i.e. cells with automatically wrapping text cost cpu time, an icon that follows variable length text and is not aligned costs cpu time, using the treeview column GROW_ONLY option saves you cpu time etc.

  11. Hello just thought i would tell you something.. This is twice now i’ve landed on your blog in the last 2 weeks looking for totally unrelated things. Spooky or what?

  12. That is some inspirational stuff. Never knew that opinions could be this varied. Thanks for all the enthusiasm to offer such helpful information here.

Leave a Reply

Your email address will not be published. Required fields are marked *