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 😉

Showing off GtkComboBox rendering

This is the next installment on the subject of GtkCellArea hacking (which I introduced last week).

After a raw struggle with GtkComboBox and some final brain-wracking problem solving I was able to finally deliver a new and improved GtkComboBox with much more flexibility in terms of rendering cells.

Of course you’ve all seen what GtkTreeMenu does last week, so there’s not much left to show… except I did have to do a bit of problem solving to make sure it renders properly when cells are oriented vertically as well, even when their height is not fixed over a collection of GtkTreeModel rows (or, menu items).

The end compromise is that cell alignments (i.e. particularly the alignments of cells relative to the same cells in adjacent rows) can only be done when the collection of rows have a fixed size in the said orientation. If that’s not clear enough, take for example a treeview; in a treeview column every row gets the same width… in this case it’s possible to align cells with the same cell’s position above and below it in adjacent rows. However if the column cells are oriented vertically, space is not reserved vertically to match the space used in rows above and below… that can only be done if the treeview is set to use a fixed row height. (actually, it would be technically possible to do where only the last cell defines the difference in height, but that would be very tricky both for the GtkCellArea to handle as well as for the calling GtkTreeView widget, for reasons I wont get into in this post, in the end I think its a very logical tradeoff).

So, here’s some illustration of the combo box in action, delegating a large portion of the work to GtkTreeMenu.

GtkComboBox using GtkTreeMenu using GtkCellArea

For convenience sake, instead of adding lots of code to testcombo.c I just added a combo-box reflecting the same model and cell area already defined in testtreemenu:

The initial test case seen when starting up ./tests/testtreemenu

Note in this shot that the combo box is taller than the contents but is still at it’s minimum size. This was always a requirement because the combo box should not start changing size depending on what item is selected (of course). However now instead of some jumbled iterative code in gtkcombobox.c, this is achieved by telling the cellview it packs onto itself to “fit-model”. The GtkCellView in turn assumes the job of properly requesting enough space to fit the entire GtkTreeModel for any selection it might receive.

Heres another basic shot with another row selected (with the tallest / longest wrapping text):

Here the first cell has some short text and the last cell wraps to the tallest height.

This row constitutes the tallest size for the combo box and is the reason for requesting so much space. Note that since we’ve set the third cell to “align” with adjacent cells, there is a gap between the second and third cell (Actually if you fire the test up yourself you will find the effect quite nice when using keynav or mouse scroller to navigate through items, since the last cell always stays aligned with previously shown selections).

But the part that hurt my brain the most to get right was…

Vertically stacked Cells

The same combo box with cells stacked vertically

In the case of vertically stacked cells, alignments only work on the GtkCellView that is displayed on top of the combo box. Since the GtkCellView on top needs to request enough space for any row in the model, it can afford to allocate a fixed height to the underlying GtkCellArea which does the rendering (i.e. the height used to display every row of data is a constant height).

However a compromise is made for the menu items, since they naturally use a different height for every menu item the alignments are thrown away and ignored (expand space is also irrelevant here because a menu never receives more space than it requested).

The combo-box's drop-down GtkTreeMenu with vertically stacked cells

As you can see here every individual menu item uses only exactly the height which it needs.

Now, of course this example is only here to show the potential of what can be done with new menus and combo boxes… the above example is not exactly a “nice” menu to render with cells vertically stacked, however vertically stacking menus might still be attractive.

Or better yet, if your really hard-core you could take it to the next level and write a tabular GtkCellArea and use that to render the cells in your combo-boxes and menus (for instance, a thumbnail on the left and then some vertically stacked text/icon on the right might be interesting).

Although at Openismus we were only particularly interested in achieving proper alignments of cells in combo boxes and menus (for the normal case of multiple cells rendered horizontally), GtkCellArea brings alot of new stuff into the game of rendering cells.

Stay tuned for the actual GtkTreeView using GtkCellArea to render its cells 🙂

Update

Murray wanted a normal screen shot displaying how a basic combo-box drop down menu can be properly aligned so I went ahead and added a (simpler) test case to testcombo this time displaying aligned cells in the drop down menu.

This is the smallest size of the testcombo application because of other widgets in the window. In this case both cells are set to "align" and to "expand".
Both cells in the combo box are set to align and expand here.

Here we have a normal combo box displaying more than a single column and the cells are setup to “align” with cells in adjacent rows (note that the “align” child property of cells is meaningless for the first cell in a box, because it has no preceding cells it is always placed at the beginning of the allocation… however this might not be the case for right-to-left text direction, I still need to finalize the details of rtl layouting).

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 !

