Dear Lazyweb: Shadows and Shaped Windows

I’ve been trying for a couple of days to figure out why a shaped window in Compiz doesn’t have a shadow. After looking through the Emerald and GTK+ decorators’ code, it seems that they only apply a shadow if the window has a titlebar decoration. This seems quite silly and limiting. Does anybody know of a way around this, or why we’re limited in such a way? Is this something that could be fixed?

Love,

Christian

Re: Subverting Subversion

I used to use the same trick Rodney Dawes describes in Subverting Subversion. Yes, it was very annoying to have to set everything from a file every time, or from stdin.

Ah, but there’s a better way, and people new to SVN seem to somehow miss this valuable command.

$ svn propedit svn:ignore

Up comes your editor, just as if you opened .cvsignore. You can now safely nuke your .cvsignore files. This is a useful command, so write it down until it’s burned into your brain.

Happy New Year!

Another year come and gone. Hopefully everybody is out having a good time or recovering from a good time. I just finished up for the night and am planning to get some reading done before bed, but thought a quick blog post summarizing the year was in order.

  • This year marks my second year living in the Bay Area and working at VMware, and my first year really heading up some larger projects. I began the year by working on what would in time be known as VMware Server, and spent the first half of the year head down at my desk working on each release. Following that, I’ve been working on specifications, feature implementations, and even Tango-style icons for VMware Workstation 6.0, which I will continue with for some time.
  • Jamie and I have now been dating for just over two years.
  • For the first time, I was invited to FooCamp, hosted by Tim O’Reilly. Awesome event that I really hope to be invited back to next year.
  • I learned to boogie board with a couple of friends in Santa Cruz. Later in the summer, Jamie and I took a nice vacation in Santa Cruz and spent most of it at the Santa Cruz Beach Boardwalk.
  • I had my second trip to Boston this year for the GNOME Summit. I went with Jamie to Salem and explored the touristy little town. Fun place, and I’d definitely like to spend more time there in the next couple of years.
  • I went with some friends to Castro St. in San Francisco for the first time for the big Halloween costume party that they have every year. We found out the following day that there was a shooting not too far from us, which was unnerving.
  • I got a Nintendo DS and a Wii, and have spent far too much money on both. I’m going to just start signing over my paychecks to Nintendo.

I’m sure I’m missing a few things. 🙂

Now, New Years Eve doubles as my mom’s birthday, which I imagine was not fun when growing up. I think the only day worse for a birthday is Christmas. It was certainly fun tonight, though. The night was spent with my mom, stepdad, brothers, sister, and Jamie. We watched Cars and played Scrabble, Bomberman Touch! for the DS (fun game!), and Cranium. We ended up spending most of the evening on Cranium, which has to be my favorite family game. We kept laughing so hard that we got ourselves sick.

Jamie brought over a bottle of champagne for us to drink. It was a late Christmas present, intended for New Years. We’re not heavy drinkers, so we only finished half the bottle, but it was very nummy. Along with that, we had a variety of snacks, including cheeses, beef sausages, vegetables, sodas, and pizza. This was, I think, my first New Years with my sister Jenna, as she had always been visiting cousins on New Years. She was pretty good most of the night, considering she had to stay up until midnight 🙂

January 1, 2007. With any luck, this year will be more exciting and eventful than last year. There are changes I hope to make, projects to abandon, projects to take up, and places to go. I think I’ll get some sleep so that I can get a head start on that.

Happy New Year!

Time to rethink GTK+ Tab Dragging

I’ve been planning on adding tab drag-and-drop functionality to VMware Workstation 6.0. Rather than implementing this ability from scratch (which I did with Gaim, and would rather not do again), I took the sane approach and started investigating the GTK+ 2.10 API for GtkNotebook tab drag-and-drop.

This is functionality that many applications have had to implement themselves, so it’s great that support had finally gone into GTK+ 2.10. So I must wonder, with all the various applications that would benefit from this new API, how did we get it so wrong?

Now, before I continue, let me say that I applaud the effort in getting this into GTK+ in the first place. Reordering tabs looks smooth, it’s only one API call, and the basics are trivial. Where this all falls down is when you try to do anything complicated with it, and by complicated I mean anything beyond a simple text editor.

Before starting, I investigated how many projects were using this API. A quick Google Code search shows that almost nobody does, aside from maybe the tab reordering API. I did find this list of complaints, which I remember reading before. I won’t repeat everything on this list, but I will list what I’ve ran into, and how I think we can improve this API.

Global functions are very bad. (Bug 386935)

In order to allow for a tab to be dragged out and form a new window, the application must call gtk_notebook_set_window_creation_hook and pass a callback function. When a tab marked as detachable is dragged to the root window, this function will be called. It is expected that the function create a new window, position it as per the x and y coordinates of the drop (if it so desires), show it, and return the resulting notebook. GTK+ will then add the tab to that notebook.

While useful, this suffers from a major design flaw. You can’t set a window creation hook per-notebook. You get exactly one window creation hook function, which must be responsible for any and all notebooks in the program. The hook function can only distinguish between them using the notebook’s group ID.

For smaller programs, this isn’t a huge limitation. Simple text editors and the like only need one function. However, imagine if your application has multiple notebooks that each need to be dragged, and imagine if the code for those notebooks are in two separate parts of the tree. Maybe you have a nice separation of the different parts of the project. Regardless, now you have to have one common function that knows about both and handles their window creations.

The problem gets far worse for applications separated into different libraries, or those using widget utility libraries. Two separate libraries both providing a notebook with drag-and-drop with their own window creation won’t be able to set up a hook. They would require that the main application handle determining the group IDs of the notebook widgets they care about and then calling the proper functions in the libraries. While doable, it’s a horrible burden on the application, and it doesn’t always work.

