Never give an applet a tooltip

The following code creates a GNOME panel applet with three things in it: some text, a button, and some more text. The button has a tooltip, and everything else has a different tooltip. However, there is a bug here. Can you find it?

#! /usr/bin/env python
 
import gnomeapplet
import gtk
 
def applet_factory (applet, iid):
    applet.set_applet_flags (gnomeapplet.EXPAND_MINOR)
    box = gtk.HBox ()
    label1 = gtk.Label ("blah blah blah")
    button = gtk.Button ("Do something")
    label2 = gtk.Label ("Bob Loblaw")
 
    applet.set_tooltip_text ("Stuff")
    button.set_tooltip_text ("(doesn't actually do anything)")
 
    box.pack_start (label1)
    box.pack_start (button)
    box.pack_start (label2)
    applet.add (box)
    applet.show_all ()
 
    return True
 
if __name__ == "__main__":
    gnomeapplet.bonobo_factory ("OAFIID:name_of_some_applet_factory",
                                gnomeapplet.Applet.__gtype__,
                                "test",
                                "0.0.0",
                                applet_factory)

(Hint: re-read the title of this post.)

To make the applet easy to use, we consider Fitt’s law, which deals with how easy or difficult it is for the user to move the mouse pointer to a particular position on the screen. Specifically, the easiest position is obviously the current position, since no movement is required. Second easiest are the corners of the screen: the user just shoves the mouse in the right direction. Since the pointer stops at the edge of the screen, it’s impossible to overshoot. Third easiest are the edges of the screen, for a similar reason.

Since panels live along the edge of the screen, we want to make sure our button is positioned precisely at the edge of the screen, making it easier to click on. That’s why the above code sets the EXPAND_MINOR flag, telling the applet to fill all available room between the edge of the screen and the edge of the panel. Our expectation is that the button will then be stretched to fill this space.

However, it doesn’t! There’s a one-pixel border around the applet’s contents. If we push the mouse pointer to the edge of the screen, we wind up overshooting the button by one pixel. So close, yet so far.

(Fun fact: earlier versions of Windows suffered from a similar problem. The Start button was a pixel or two away from the corner of the screen, so clicking in the corner didn’t activate it. This was fixed in Windows XP, or possibly earlier — I don’t have ready access to a Windows 2000 machine to check.)

The cause of the problem is hard to see, and took me a good amount of debugging to find: adding a tooltip to an applet causes that one-pixel border to appear! Remove the tooltip, and the button gets those two extra pixels that let it fill the entire panel along the minor axis. One wouldn’t expect setting a tooltip to affect widget layout, but it does.

To get the effect we want, we set the tooltip on the box used to pack the widgets, instead of on the applet itself. The one-pixel-border effect only happens with the applet itself, for reasons I can’t explain. So we replace this:

    applet.set_tooltip_text ("Stuff")

With this:

    box.set_tooltip_text ("Stuff")

Now all is right with the world, where “world” is defined as “Panflute bug #412309“.

(Pedant’s corner: another bug in the code above is that middle- and right-clicks on the button don’t get propagated to the applet for handling there. Fixing that requires subclassing gtk.Button to override do_button_press_event and return False if event.button != 1. But that’s much easier to figure out. I omitted that detail from the example above for simplicity’s sake.)

One Response

  1. Did you forget that Kuliniflute is an applet because you stopped calling it Music Applet?

    This problem could have been avoided if you would have just accepted the eponymously cool name of Kulinibox from the start!

Comments are closed.