Late back to work post

Hello again GNOME !

I’ve been back for a few weeks now from taking a month vacation in Mexico with my girlfriend; heres a courtesy picture of us standing on top of the world… or at least on top of a pyramid 🙂

At Teotihuacan pyramids near Mexico City

… and the great news is that I’m continuing my employment with Openismus GmbH indefinitely, working specifically on GTK+ and GNOME.

So let’s recap on what’s been going on in GTK+ height-for-width resizing land since my last post.

Out of the patches that were available on the GTK+ native-layout branch, we landed the work on GtkExpander allowing the expander labels to wrap but still havent finished the work on wrapping text inside treeviews.

Similar to the GtkExpander implementation, we added support for wrapping/ellipsizing text in GtkFrame label widgets (to see this all in action you can try the ./tests/testheightforwidth demo from GTK+ git master at any time, I wont include shots for the GtkFrame here as it works pretty much exactly as the expander does).

So instead of running full till into another revision on the GtkTreeView height-for-width patches I took a different approach and moved right along to implement naturally sizing GtkComboBox (which we landed in GTK+ git master last week).

Some might wonder what is the relation between combo boxes and treeviews. The key point here is that they both end up using GtkCellRenderers to render content; the combo box by way of the GtkCellView widget. Since the cell renderer modifications for height-for-width GTK+ was a huge patch; implementing GtkComboBox first was a good excuse to pull that work in first and hopefully simplify the work that needs to still be done for treeviews.

So without further adieu here are some shots of how the combo box is performing in height-for-width land:

combo boxes demo at the initial size

So far not too much to observe, the above combo box renders the liststore with a wrapping cell renderer, the combo box below renders the same liststore but uses ellipsizing cell renderers instead.

For the wrapping renderer, you need only specify the “wrap-width” which will be used as a minimum width request, if no “max-width-chars” was set on the cell renderer then the renderer will request the fully unwrapped text width as a natural width.

For ellipsizing renderers, you just specify the ellipsize mode and again the renderer will try to request the full text width as a natural size.

Combo boxes demo with the left pane streached wide

In this (above) shot we see the text unwrap fully when the combo is allocated sufficient space to display the underlying cell renders (and similarly the lower combo box need not ellipsize at this size).

Combo box demo with the left pane at minimum width

And finally, at their smallest possible size the cell renderer text wraps up to the minimum width which was specified by the user/programmer via the GtkCellRendererText:wrap-width property.

Well once I got that far I thought it would be enough… until I clicked on a streached out combo box and found that the menu items were still requesting the height for the minimum cell width instead of the height for the menu’s allocated width (imagine a menu with random looking menuitem heights and text that doesnt quite fit the vertical allocation).

So we went on to implementing the natural size allocation in GtkMenu and the coresponding requests in GtkMenuShell:

The drop down menu at a larger than minimum width

Note in this screenshot some menu items wrap while some dont wrap, however they are all allocated the correct height.

Here the combo box drops down its menu at the minimum width

Here at the minimum width all of the items are wrapping and they still get allocated a correct height.

And with combo boxes doing height-for-width requests.. finally we were able to close this nasty bug.

Enjoy ! … and stay tuned for another episode of wrapping/ellipsizing text in treeview widgets 🙂

GTK+ learns height for width episode II

These last weeks I’ve been working on finishing the remaining bits of the height-for-width layout system for GTK+.

The GtkExtendedLayout interface, which has recently been renamed to GtkSizeRequest has been merged into GTK+ recently with only some basic features leveraging the new code (the new GtkSizeRequest interface allows widgets to trade height for width when negotiating their geometry, read this informative post for more details on how this works).

I’m going to be putting a branch together to be reviewed but here’s a sneak preview of what’s been added in height-for-width land.

Wrapping expander labels:

Shows the expander fully unwrapped, note that the focus shows the label is allocated no more than its full natural width

When the expander is allocated something smaller:

Note the extra vertical space is not evenly shared. The expander label is allocated a sufficient height and the remaining space is given to the child.

At it’s smallest size:

All height-for-width widgets will always ensure enough height to fit the minimum width.

Ok ok I know expanders are not that exciting… I’m only posting them cause the screenshots have pretty colours in them…

Naturally sizing Tree Views

Ok this is the juice, as you might guess working anything new into the treeview code is … some kind of nightmare… so first the easy part… here are some ellipsizing cell renderers packed into some columns in a view to show how it naturally sizes:

Note how the width of renderers in the right column is shared according to natural widths

And when you shrink it:

Note that when not all renderers get their natural width, at least smaller widths are prioritized automatically.