Of course, the whole thing completely breaks down when you’re writing a plugin with a notebook rather than an application. The plugin won’t be able to offer its own window creation, due to possible conflicts with the main application and other plugins.

The solution, of course, is to have per-notebook window creation hooks. GTK+ could attempt to call one of these and then fall back to the global hook, if it exists. If calling the global hook, GTK+ could spit out a warning informing the user that the program should be upgraded to the new API and that the existing method is deprecated.

Rather than functions, though, a signal handler may make more sense. It could use a collector and call the handlers until it finds one that returns a GtkNotebook. I could see this being useful if a widget component library (as part of a larger project) provides a default window creation handler that the calling application wants to override for a specific case.

Numeric group IDs lead to namespace collisions. (Bug 386930)

Right now, in order to indicate which notebooks are compatible for drag-and-drop operations, each GtkNotebook gets a group ID. This is an unsigned integer with absolutely no rules on how an ID should be picked. This is very dangerous, as it could cause namespace collisions in larger applications, resulting in tabs being droppable onto incompatible notebooks. This could easily crash such applications.

There’s no reason for us to be using integers. Take a look at GtkRadioButton. They also have groups, but they work a bit differently. The first GtkRadioButton defines the group, and the rest get passed that as the group identifier. In gtkmm, you actually have a Gtk::RadioButtonGroup object that you simply instantiate and then pass to each radio button.

Now, in any well-designed program, there should be only one place creating the notebook for a certain type of window, and usually that’s the only type of notebook capable of accepting tabs from the same class of notebook. So, why not do something like what gtkmm does and have some sort of static object that represents that group, define it once, and pass it to each notebook? This is guaranteed to be unique, and solves the namespace collision issue.

Signals need to be more clear. (Bug 386943)

You can determine if a page was added to a notebook or removed from it, but there’s no clear way of determining if it was due to an API call, or a drag-and-drop operation. We worked around it in VMware Workstation, but it would have been very helpful to know precisely that a page was added due to drag-and-drop. Same with the removed signal. I know they wanted to condense the signals and figured it would work in all cases, but it doesn’t, so please, give us some more specific signals!

Drop operations should be able to be programatically rejected. (Bug 386950)

There are times when you want to allow a tab to be draggable, but want to reject it in notebooks under certain circumstances. For example, in VMware Workstation, we have the “Home” tab. I would like to be able to drag this to empty windows, but if that window has a “Home” tab already, I want to reject the drag. To my knowledge, there is no way to do this currently.

Detaching tabs into new windows requires a drag to the desktop. (Bug 360225)

In most any program with tab drag-and-drop, you can drag a tab off into any area not in the tab bar and it will detach into a new window. With the GTK+ tab dragging, you have to actually drag it to the root desktop. Even dragging off into another window isn’t good enough. This sucks. Let’s fix this properly. We’re not doing anybody any favors.

I’m missing a few annoyances I ran into, but I’ll be blogging about them separately once I remember.

So, to recap, I believe we should:

  • Deprecate gtk_notebook_set_window_creation_hook and add a new create_window signal, returning a GtkNotebook.
  • Ditch the numeric group IDs and use some sort of identifier object or generic pointer to a static variable and pass that in instead.
  • Add new signals or something telling us specifically how the tab was added/removed.
  • Provide a way to reject a tab drop on a particular notebook programatically.
  • Call the window creation hook any time the tab is dragged off the tab portion of the notebook.

It would be great to see these things fixed so that more applications can actually use this API without major headaches. Anyone up for the task?

I plan to put up a new post soon giving a couple of tips for using the current API.

Update: Bugs have been filed for the above. They’re in the topic headers.

New libnotify, notification-daemon and notify-python releases

I’m very tired, so I’ll keep this brief.

I just put out libnotify 0.4.0, notification-daemon 0.3.5, and notify-python 0.1.0 releases. Most of the really annoying bugs people have reported have been fixed. More information is available on the news post.

I made a decision that will be unpopular to some, and I expect some disagreement on it. notification-daemon 0.3.5 does not ship with the Bubble theme. A large number of the problems people have reported to me on IRC and in e-mail centered around this theme, and until I have the time to give it the attention it needs, I’m removing it from the default install. It’s still in SVN and the tarball, and development will resume on it at a later date. However, I want to give people the best out-of-the-box experience as possible, and the Bubble theme currently makes that hard. If people want to chip in and help, you’re more than welcome.

Aside from that, it’s a very good release and I highly recommend people update. As always, please make sure to report bugs.

I have a couple of neat things I plan to work on. One is a little event notifier for scheduled events on online calendars (30Boxes.com to start). This will be using the new libnotify Python bindings. If it proves useful, I hope to add Google Calendar support as well. I’ll make some sort of announcement once I get a prototype working.

Alpha channels and release schedules

  • GTK+ and semi-transparent windows

    Mike Hearn wrote a blog entry on writing GTK+ applications that provide semi-transparent Cairo-rendered windows. He suggests a SexyWindow class for libsexy, which actually fits in with some of my plans nicely. More on this… someday.

  • Galago 0.5.0.. Almost

    Galago 0.5.0 is about to be released. I’ve said this for a while, but it’s actually happening now. The only thing left is to get the GalagoGtk# bindings out, but I’ve ran into a problem… I want to call the namespace Galago.Gtk, but then the GAPI-generated code tries to use Gtk.Widget and such, which causes a lookup in Galago.Gtk. I don’t know how to fix this, and may have to go back to the GalagoGtk namespace. Any suggestions?

  • Thanks Federico

    I’m somewhat borrowing Federico’s blog entry format on a trial basis for some posts. I’ve grown to like it. Helps to stay organized without being too verbose.