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 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 !