Wrapping Cell Renderers in Tree Views

Ok heres the wild stuff, first of all I have to say I’m not very proud of all the cute kittens I had to slay to get this done… that is to say that there is an interesting new problem for scrolled windows that parent height for width widgets that may result in deadly feedback loops that lockup your desktop… thankfully I found a trick to avoid it. It’s not that pretty and in the long term the hack should be replaced… with… well with something generally more kind to your kittens.

All that said… it does work and calculate row-height for width asynchronously in the background and only when columns are set to resize etc etc… hopefully this wont be much of a performance hit for views that handle large datasets.

Except that the columns/renderers are automatically allocated natural widths, nothing is special about this treeview.

The above shows the treeview at its full width; it’s packed in a scrolled window with automatic scrollbars.

When you shrink the view the row heights will be recalculated and redisplayed, the horizontal scrollbar of the scrolled window will only appear if the child view is allocated less than its minimum possible width.

This can be useful where its more important to see the full content of each visible row than it is to see a lot of rows at a time.

This particular view can be resized very slim, just because the minimum width-chars of the renderers are not set and so they are all permitted to get pretty small:

This view's rules permit it to become very skinny - one renderer is set to ellipsize and not wrap.

While I’m happy to have got as far as wrapping text in treeview’s; that was only the hardest challenge of the GtkCellRenderer brand – after this there is still more to be done for GtkIconView, GtkCellView and consequently, GtkComboBox.

And of course, all of this fancy cell renderer stuff is going to have to pass review and all… but we’re getting there !

And I’m proud to say that all these recent developments have been brought to you by Openismus GmbH.

Enjoy !

GTK+ Learns height-for-width geometry

Over the past weeks I’ve been concentrating my full energies on an Openismus funded expedition to finalize the extended layout patches that teach height-for-width geometry management to GTK+.

Its time to show off a little what came out of this effort:

The test/extendedlayoutexample program screenshot cropped by gimp running against the native-layout branch.

No ! not the crazy allocation assertions from hell ! I’m just kidding; I’m not sure if those assertions come from running the Gimp against git master or from running against the native-layout branch. The point of this shot is only to say the stuff is pretty damn stable. I’ve been creating the demos (in this post and my last blog post) using Glade running directly against a modified height-for-width GTK+ core. The above screenshot was taken with the Gimp running against the modified GTK+ and it displayed and behaved fine during the exercise.

So that was my first priority: turn the work into something that we can all use right now, there are some really minor API breaks (also discussed in the announcement) but from what I’ve seen so far you’re favorite application will run just fine against the new GTK+ code (that includes sticky places where the application tries to hack its own height-for-width labels by way of handling “size-request” signals and such).

One of the really difficult parts of getting this working was to make container widgets report sensible width-for-height/height-for-width collectively for their children:

Two wrapping labels in a horizontal box (top left)

The trick is the horizontal box has to compute a collective minimum and natural height for its overall allocated width:

When stretched, the horizontal box reliquishes height by collectively calculating height-for-width of its children.

Trickier still, was to figure out how a height-for-width widget and a width-for-height widget could live together  in the same hierarchy and at least always require enough space in both dimensions (so that we can set reasonable window constraints based on the interface’s minimum size and labels don’t wrap ever out of view unexpectedly).

After a lot of thought and valuable insights shared on irc (Owen, Matthias…), a good nights sleep and then another day hacking we were able to dish out a GTK+ that will also do width-for-height:

An interface that uses width-for-height

And the two possible extremities of the window pane:

Lowest position for the window pane

And:

Highest position of the window pane

All of this is available in the ‘native-layout’. A trimmed down branch has been created for review of the base feature set called ‘native-layout-incubator’ where you can find the above demo ‘extendedlayoutexample’.

Thanks Murray and Openismus for the great experience, and for the great new features for GTK+ !

Glade could use a little help

While the Glade project can seem complex and not everybody writes GObject code in C; there are a few things that could really improve the usability of Glade without applying any real coding skill.

For instance, ever since GtkBuilder was introduced, we are still missing icons for almost all the important and attractive new objects available with GtkBuilder:

The missing icons include things like Size Group, Status Icon, Text Buffer, Text Tag, Entry Buffer, some of the newer Cell Renderers etc. Glade uses these icons every chance it gets to help to give context to the user while editing the interface, like in the Property Editor:

They are also a great indication when viewing the overall project with the Inspector view:

Note that most of the missing icons are not widgets and don’t already have a visual representation. The design needs creativity to create visually familiar patterns based on functionalities of new and interesting objects that are available, like a Size Group or a List Store.

