Designing SNIT widgets as mixins

Difference between version 14 and 15 - Previous - Next
[A/AK]
The purpose of this page is to advocate one particular way of writing [SNIT] widgetadaptors. I think that this way deserves much more attention than it gets now by miscellaneous [Snidget] authors.

There is already a lot of useful [Snidgets] around. It won't take much time before it will become hard to choose between existing alternatives for a given function. Imagine that one [text]-based [widget] gives you a decent syntax highlighting, while another one lacks syntax highlighting but provides a wonderful find dialog, the third one has autoscrolling and syntax highlighting but... You've got it. 

If there are ''n'' independent features that the standard [text] widget doesn't have, there may be 2^n-1 ''extended text widgets'' that implement different subsets of this features. 

There is a way to extend existing widgets, fully supported by [SNIT] from the very start, that doesn't create such a mess. It's rather similar to the approach that many [OO] systems call ''mixins'': when the particular instance of a class is enhanced in runtime with the functions coming from another class.

There is no extra effort needed to design [SNIT] widget this way. First, we need to use ''snit::widgetadaptor'' instead of a ''snit::widget'' declaration. Many programmers seem to avoid using widgetadaptor when the hull widget is either [frame] or [toplevel]; but it is evident for me that the choice between ''widget'' and ''widgetadaptor'' should be made considering the nature of a widget, not only its hull type. 

Second (supposed to be the main point of this page, actually), it's sometimes ''better'' to leave choosing actual hull type and creating hull widget out of the widgetadaptor. The widgetadaptor's constructor will look like this:

======
 constructor {args} {
     installhull $win
     $self configurelist $args
 }
======

This kind of widgetadaptor must be created when the hull widget already exists; when the widgetadaptor's instance is created, the existing widget suddenly gets new methods and/or options and functions.

It turned out to be very effective to design necessary addons for a particular widget type as ''independent'' pieces, implementing only a well-defined, highly-specialized function in each widgetadaptor. When you need some combination of those extensions in your application, it will be easy to write something like that:

======
 undoable [autocontextmenu [filecompletion [entry $path.ent]]]
======

where ''undoable'', ''autocontextmenu'' and ''filecompletion'' is a name of your widgetadaptors adding miscellaneous features to an entry.

There's more than that. When you'll need to adapt a widget that is not an [entry], but is much like an [entry], no change in the widgetadaptor will be required usually:

======
 undoable [ttk::combobox $path.myCombobox]
======

(I actually have an "undoable" adapter for entries that runs unmodified with [tile] entries and various comboboxes).

Having a toolbox of widgetadaptors that don't enforce any predefined hull type is much like working with [Unix] environment. You don't have to choose between ''ls with integrated awk scripting and tcl interpreter'' and another ''ls that reads [zip] archives but lacks builtin awk'': every tool does its own job, and they all are easily combined when a complex job must be done.
----
[NEM]: See also [Traits] which take a similar approach to composing objects.
----
[ABU] 13-feb-2006

Thanks to [A/AK] for his enlightening discussion. Here are some links about my work with '''widgetadaptors''':
   * [Extending the BWidget Tree]
   * [Paved Widgets] 
----
'''[WHD] - 2009-09-28 14:24:39'''

Very cool.  I don't tend to do this kind of thing because I worry about performance; each widget adds another layer of wrapping, and it slows things down.  But computers are a lot faster now than I first wrote Snit, and maybe I worry too much. :-)
----
'''[A/AK] - 2009-09-29 04:42:00 MSK'''

I used to worry about performance as well, but with snit2 based on [namespace ensemble] method dispatching is much faster than before. Once a delegated method is called, it stays in the object's method cache, and in the case of ensembles it means
that no TCL code is involved in dispatching it on all further calls. If there were even twenty redirections of this kind, 
I think it still won't add any significant performance penalty (with snit2 and tcl8.5+).

----
'''[WHD] - 2009-09-30 14:47:04'''

That was the intent; it's nice to know that it's worked out in practice.

----'''[DDG] - 2020-04-06:'''

Adding a few example implementations for [ttk::treeview mixins].

<<categories>> Snit Widgets | Snit | Design | Discussion