Favorite Bug of the Day

As we rapidly approach our final 1.0 release at the end of this week, Gabriel and I have been bug fixing machines since RC1.

Today though, the following issue wins first prize in the all-time favorite-of-the-day category:

As a result, I did some digging, and in turn filed a downstream bug.

I guess it’s the price we pay for being second class to our toolkit of choice… almost a year later.

UPDATE: After thinking about this a little longer, what I find really interesting here is that at the engine level, Clearlooks seems to have cleaned up a bit and possibly supports standardized hints, something I suggested as a possible stop-gap solution in my original rant from last year, linked above.

This means much of the fundamental problem is seemingly deferred now to GTK RC, which at least allows for hackable workarounds in non-stock GTK widgets. I’ll investigate setting the following directly on our widget:

engine "clearlooks" {
    hint = "treeview"
}

At the end of the day though, GTK is way overdue for some serious overhaul.

Banshee 1.0 Alpha 1

It is my immense pleasure to announce the first preview release of the next generation of Banshee. This Banshee 1.0 Alpha 1 release is a culmination of so much work by so many awesome contributors.

The Banshee logo

Now, first things first. This is going to be a long post, so let me link to the crucial bits first for the lazy enthusiasts out there:

Kudos

Before I dive into the details of the release I would like to highlight few people in particular who have really spent a lot of their own time molding Banshee recently.

  • Scott Peterson – Scott has been a code and ideas machine. He’s committed huge amounts of work to many areas of the new codebase, all while going to school full time as a drama major at NYU, and working on a number of other related projects we’ll eventually sweep up into Banshee.

  • Alexander Hixon – Alex sort of came out of nowhere a couple of months ago and started hacking on the new Banshee by reviving the old Equalizer patch. Alex is also responsible for making Audio Scrobbling work again.

  • Will Farrington – Will has been hanging out in our community for a while, but a couple of months ago he started submitting patches and getting familiar with the codebase. He’s new to C# and some of the technical concepts around GTK/GNOME/Mono, but he’s been learning quickly and making valued contributions.

  • Gabriel Burt – Having Gabriel on the Desktop Team at Novell working on Banshee with me has been fantastic. We’ve made so much progress in so little time. From Last.fm to our new database and Xesam/Query layers, Gabriel is at the heart of it all.

A Short History

This is the first release that shows off the hard work we’ve done on rewriting the core of Banshee. There were a number of critical flaws in previous releases due primarily to the fact that writing custom data models for the GtkTreeView was not possible until very recently in Gtk#.

We took some much needed time to redesign the database layer of Banshee to be able to deliver powerful model/query/cache level features and provide a framework to build on for years to come.

I decided to ditch the GtkTreeView and it has paid off. On top of this model sits a slick new list view rendered using Cairo. We control 100% of the drawing, so we can take this thing anywhere we want in the future – things you can only dream of with the GtkTreeView. You’ll already notice some nice GUI “bling” when using the view – try reordering columns.

With all of these core architecture changes, what we have now is a truly flexible framework for developing our prized Banshee. Here’s a somewhat dated diagram of how the different components all fit together.

That said, we still have a ton of work to do. This release does not have feature parity with previous releases. We’ve still got some more core changes to make (namely, finishing the new hardware layer) and a number of plugin features to port to the new core. See the release notes for details on what features are not yet available in this release.

Screenshot. Just one.

Banshee 1.0 Alpha 1

New Features

Just a quick overview of new features. You should really read the release notes after this.

  • Artist/Album Browser – Yes. Finally. Probably the most requested feature over the past three years has been the ability to filter a source by Artist and Album selections. The album view features glorious cover art previews, of course.

  • Listen with Peace of Mind – Now that Banshee is built on a solid data model, we can drive playback independent of what source you have in view. This means you can play a song in one source and switch to another source without having the playback switch to the new source once the song you started playing finishes. Playback continues from whatever source you start playing from.

  • The Play Queue – Really this is just a small UI layer on top of the dedicated source playback mentioned in the previous bullet. But it’s pretty nice. If the queue is populated, it forces itself to always be the dedicated playback source. Once an item plays from the queue, it is removed. When the queue is empty, playback resumes from your library.

  • Last.fm Integration – Last.fm is everywhere in Banshee, and we’re not done by a long shot. The Last.fm radio extension will change the way you listen to music. Try it now.

  • Software Equalizer – At least it’s entertaining to play with, though it’s hard for me to be a real fan of software eq. I’m told though that it can help a lot when you’re stuck with a less than ideal sound system.