We’ve made requests before but I suppose the interest just wasn’t there.

But really, Glade’s engine is not doing so bad, if it only had a good paint job to match then it wouldn’t appear so much like it just came out of the junkyard.

Note also Glade installs the icons into the theme, which means other development tools can also leverage the icon set that Glade installs.

Please help us with the icons ?

Good afternoon 😉

Extending the layout for GTK+

These weeks, on the generous behalf of Openismus; I’ve been spending my full attentions on teaching GTK+ some new tricks where resizing the interface is involved.

It was a rocky ride of detective work at first; picking up the bit’s and pieces of Mathias Hasselmann’s original work, various other patches from the old branch and reading dozens of email threads and bug reports; all of which make up a web of information that spans over the last 5 years.

This was followed by a session of regression testing; I wanted to have a good grasp of what was working and what was broken in the branch (I also hoped I could kill all the regressions before introducing any features). That went quite well as I have a good grasp on what’s still broken and what features are still missing.

After all that hacking it was about time I owed you all a blog so lets take a look at what the features are and where we stand with them.

What is the Extended/Natural Layout ?

The Extended Layout has been defined as a few things in the past which covered different feature sets. For the purpose of this writeup we intend to achieve the 2 main goals which are:

  • Smartly distributing space to child widgets to display the maximum content of children before adding any extra space.
  • Allowing height-for-width/width-for-height contextual resizing

Some key points about what these features introduce are:

  • In addition to the concept of a widget having a minimum size; a widget can also use up extra space by declaring a “natural size”, this aspect allows for the first feature.
  • Sizes are only ever computed in one dimension at a time, either for the default widths/heights or in context of the size in the opposing dimension. This means a minimum “size” no longer exists as we know it; although it can be emulated by getting the minimum width for the natural height of a widget. This change is also what allows us to negotiate size properly with wrapping labels.

So, people may remember Havoc Pennington’s blog post on text that resizes properly (it was years ago), anyhow; here are some similar visuals on how GTK+ widgets are resizing in the native-layout branch:

Here is a horizontal box with some basic display widgets inside; at the natural size:

And here at a smaller size, note that the larger button and label will ellipsize before the small combo box:

But all the children do ellipsize:

When the layout is larger; the button in the center is sized to its natural size before allocating extra size to the children which asked to “expand”:

Well that’s an idea of what the natural sizes bring to the GTK+ interface, there are of course other widgets that are not yet working with the new layout; I gave cell renderers some attention for combo boxes but they still need work to be usable in treeviews (that’s one of the current regressions actually…).

Height-for-Width

The height for width feature basically allows for the above feature to work the same for container widgets who are interested in allocating decent sizes for children, but allow for more flexible display widgets found deep in the roots of GTK+ widget hierarchies.

Here is a quick example of what wrapping labels can do with height-for-width support, here is a vertical box with some height-for-width aware widgets packed in; along with a static Label widget to fill the extra space:

Here is the same interface slightly wider:

Note how the wrapping labels request less height for every line they unwrap:

Extended Layout features allow labels to unwrap automatically; relinquishing valuable screen realestate to parent containers in the interface. These are the effects of "height-for-width".

Currently I’m also experiencing some difficulty getting height-for-width information to cascade correctly through the ancestry of boxes in GTK+, I tried my best to outline the details in a post I wrote to gtk-devel-list yesterday, of course all feedback welcome there.

For those who will checkout the branch; I will be adding some more test cases next week (in a few days), currently sizegroups are broken after a mega API change; although they we’re broken in some ways to begin with and now I have a good idea of how to fix them; any feedback on the new GtkExtendedLayout/GtkExtendedCell apis would be great if you have time to stop by gtk-devel-list.

Well so far it’s been an honor and pleasure to do this work for Openismus, on a project I already like to participate in (GTK+). The project is an ambitious one surely, lets go do it justice 😉

Update:

Murray suggested I should post some comparison shots; Good idea.

All the previously shown screen shots were taken using the new GTK+ native-layout features.

Here are some comparisons of how the interface reacts with and without the added features. In this first example you can see that with current GTK+; Combo Boxes request widths for ellipsizing Cell Renderers which it shouldn’t. Also you can see that Buttons/Labels will not know how to request more than: “…”:

A Combo Box, Button and Label with Current GTK+

Here we can see that without any explicit width requests, GTK+ with Extended Layout features can spread the ellipsizing Labels properly:

A Combo Box, Button and Label with the Extended Layout

when stretched out, the exact same interface will comparatively look like this:

Stretched Combo Box, Button and Label with Current GTK+

In the case of normal GTK+ the Button in the center never requests a “Natural” size. As it is also not set to expand inside the Horizontal Box; the button stays very small in the center.

Here we can see that even though we never told the button to expand, Extended Layout will help it at least request a “Natural” size:

Natural Size Combo Box, Button and Label with Extended Layout

In the height-for-width case, this is what the stretched UI would look like without the Extended Layout features:

GTK+ does not unwrap the Label naturally to fit the width. With the Extended Layout the Labels will unwrap and relinquish vertical space to its parents.

In this final case its clear that GTK+ doesn’t know how to rewrap the label on its own, some applications these days implement hacks to get this to work by riding the “size-request” and “size-allocate” signals.

One week of Glade brought to you by Openismus GmbH

So its Tuesday afternoon prime time on Planet GNOME, here’s an inside look on whats been going on this week for Glade.

Runtime Warnings

Last week we got the GtkToolPalette integrated into Glade and got Glade vertical again, this week I went on a rampage of hunting down startup warnings, warnings caused by loading Glade files and generally any warnings/assertions that can be fired while manipulating projects in Glade.

That effort left us with a couple interesting side effects:

  • Now we introspect if widget classes are scrollable and we don’t allow adding non-scrollable widgets to scrolled windows
  • Now we update treeviews in the workspace seamlessly when the underlying treestore columns are redefined
  • A nasty data corruption bug occurring when loading liststore data that contains gaps (missing unserializable columns) was also fixed.

Once these items were fixed I was pretty much left on my own to fix some issues of my choosing and improve some things in Glade – awesome – so I decided to prioritize on issues that did not need backing support in GTK+, as there has been lots of development there that we haven’t been able to keep up with.

The signal editor

The first issue I decided to tackle was the ‘swapped’ attribute for signal connections defined in Glade – that’s right, until this week Glade was never able to read/write this attribute. To illustrate the importance this has in Glade I want to show you a before shot:

The "before" signal editor

Ok so the editor looks alright at first sight but there are some confusing aspects, for instance the “user data” in the signal can only ever refer to an object in the Glade file – no mention of that is made. Furthermore when one goes through the whole google research to find that the user data is supposed to be an object, they immediately get stumped by GTK+ swapping the object at connection time for seemingly no desired reason – very, very confusing.

Something to keep in mind as well is that signal definitions in Glade have generally been unused. Mostly because of an inability to pass data external to the Glade file as user data to callbacks. Generally people use a parent or controller object as a module pointer to be passed to all callbacks; this technical problem with signals has really been a non-issue ever since the introduction of the gtk_builder_connect_signals() api which takes a default pointer argument to pass to signals for just this purpose.

Well, moving on – after adding the “swapped” attribute to the editor I was quite unable to tear myself away from the editor, it hasn’t seen an update in many years and has been lagging behind the rest of the UI, so without further adieu I present to you:

The "after" signal editor

Now the signal editor is doing a bit better, I didn’t have the time for a full scale rewrite using GtkBuilder for its store, view and renderers as I have been hoping to do for a while but – at least she learned some tricks from her big brother the property editor:

  • Now the signal editor lets you view the documentation for a signal at the click of an icon (Thank you Frédéric Péters for getting DevHelp page contextual searches working again ! sorry for waiting 6 months+ to notice !).
  • The editor does not let you type text for connected objects for user data, instead you get an object selection dialog
  • Now versioning support icons/tooltips show up for signals that are introduced in the future of your project’s target version.
  • Tooltips explaining about the signal editor fields show up on the treeview column headers
  • Signal, Handler and Object fields in the editor are user resizable but allocate a reasonable size.

Actions

So, with a rocking new signal editor in master I still had some hours to kill, I went on to tackle the relationships of GtkActionGroup <–> GtkAction in Glade – I wasn’t able to pull out a customized editor for this yet, but I imagine it will look like the menu or toolbar editor and allow hierarchical configuration of the project’s action groups/actions (currently Glade will only let you add an action to a group via the Inspector widget: Right click -> Add widget here / Paste commands will do the trick).

Glade now also lets you set an accelerator key for a GtkAction, the property only applies to actions which are placed into a group, ideally this will be the ultimate place to define accelerator keys using Glade so that they integrate well with menus and toolbars.

Thank you

A big thankyou to Openismus GmbH who sponsored the project and made all of these unlikely advancements in Glade actually happen.

I’ll be turning my attention away from Glade this week to look at some other serious GNOME issues, keep up with my blog for regular updates on this Openismus sponsored effort.