Applet sizing

People often want to make applets that scale to the current size of the panel. The problems people often have with this are:

  • The panel grows and grows...
  • ...or shrinks and shrinks...
  • ...or the user can't change the size anymore from the Options box.

These are caused by the applet trying to tell the filer what size the applet wants to be. The filer then sets/limits the panel size as requested, which often causes the applet to try to change its size for the new panel, etc.

Here's how to do it correctly. This explanation assumes a horizontal panel. For vertical panels, reverse width and height...

The trick is: set your vertical size request to a small and fixed value. 8 is good. Set the horizontal width to the desired size.

The sequence then looks like this:

  1. The panel asks the applet for its desired size.
  2. The applet responds 50×8 (want to be 50 pixels wide and as high as the panel).
  3. The panel sends the applet a size-allocate with its new size of 50×100 (the requested width × the actual height).

* Do not request the applet's actual height. This value is the minimum height for the applet. Stick to 8 (or whatever the actual minimum is).
* Do not request a height of -1. GTK will then use the widget's default natural size, which for images is the same situation as the previous point.

Here is a Demo Applet that both scales with the Panel and also adjusts to the orientation of the Panel (Horizontal or Vertical).

#!/usr/bin/env python
 
import findrox
findrox.version(1,9,0)
import rox, sys
from rox import applet, g
 
# This XPM ripped EVILly from the GTK tutorials:
xpm_data = [
        "16 13 3 1",
        "       c None",
        ".      c #000000000000",
        "X      c #FFFFFFFFFFFF",
        "   ......       ",
        "   .XXX.X.      ",
        "   .XXX.XX.     ",
        "   .XXX.XXX.    ",
        "   .XXX.....    ",
        "   .XXXXXXX.    ",
        "   .XXXXXXX.    ",
        "   .XXXXXXX.    ",
        "   .XXXXXXX.    ",
        "   .XXXXXXX.    ",
        "   .XXXXXXX.    ",
        "   .XXXXXXX.    ",
        "   .........    "]
 
class DemoApplet(applet.Applet):
    """
    A Demo Applet that displays a GtkImage and scales along with the Panel
    and adjusts according to the orientation of the Panel.
    """
    def __init__(self):
        applet.Applet.__init__(self, sys.argv[1])
 
        self.vertical = self.get_panel_orientation() in ('Left', 'Right')
        if self.vertical:
            self.set_size_request(8, -1)
        else:
            self.set_size_request(-1, 8)
 
        self.image = g.Image()
        self.add(self.image)
 
        self.pixbuf = g.gdk.pixbuf_new_from_xpm_data(xpm_data)
        self.image.set_from_pixbuf(self.pixbuf)
        self.size = 0
 
        self.connect('size-allocate', self.event_callback)
 
    def event_callback(self, widget, rectangle):
        """
        Get the new size and resize the pixbuf,
        but only if the size is different and is valid.
        This also assumes you want square widgets.
        """
        side = self.get_panel_orientation()
        if self.vertical:
            size = rectangle[2]
        else:
            size = rectangle[3]
 
        if size != self.size:
            self.resize_image(size)
 
    def resize_image(self, size):
        """Create a scaled version of the pixmap, and set image to that."""
        scaled_pixbuf = self.pixbuf.scale_simple(size, size,
                                                 g.gdk.INTERP_BILINEAR)
        self.image.set_from_pixbuf(scaled_pixbuf)
        self.size = size
 
    def get_panel_orientation(self):
        "Return the panel orientation ('Top', 'Bottom', 'Left', 'Right')"
        pos = self.socket.property_get('_ROX_PANEL_MENU_POS', 'STRING', False)
        if pos:
            return pos[2].split(',')[0]
        return 'Bottom'
 
main = DemoApplet()
main.show_all()
rox.mainloop()