Performance

The new Banshee has loads of impressive performance improvements that really should be the subject of a separate detailed writing. With that in mind, I’ll just touch on each point.

  • Faster Startup – For me, on average it takes 1.5 seconds to fully “boot” Banshee. The key here is that startup time is no longer a function of the size of your collection. While there are still many things to optimize (just connecting to DBus appears to take about 1/5 of a second), this time is impressive compared to previous Banshee releases (startup time was proportional to the size of your library).

    For curious users, starting Banshee from the command line with the --debug argument will print a summary of exactly how the startup time breaks down. This will help us pinpoint services to improve later, leading to even faster start up.

  • Decreased Memory Footprint – Again, regardless of the size of your collection, Banshee should have a relatively constant memory footprint that is much reduced from what you might be used to with previous Banshee releases. I’ll talk numbers in a separate post.

Try it now!

One huge thing to note about this release, and all releases to follow – it can be installed and used in parallel with previous Banshee releases. This means you can try the new stuff out without ditching the old! So there should be nothing stopping you from trying it!

Your existing Banshee library will first be copied and then migrated to the new database format. While it is not backwards compatible, the new releases will not mutate any data used by previous Banshee releases.

It’s safe to package, install, and use side-by-side with previous releases. I will be submitting the new release to openSUSE 11 tomorrow and we may ship with both package sets, depending on when we actually can make the final 1.0 feature parity release. I say this to emphasize it’s safe to use… now.

More to come

We’ve got so much more planned. Remember, this is just the first alpha release leading up to the 1.0 release. What will that be? As soon as we reach feature parity with our current stable series – that’s when – and it’s not too far off either. In the mean time we’ll also be adding new features, so stay tuned, and try the releases.

UPDATE: I forgot to mention some resources on the Banshee Wiki that might be useful for those who want to dive in and try this preview release from source tarball or trunk. Quite important, these links!

Digg It

Suboptimal Theming in GTK

I am a fan of custom widgets. I write a lot of them. Most are not over the top, or even noticeable, but are useful. In fact, most of my widgets I more often call “composite” widgets instead of “custom.” That is, a widget that does some common things to a group of child widgets. Not much involved, but reusable and useful in some context. Gtk# makes this kind of widget writing actually a simple and natural thing to do, unlike C, where you start your work by despising the task at hand. Ahhh – the usefulness of a good binding.

However, I have also written a number of full blown custom widgets, wherein I handle all input and drawing. It’s this kind of custom widget I’d like to talk about today. Actually, let’s just skip to the drawing part!

Ideally, when a custom widget needs to draw some control part, the first thing the author should do to accomplish this is look to see if there is some style that can be reused in GTK. Adhering to this guideline means the custom widget should look like the rest of GTK. It’s good for integration on a number of levels. It’s at this level that the problem arises: in many cases the theme engine will completely ignore the draw request!

In GTK there are a number of theme drawing primitives: hline, vline, shadow, arrow, box, check, focus, tab – to name a few. It’s then up to the theme engine to actually draw the requested style. A “detail” can be passed to each of the drawing functions that the engine can use as a hint. I think these details are somewhat standard, but I can’t find any documentation of them.

So what’s the problem? I have come to the conclusion that either I do not know what I am doing or theme engine authors do not understand the object model and power of GTK itself.

For instanced, when I try to ask GTK to draw a box in the same style that it’s drawn for the GtkTreeView, I get just a regular plain box. No special theming. Why? Because the engine itself is written to not allow this!

