Extending the BWidget Tree

ABU 13-feb-2006

After developing Paved Widgets, an extension of some standard widgets with tiling support, I was tempted to adapt the BWidget::Tree providing a tiled background to him, too.

After inspecting its code (BWidget::Tree is pure-tcl code), I discovered that the graphical-tree is drawn over an internal canvas widget.

My first idea was to upgrade its code


with a very simple patch:

  • Add an header for loading the canvas extension
  package require Paved::canvas 1.1
  • Replace the following lines
  Widget::tkinclude Tree canvas .c \
  eval [list canvas $path.c] [Widget::subcget $path .c] -xscrollincrement 8


  Widget::tkinclude Tree Paved::canvas .c \
  eval [list Paved::canvas $path.c] [Widget::subcget $path .c] -xscrollincrement 8
  • Then, I would have been able to create and use an extended BWidget::Tree in this raw way:
  package require BWidget ; # my upgraded version
  Tree .t
  .t.c configure -tile xyz.gif

This would be a quick and easy solution.

I didn't like this solution because it was just a temporary patch; I would be forced to reapply this patch for every next BWidget release. This is maintenance !

So, a better idea raised after reading Designing SNIT widgets as mixins.

Instead of patching the original code, I could adapt and extended it at ""run-time""" !

Just a quick experiment:

 package require Paved::canvas 1.1
  # create a standard BWidget-tree
 Tree .mytree
  # transform the internal canvas in a paved-canvas
 Paved::canvas .mytree.c
  # then show the tiled background (assuming "xyz.gif" be in current directory)
 .mytree.c configure -tile xyz.gif

It works ! Now .mytree is a BWidget::Tree with a tiled background.

Seeing is believing ...



Now, it's time to wrap all this stuff with a good snit widgetadaptor ...

 package require snit
 package require BWidget
 package require Paved::canvas

 snit::widgetadaptor Paved::TreeAdaptor {

    # pseudo-component (see the constructor)
   component internalCanvas

   delegate option -tile to internalCanvas
   delegate option -tileorigin to internalCanvas
   delegate option -bgzoom to internalCanvas

   delegate option *     to hull
   delegate method *     to hull

   constructor {args} {
      installhull $win
       # a BWidget-Tree uses a canvas sub-widget for drawing the tree.
       # This internal canvas is named ".c".
       # BWidget-Tree provides an undocumented (and steady) method
       #  for accessing this feature 

       # internalCanvas is 'pseudo' Snit component. 
       # Being already within $win, you shouldn't install it;
       #  just provide a reference          
      set internalCanvas [Tree::getcanvas $win]
      Paved::canvasAdaptor $internalCanvas
      $win configurelist $args


 namespace eval Paved { ; }

  # this command *creates* a *new* Paved::Tree widget
  # (in contrast with Paved::TreelAdaptor 
  #  that wraps a pre-existing Tree widget)
 proc Paved::Tree {path args} {
   eval TreeAdaptor [::Tree $path] $args
   return $path

then, when you need to create a Paved::Tree, you only need:

  package require Paved::Tree
  Paved::Tree .mytree -tile $myGif ..... other options ...

the big plus is that the internal canvas is hidden, so that you apply the -tile option to the paved-tree, not to the internal canvas ...

Moreover, now you are able to adapt (at run-time) pre-existing Tree widgets with just one line:

    # be "$tt" your pre-existing Tree-widget you want to be tiled.
   Paved::TreeAdaptor $tt -tile $myGif 

From now, $tt is a Paved::Tree, 100% identical to an original Bwidget::Tree (both for its visual-look and for its programming interface).

Thanks again to William Duquette for its powerful Snit.