Take this snippet, from Clearlooks:

else if (DETAIL ("button") && widget && widget->parent &&
    (GE_IS_TREE_VIEW(widget->parent) ||
    GE_IS_CLIST (widget->parent) ||
    ge_object_is_a (G_OBJECT(widget->parent), "ETree"))) /* ECanvas inside ETree */

Here it will only draw the requested style if it has the proper detail, and the widget happens to be a real GtkTreeView (among other special cases). This makes it impossible for custom widgets to be written which emulate the look of stock GTK. Evidence of this can be seen throughout the desktop.

Let’s start with a stock GtkTreeView. Observe very carefully how the header looks. This is what the three custom widgets following have to try to mimic because it is impossible to actually request this style to be drawn.

GTK Headers in GtkTreeView

Here’s Evolution’s ETree headers. They use the “button” detail to actually draw each column header, probably because the author ran into this larger issue when writing the theming for the widget. In fact, there’s a separate hard coded hack in Clearlooks itself (which can be seen in the snippet above) to draw the button in a special way just for ETree, implying that even the engine author knows this is an issue!

GTK Headers in ETree

For XUL, there is a theme that uses the GTK theming engine. Most widgets look okay, after padding and spacing hacks, but I point back to their list view. Here’s a screenshot of how their headers are drawn. I talked with Garrett about this, and he said he remembers running into this very same issue when working on the Firefox/GTK theme.

GTK Headers in XUL

Finally, when working on my new high performance data binding 2.0 synergistic .NET list view widget, I again ran into this problem (I have run against similar problems in other custom widgets) when working on the drawing code for my headers. I used another hack to best replicate what I observed in most themes. I drew one large “button” box for the entire header at something like (-10, -10, Allocation.Width + 20, Allocation.Height + 20). This way there would be no button border or rounding, but you could get the subtle gradient/raised look that themes often draw. A nice hack that worked relatively well, but was still a nasty hack at the end of the day.

GTK Headers in Managed List View

This is just one example of inconsistency regarding custom widgets due to limitations in the theme engine. It’s not just Clearlooks however. In the gtk-engines module, there is a support library of common functions that theme engines can use. There are a bunch of macros taking the form GE_IS*. These macros are used to determine the type of an object, so that theme parts can be drawn in different ways depending on what widget they are to be drawn.

Here’s a really crappy shell command that counts all of the GE_IS* instances in gtk-engines to show how ingrained the idea of using an object’s type to conditionally style it is:

(s=0; for v in $(grep -c GE_IS `find -iregex "^.*\.[ch]$" | xargs` | cut -d':' -f 2); do s=$((s + v)); done; echo $s)

I’m sure there’s an easier way to get the sum of the counts from grep, but I am just that lame. Anyway, I am counting 286. Wow.

My widget does not derive from GtkTreeView, so I cannot use the styles defined for it by the engine. Because of this, custom widgets immediately become second class citizens. This is bad for complex applications that may do their own theming, and probably ISVs if they care, and most certainly for other toolkits (XUL, QT, Wx) which can use the GTK engine to provide a theme that integrates applications consuming another toolkit with the rest of GTK.

This limitation is pointless to me. All of this object-hierarchy-bound conditional theming can be replaced with a much better detail system that would allow any widget to draw any style. Details should also be published and made standard so it is easy to replicate the styles of a particular widget. The bottom line is that the theme engine should never need to know, or at least never require a widget to be of a particular type for the desired style to be drawn.

There are also a lot of other minor issues, mainly regarding RC styles and proper descriptions of states and colors. There are many themes that do not follow the directions when defining colors for states, which leads to many inconsistencies in custom widgets. This just needs to be better documented.

Of course, and again, maybe I just “don’t get it.” After all I am no theming expert… What I do know is that if I can’t achieve consistent widget theming in GTK, I might as well stop trying to fake it. I have decided, although tentatively, to just use Cairo to do all my drawing with my new list widget, and make it blend the best I can by reusing RC colors. Good-bye GTK Style API.