[comment {-*- tcl -*- doctools manpage}]
[manpage_begin snitfaq n 0.93]
[copyright {2003-2004, by William H. Duquette}]
[moddesc {Snit}]
[titledesc   {Snit Frequently Asked Questions}]
[require Tcl 8.4]
[require snit [opt 0.93]]
[description]
[para]


[section OVERVIEW]

[emph {What is this document?}]
[para]



This is an atypical FAQ list, in that few of the questions are
frequently asked.  Rather, these are the questions I think a newcomer
to Snit should be asking.  This file is not a complete reference to
Snit, however; that information is in the [cmd snit] man page.

[para]
[emph {What is Snit?}]
[para]



Snit is a framework for defining abstract data types and megawidgets
in pure Tcl.  The name stands for "Snit's Not Incr Tcl", signifying
that Snit takes a different approach to defining objects than does
Incr Tcl, the best known object framework for Tcl.

[para]
[emph {What version of Tcl does Snit require?}]
[para]



Snit requires version Tcl 8.4 or later.

[para]
[emph {What are Snit's goals?}]
[para]



In developing Snit I had the following goals:

[para]

[list_begin bullet]
[bullet]

It should be at least as efficient as the object code I'd been writing
by hand.

[bullet]

The fact that Snit was used in an object's implementation should be
transparent (and irrelevant) to clients of that object.

[bullet]

Snit should be able to encapsulate objects from other sources,
particularly Tk widgets.

[bullet]

Snit megawidgets should be (to the extent possible) indistinguishable
in interface from Tk widgets.

[bullet]

Snit should be Tclish--that is, rather than trying to emulate C++,
Smalltalk, or anything else, it should try to emulate Tcl itself.

[bullet]

It should have a simple, easy-to-use, easy-to-remember syntax.

[list_end]

[para]
[emph {How is Snit different from other OO frameworks?}]
[para]



Snit is unique among Tcl object systems (so far as I know) in that
it's a system based not on inheritance but on delegation.  Object
systems based on inheritance only allow you to inherit from classes
defined using the same system, and that's a shame.  In Tcl, an object
is anything that acts like an object; it shouldn't matter how the
object was implemented.  I designed Snit to help me build applications
out of the materials at hand; thus, Snit is designed to be able to
incorporate and build on any object, whether it's a hand-coded object,
a Tk widget, an Incr Tcl object, a BWidget or almost anything else.

[para]
[emph {What can I do with Snit?}]
[para]



Using Snit, a programmer can:

[list_begin bullet]
[bullet]

Create abstract data types and Tk megawidgets.

[bullet]

Define instance variables, type variables, and option variables.

[bullet]

Define constructors, destructors, instance methods, type methods, and
several kinds of handler.

[bullet]

Assemble a type out of component types.  Instance methods and options
can be delegated to the component types automatically.

[list_end]

[section OBJECTS]

[emph {What is an object?}]
[para]



Obviously, a full description of object-oriented programming is beyond
the scope of this FAQ.  In simple terms, an object is an instance of
an abstract data type--a coherent bundle of code and data.

There are many ways to represent objects in Tcl/Tk; the best known
example are the Tk widgets.  A widget is an object; it is represented
by a Tcl command.  The object's methods are subcommands of the Tcl
command.  Snit uses the same conventions as Tk widgets do.

[para]
[emph {What is an abstract data type?}]
[para]



In computer science terms, an abstract data type is a complex data
structure along with a set of operations, like a stack, a queue, or a
binary tree--that is to say, in modern terms, an object.  In systems
that include include some form of inheritance the word [term class] is
usually used instead of [term {abstract data type}], but as Snit
doesn't do inheritance, the older term seems more appropriate.
Sometimes this is called [term {object-based}] programming as opposed
to object-oriented programming.

[para]

In Snit, as in Tk, a [term type] is a command that creates instances
-- objects -- which belong to the type.  Most types define some number
of [term option] which can be set at creation time, and usually can be
changed later.

[para]

Further, an [term instance] is also a Tcl command--a command that
gives access to the operations which are defined for that abstract
data type.  Conventionally, the operations are defined as subcommands,
or instance methods of the instance command.  For example, to insert
text into a Tk text widget, you use the text widget's [method insert]
method:

[para]
[example {
    # Create a text widget and insert some text in it.
    text .mytext -width 80 -height 24
    .mytext insert end "Howdy!"
}]
[para]

In this example, [cmd text] is the [term type] command and
[cmd .mytext] is the [term instance] command.

[para]
[emph {What kinds of abstract data types does Snit provide?}]
[para]



Snit allows you to define three kinds of abstract data types:

[para]

[list_begin bullet]
[bullet]

[cmd snit::type]
[bullet]

[cmd snit::widget]
[bullet]

[cmd snit::widgetadaptor]
[list_end]

[para]
[emph {What is a snit::type?}]
[para]



A [cmd snit::type] is a non-GUI abstract data type, e.g., a stack or a
queue.  [cmd snit::types] are defined using the [cmd snit::type]
command.  For example, if you were designing a kennel management
system for a dog breeder, you'd need a dog type.

[para]
[example {
% snit::type dog {
    # ...
}
::dog
}]
[para]

This definition defines a new command ([cmd ::dog], in this case)
that can be used to define dog objects.

[para]

An instance of a [cmd snit::type] can have [term {instance methods}],
[term {instance variables}], [term options], and [term components].
The type itself can have [term {type methods}] and [term procs].

[para]
[emph {What is a snit::widget?}]
[para]



A [cmd snit::widget] is a Tk megawidget built using Snit; it is very
similar to a [cmd snit::type].  See [sectref WIDGETS].

[para]
[emph {What is a snit::widgetadaptor?}]
[para]



A [cmd snit::widgetadaptor] uses Snit to wrap an existing widget type
(e.g., a Tk label), modifying its interface to a lesser or greater
extent.  It is very similar to a [cmd snit::widget].

See [sectref {WIDGET ADAPTORS}].

[para]
[emph {How do I create an instance of a snit::type?}]
[para]



You create an instance of a [cmd snit::type] by passing the new
instance's name to the type's create method.  In the following
example, we create a [cmd dog] object called [cmd spot].

[para]
[example {
% snit::type dog {
    # ....
}
::dog
% dog create spot
::spot
}]
[para]

The [method create] method name can be omitted so long as the instance
name doesn't conflict with any defined [term {type methods}].  So the
following example is identical to the previous example:

[para]
[example {
% snit::type dog {
    # ....
}
::dog
% dog spot
::spot
}]
[para]

This document generally uses the shorter form.

[para]

If the [cmd dog] type defines [term options], these can usually be given defaults
at creation time:

[para]
[example {
% snit::type dog {
    option -breed mongrel
    option -color brown

    method bark {} { return "$self barks." }
}
::dog
% dog create spot -breed dalmation -color spotted
::spot
% spot cget -breed
dalmation
% spot cget -color
spotted
}]
[para]

Either way, the instance name now names a new Tcl command that is used
to manipulate the object.  For example, the following code makes the
dog bark:

[para]
[example {
% spot bark
::spot barks.
}]
[para]

[emph {How do I refer to an object indirectly?}]
[para]



Some programmers prefer to save the object name in a variable, and
reference it that way.  For example,

[para]
[example {
% snit::type dog {
    option -breed mongrel
    option -color brown

    method bark {} { return "$self barks." }
}
::dog
% set d [dog spot -breed dalmation -color spotted]
::spot
% $d cget -breed
dalmation
% $d bark
::spot barks.
}]
[para]

[emph {How can I generate the object name automatically?}]
[para]



If you'd like Snit to generate an object name for you,
use the [const %AUTO%] keyword as the requested name:

[para]
[example {
    % snit::type dog {
	method bark {} { return "$self barks." }
    }
    ::dog
    % set d [dog %AUTO%]
    ::dog2
    % $d bark
    ::dog2 barks.
}]
[para]

The "%AUTO%" keyword can be embedded in a longer string:

[para]
[example {
    % set d [dog dog%AUTO%]
    ::dogdog4
    % $d bark
    ::dogdog4 barks.
    %
}]
[para]


[emph {Can types be renamed?}]
[para]



Tcl's [cmd rename] command renames other commands.  It's a common
technique in Tcl to modify an existing command by renaming it and
defining a new command with the original name; the new command usually
calls the renamed command.

[para]

[cmd snit::type]'s, however, should never be renamed; to do so breaks
the connection between the type and its objects.

[para]
[emph {Can objects be renamed?}]
[para]



Tcl's [cmd rename] command renames other commands.  It's a common
technique in Tcl to modify an existing command by renaming it and
defining a new command with the original name; the new command usually
calls the renamed command.

[para]

All Snit objects (including [term widgets] and [term widgetadaptors])
can be renamed, though this flexibility has some consequences:

[para]

[list_begin bullet]
[bullet]

In an instance method, [var self] will always contain the object's
current name, so instance methods can always call other instance
methods using [var self].

[bullet]

If the object is renamed, however, then $self's value will change.
Therefore, don't use $self for anything that will break if $self
changes. For example, don't pass a callback command to another object
like this:

[nl]
[example {
    [list $self methodname args...]
}]
[nl]

You'll get an error if this command is called after your object is
renamed.

[bullet]

Instead, the object should pass the callback command like this:

[nl]
[example {
[mymethod methodname args...]
}]
[nl]

The [cmd mymethod] command returns code that will call the desired
method safely; the caller of the callback can safely add additional
arguments to the end of the command as usual.

[nl]

For example, one could use this code to call a method when a Tk button
is pushed:

[nl]
[example {
    .btn configure -command [list $self buttonpress]
}]
[nl]

This will break if your instance is renamed.  Here's the safe way to
do it:

[nl]
[example {
    .btn configure -command [mymethod buttonpress]
}]

[bullet]

Every object has a private namespace; the name of this namespace is
available in method bodies, etc., as [var selfns].  This value is
constant for the life the object.

Use [var selfns] instead of [var self] if you need a unique token to
identify the object.

[bullet]

When a [cmd snit::widget]'s instance command is renamed, its Tk window
name remains the same -- and is still extremely
important.

Consequently, the Tk window name is available in [cmd snit::widget]
method bodies, etc., as [var win].  This value is constant for the
life of the object.  When creating child windows, it's best to use
[var {$win.child}] rather than [var {$self.child}] as the name of the
child window.

[bullet]

The names [var selfns] and [var win] may not be used as explicit
argument names for typemethods, methods, constructors, or onconfigure
handlers.

[bullet]

procs defined in a Snit type or widget definition used to be able to
reference instance variables if [var self] was passed to them
explicitly as the argument [var self]; this is no longer the case.

[list_end]

[para]
[emph {How do I destroy a Snit object?}]
[para]



Every instance of a [cmd snit::type] has a [method destroy] method:

[para]
[example {
    % snit::type dog {
	method bark {} { return "$self barks." }
    }
    ::dog
    % dog spot
    ::spot
    % spot bark
    ::spot barks.
    % spot destroy
    % info commands ::spot
}]
[para]

Snit megawidgets (i.e., instances of [cmd snit::widget] and

[cmd snit::widgetadaptor]) are destroyed like any other widget: by
using the Tk [cmd destroy] command on the widget or on one of its
ancestors in the window hierarchy.

[para]

In addition, any Snit object of any type can be destroyed by renaming
it to the empty string using the Tcl [cmd rename] command.


[section {INSTANCE METHODS}]

[emph {What is an instance method?}]
[para]

An instance method is a procedure associated with a specific object.
It is given free access to all of the object's type variables,
instance variables, and so forth.


[para]
[emph {How do I define an instance method?}]
[para]



Instance methods are defined in the type definition using
the [cmd method] statement.  Consider the following code that might be
used to add dogs to a computer simulation:

[para]
[example {
% snit::type dog {
    method bark {} {
        return "$self barks."
    }

    method chase {thing} {
        return "$self chases $thing."
    }
}
::dog
}]
[para]

A dog can bark, and it can chase things.

[para]

The [cmd method] statement looks just like a normal Tcl [cmd proc],
except that it appears in a [cmd snit::type] definition.  Notice that
every instance method gets an implicit argument called [var self];
this argument contains the object's name.

[para]
[emph {How does a client call an instance method?}]
[para]



The method name becomes a subcommand of the object.  For example,
let's put a simulated dog through its paces:

[para]
[example {
% dog spot
::spot
% spot bark
::spot barks.
% spot chase cat
::spot chases cat.
}]
[para]

[emph {How does an instance method call another instance method?}]
[para]



If method A needs to call method B on the same object, it does so just
as a client does: it calls method B as a subcommand of the object
itself, using the object name stored in [var self].

[para]

Suppose, for example, that our dogs never chase anything without
barking at them:

[para]
[example {
% snit::type dog {
    method bark {} {
        return "$self barks."
    }

    method chase {thing} {
        return "$self chases $thing.  [$self bark]"
    }
}
::dog
% dog spot
::spot
% spot bark
::spot barks.
% spot chase cat
::spot chases cat.  ::spot barks.
}]
[para]

[emph {Are there any limitations on instance method names?}]
[para]



Not really, so long as you avoid the standard instance method names:
[method configure], [method configurelist], [method cget],

[method destroy], and [method info].

[para]
[emph {How do I make an instance method private?}]
[para]



It's often useful to define private methods, that is, instance methods
intended to be called only by other methods of the same object.

[para]

Snit doesn't implement any access control on instance methods, so all
methods are [emph {de facto}] public.  Conventionally, though, the
names of public methods begin with a lower-case letter, and the names
of private methods begin with an upper-case letter.

[para]

For example, suppose our simulated dogs only bark in response to other
stimuli; they never bark just for fun.  So the [method bark] method
could be private:

[para]
[example {
% snit::type dog {
    # Private by convention: begins with uppercase letter.
    method Bark {} {
        return "$self barks."
    }

    method chase {thing} {
        return "$self chases $thing. [$self Bark]"
    }
}
::dog
% dog fido
::fido
% fido chase cat
::fido chases cat. ::fido barks.
}]
[para]

[emph {Are there any limitations on instance method arguments?}]
[para]



Method argument lists are defined just like normal Tcl proc argument
lists; they can include default values, and the [var args] argument.
However, every method is called with a number of implicit arguments
provided by Snit in addition to those explicitly defined.  The names
of these implicit arguments may not used to name explicit arguments.

[para]
[emph {What implicit arguments are passed to each instance method?}]
[para]



The arguments implicitly passed to every method are [var type],
[var selfns], [var win], and [var self].

[para]
[emph {What is $type?}]
[para]



The implicit argument [var type] contains the fully qualified name of
the object's type:

[para]
[example {
% snit::type thing {
    method mytype {} {
        return $type
    }
}
::thing
% thing something
::something
% something mytype
::thing
}]
[para]

[emph {What is $self?}]
[para]



The implicit argument [var self] contains the object's fully
qualified name.

[para]

If the object's command is renamed, then [var self] will change to
match in subsequent calls.  Thus, your code should not assume that
[var self] is constant unless you know for sure that the object
will never be renamed.

[para]
[example {
% snit::type thing {
    method myself {} {
        return $self
    }
}
::thing
% thing mutt
::mutt
% mutt myself
::mutt
% rename mutt jeff
% jeff myself
::jeff
}]
[para]

[emph {What is $selfns?}]
[para]



Each Snit object has a private namespace in which to store its

[term {instance variables}] and [term options].  The implicit argument
[var selfns] is the name of this namespace; it never changes, and
is constant for the life of the object, even if the object's name
changes:

[para]
[example {
% snit::type thing {
    method myNameSpace {} {
        return $selfns
    }
}
::thing
% thing jeff
::jeff
% jeff myNameSpace
::thing::Snit_inst3
% rename jeff mutt
% mutt myNameSpace
::thing::Snit_inst3
}]
[para]

The above example reveals how Snit names an instance's private
namespace; however, you should not write code that depends on the
specific naming convention, as it might change in future releases.

[para]
[emph {What is $win?}]
[para]



The implicit argument [var win] is defined for all Snit methods,
including those of [term widgets] and [term widgetadaptors], though it
makes sense mostly for the latter two kinds.  [var win] is simply
the original name of the object, whether it's been renamed or not.
For widgets and widgetadaptors, it is also therefore the name of a Tk
window.

When a [cmd snit::widgetadaptor] is used to modify the interface of a
widget or megawidget, it must rename the widget's original command and
replace it with its own.

Thus, using [var win] whenever the Tk window name is called for
means that a [cmd snit::widget] or [cmd snit::widgetadaptor] can be
adapted by a [cmd snit::widgetadaptor].  See [sectref WIDGETS] for
more information.

[para]
[emph {How do I pass an instance method as a callback?}]
[para]



It depends on the context.

Suppose in my application I have a [cmd dog] object named [cmd fido],
and I want [cmd fido] to bark when a Tk button is pressed.  In this
case, I pass the instance method in the normal way, as a subcommand of
[cmd fido]:

[para]
[example {
    button .bark -text "Bark!" -command [list fido bark]
}]
[para]

In typical Tcl style, we use a callback to hook two independent
components together.  But what if the [cmd dog] object itself, passing
one of its own instance methods to another object (one of its
components, say)?  The obvious thing to do is this:

[para]
[example {
% snit::widget dog {
    constructor {args} {
        #...
        button $win.barkbtn -text "Bark!"  -command [list $self bark]
        #...
    }
}
::dog
}]
[para]

(Note that in this example, our [cmd dog]

becomes a [cmd snit::widget], because it has GUI behavior.  See
[sectref WIDGETS] for more.)  Thus, if we create a [cmd dog] called
[cmd .spot], it will create a Tk button called [cmd .barkbtn] and pass
it [cmd {$self bark}] as the command.

[para]

Now, this will work--provided that [cmd .spot] is never renamed.  But
why should [cmd .spot] be renamed?  Surely renaming widgets is
abnormal?  And so it is--unless [cmd .spot] is the hull component of a
[cmd snit::widgetadaptor].  If it is, then it will be renamed, and
[cmd .spot] will name the [cmd snit::widgetadaptor] object.  When the
button is pressed, the command [cmd {$self bark}] will be handled by
the [cmd snit::widgetadaptor], which might or might not do the right
thing.

[para]

There's a safer way to do it, and it looks like this:

[para]
[example {
% snit::widget dog {
    constructor {args} {
        #...
        button $win.barkbtn -text "Bark!"  -command [mymethod bark]
        #...
    }
}
::dog
}]
[para]

The command [cmd mymethod] can be used like [cmd list] to build up a
callback command; the only difference is that [cmd mymethod] returns a
form of the command that won't change if the instance's name changes.

[para]
[emph {How do I delegate instance methods to a component?}]
[para]



See [sectref DELEGATION].


[section {INSTANCE VARIABLES}]


[emph {What is an instance variable?}]
[para]



An instance variable is a private variable associated with some
particular Snit object.  Instance variables can be scalars or arrays.

[para]
[emph {How is a scalar instance variable defined?}]
[para]



Scalar instance variables are defined in the type definition using the
[cmd variable] statement.  You can simply name it, or you can
initialize it with a value:

[para]
[example {
snit::type mytype {
    # Define variable "greeting" and initialize it with "Howdy!"
    variable greeting "Howdy!"
}
}]
[para]

[emph {How is an array instance variable defined?}]
[para]



Array instance variables are also defined using the [cmd variable]
command; however, you can't initialize them using the variable
command.  Typically, they get initialized in the constructor:

[para]
[example {
snit::type mytype {
    # Define array variable "greetings"
    variable greetings

    constructor {args} {
        set greetings(formal) "Good Evening"
        set greetings(casual) "Howdy!"
    }
}
}]
[para]

[emph {Are there any limitations on instance variable names?}]
[para]



Just a few.

[para]

First, every Snit object has a built-in instance variable called
[var options], which should never be redefined.

[para]

Second, all names beginning with "Snit_" or "snit_" are reserved for
use by Snit internal code.

[para]

Second, instance variable names with the namespace delimiter
([const ::]) in them are likely to cause great confusion.

[para]
[emph {Do I need to declare instance variables before using them?}]
[para]



No. Once you've defined an instance variable in the type definition,
it can be used in any instance code without declaration.  This differs
from normal Tcl practice, in which all non-local variables in a proc
need to be declared.

[para]
[emph {How do I pass an instance variable's name to another object?}]
[para]



In Tk, it's common to pass a widget a variable name; for example, Tk
label widgets have a [option -textvariable] option which names the
variable which will contain the widget's text.  This allows the
program to update the label's value just by assigning a new value to
the variable.

[para]

If you naively pass the instance variable name to the label widget,
you'll be confused by the result; Tk will assume that the name names a
global variable.  Instead, you need to provide a fully-qualified
variable name.  From within an instance method or a constructor, you
can fully qualify the variable's name using the [cmd varname] command:

[para] 
[example {
snit::widget mywidget {
    variable labeltext ""

    constructor {args} {
        # ...

        label $win.label -textvariable [varname labeltext]

        # ...
    }
}
}]
[para]

[emph {How do I make an instance variable public?}]
[para]



Practically speaking, you don't.  Instead, you'll implement public
variables as [term options].

Alternatively, you can write [term {instance methods}] to set and get
the variable's value.



[section OPTIONS]

[emph {What is an option?}]
[para]



A type's options are the equivalent of what other object-oriented
languages would call public member variables or properties: they are
data values which can be retrieved and (usually) set by the clients of
an object.  If a type is to be used a record type, it's possible that
options are all that's needed.

[para]

Snit's implementation of options follows the Tk model fairly exactly,
except that [cmd snit::type] objects can't interact with Tk's option
database; [cmd snit::widget] and [cmd snit::widgetadaptor] objects, on
the other hand, can and do.




[para]
[emph {How do I define an option?}]
[para]



Options are defined in the type definition using the [cmd option]
statement.  Consider the following type, to be used in an application
that manages a list of dogs for a pet store:

[para]
[example {
% snit::type dog {
    option -breed mongrel
    option -color brown
    option -akc 0
    option -shots 0
}
::dog
}]
[para]


According to this, a dog has four notable properties, or options: a
breed, a color, a flag that says whether it's pedigreed with the
American Kennel Club, and another flag that says whether it has had
its shots.  The default dog, evidently, is a brown mutt.

[para]

If no default value is specified, the option's value defaults to the
empty string.

[para]

The Snit man page refers to these as "locally defined" options.

[para]
[emph {How can a client set options at object creation?}]
[para]



The normal convention is that the client may pass any number of
options and their values after the object's name at object creation.
For example, the ::dog command defined in the previous answer can now
be used to define individual dogs.  Any or all of the options may be
set at creation time.

[para]
[example {
  % dog spot -breed beagle -color "mottled" -akc 1 -shots 1
  ::spot
  % dog fido -shots 1
  ::fido
}]
[para]

So ::spot is a pedigreed beagle; ::fido is a typical mutt, but his
owners evidently take care of him, because he's had his shots.

[para]

[emph Note:] If the type defines a constructor, it can specify a
different object-creation syntax.  See [sectref CONSTRUCTORS] for more
information.

[para]
[emph {How can a client retrieve an option's value?}]
[para]



Retrieve option values using the [method cget] method:

[para]
[example {
% spot cget -color
mottled
% fido cget -breed
mongrel
}]
[para]

[emph {How can a client set options after object creation?}]
[para]



Any number of options may be set at one time using the

[method configure] instance method.  Suppose that closer inspection
shows that ::fido is a rare Arctic Boar Hound of a lovely dun color:

[para]
[example {
  % fido configure -color dun -breed "Arctic Boar Hound"
  % fido cget -color
  dun
  % fido cget -breed
  Arctic Boar Hound
}]
[para]

Alternatively, the [method configurelist] method takes a list of
options and values; this is some times more convenient:

[para]
[example {
% set features [list -color dun -breed "Arctic Boar Hound"]
-color dun -breed {Arctic Boar Hound}
% fido configurelist $features
% fido cget -color
dun
% fido cget -breed
Arctic Boar Hound
}]
[para]


[emph {How should an instance method access an option value?}]
[para]



There are two ways an instance method can set and retrieve an option's
value.  One is to use the [method configure] and [method cget]
methods, as shown below:

[para]
[example {
% snit::type dog {
    option -weight 10

    method gainWeight {} {
        set wt [$self cget -weight]
        incr wt
        $self configure -weight $wt
    }
}
::dog
% dog fido
::fido
% fido cget -weight
10
% fido gainWeight
% fido cget -weight
11
}]
[para]

Alternatively, Snit provides a built-in array instance variable called
[var options].  The indices are the option names; the values are the
option values.  The method given above can thus be rewritten as
follows:

[para]
[example {
    method gainWeight {
        incr options(-weight)
    }
}]
[para]

As you can see, using the [var options] variable involves considerably
less typing.  If you define [cmd onconfigure] or [cmd oncget]
handlers, as described in the following answers, you might wish to use
the [method configure] and [method cget] methods anyway, just so that
any special processing you've implemented is sure to get done.

[para]
[emph {How can I catch changes to an option's value?}]
[para]



Use an [cmd onconfigure] handler.

[para]
[emph {What is an onconfigure handler?}]
[para]



An [cmd onconfigure] handler is a special kind of instance method
that's called whenever the related option is given a new value via the
[method configure] or [method configurelist] instance methods. The
handler can validate the new value, pass it to some other object, and
anything else you'd like it to do.

[para]

An [cmd onconfigure] handler is defined by an [cmd onconfigure]
statement in the type definition.

Here's what the default configuration behavior would look like if
written as an [cmd onconfigure] handler:

[para]
[example {
snit::type dog {
    option -color brown

    onconfigure -color {value} {
        set options(-color) $value
    }
}
}]
[para]

The name of the handler is just the option name.  The argument list
must have exactly one argument; it can be called almost anything, but
conventionally it is called [const value].  Within the handler, the
argument is set to the new value; also, all instance variables are
available, just as in an instance method.

[para]

Note that if your handler doesn't put the value in the [var options]
array, it doesn't get updated.

[para]
[emph {How can I catch accesses to an option's value?}]
[para]



Use an [cmd oncget] handler.

[para]
[emph {What is an oncget handler?}]
[para]



An [cmd oncget] handler is a special kind of instance method that's
called whenever the related option's value is queried via the

[method cget] instance method.  The handler can compute the value,
retrieve it from a database, or anything else you'd like it to do.

[para]

An [cmd oncget] handler is defined by an [cmd oncget] statement in the
type definition.  Here's what the default behavior would look like if
written as an [cmd oncget] handler:

[para]
[example {
snit::type dog {
    option -color brown

    oncget -color {
        return $options(-color)
    }
}
}]
[para]

The handler takes no arguments, and so has no argument list; however,
all instance variables are available, just as they are in normal
instance methods.



[section {TYPE VARIABLES}]

[emph {What is a type variable?}]
[para]



A type variable is a private variable associated with a Snit type
rather than with a particular instance of the type.  In C++ and Java,
the equivalent of type variables are called static member variables.
Type variables can be scalars or arrays.

[para]
[emph {How is a scalar type variable defined?}]
[para]



Scalar type variables are defined in the type definition using the
[cmd typevariable] statement.  You can simply name it, or you can
initialize it with a value:

[para]
[example {
snit::type mytype {
    # Define variable "greeting" and initialize it with "Howdy!"
    typevariable greeting "Howdy!"
}
}]
[para]

Every object of type [cmd mytype] now has access to a single variable
called [var greeting].

[para]
[emph {How is an array type variable defined?}]
[para]



Array-valued type variables are also defined using the

[cmd typevariable] command; however, you can't initialize them that
way, just as you can't initialize array variables using Tcl's standard
[cmd variable] command.  [emph {Type constructors}] are the usual way
to initialize array-valued type variables.

[para]
[emph {Are there any limitations on type variable names?}]
[para]



Type variable names have the same restrictions as instance variable
names.

[para]
[emph {Do I need to declare type variables before using them?}]
[para]



No. Once you've defined a type variable in the type definition, it can
be used in [term {instance methods}] or [term {type methods}] without
declaration.  This differs from normal Tcl practice, in which all
non-local variables in a proc need to be declared.

[para]
[emph {How do I pass a type variable's name to another object?}]
[para]



In Tk, it's common to pass a widget a variable name; for example, Tk
label widgets have a [option -textvariable] option which names the
variable which will contain the widget's text.  This allows the
program to update the label's value just by assigning a new value to
the variable.

[para]

If you naively pass a type variable name to the label widget, you'll
be confused by the result; Tk will assume that the name names a global
variable.  Instead, you need to provide a fully-qualified variable
name.  From within an instance method or a constructor, you can fully
qualify the type variable's name using the [cmd typevarname] command:

[para] 
[example {
snit::widget mywidget {
    typevariable labeltext ""

    constructor {args} {
        # ...

        label $win.label -textvariable [typevarname labeltext]

        # ...
    }
}
}]
[para]

[emph {How do I make a type variable public?}]
[para]



There are two ways to do this.  The preferred way is to write a pair
of [term {type methods}] to set and query the variable's value.

[para]

Alternatively, you can publicize the variable's name in your
documentation and clients can access it directly.  For example,

[para]
[example {
snit::type mytype {
    typevariable myvariable
}

set ::mytype::myvariable "New Value"
}]
[para]

As shown, type variables are stored in the type's namespace, which has
the same name as the type itself.



[section {TYPE METHODS}]

[emph {What is a type method?}]
[para]



A type method is a procedure associated with the type itself rather
than with any specific instance of the type.

[para]
[emph {How do I define a type method?}]
[para]



Type methods are defined in the type definition using the

[cmd typemethod] statement:

[para]
[example {
    snit::type dog {
	# List of pedigreed dogs
	typevariable pedigreed

	typemethod pedigreedDogs {} {
	    return $pedigreed
	}

	# ...
    }
}]
[para]

Suppose the [cmd dog] type maintains a list of the names of the dogs
that have pedigrees.  The [cmd pedigreedDogs] type method returns this
list.

[para]

The [cmd typemethod] statement looks just like a normal Tcl

[cmd proc], except that it appears in a [cmd snit::type] definition.
It defines the method name, the argument list, and the body of the
method.

[para]
[emph {How does a client call a type method?}]
[para]



The method name becomes a subcommand of the type's command.  For
example,

[para]
[example {
snit::type dog {
    option -pedigreed 0

    # List of pedigreed dogs
    typevariable pedigreed

    typemethod pedigreedDogs {} {
        return $pedigreed
    }

    # ...
}

dog spot -pedigreed 1
dog fido

foreach dog [dog pedigreedDogs] { ... }
}]
[para]

[emph {Are there any limitations on type method names?}]
[para]



Not really, so long as you avoid the standard type method names:

[para]
[method create] and [method info].

[para]
[emph {How do I make a type method private?}]
[para]



It's sometimes useful to define private type methods, that is, type
methods intended to be called only by other type or instance methods
of the same object.

[para]

Snit doesn't implement any access control on type methods; by
convention, the names of public methods begin with a lower-case
letter, and the names of private methods begin with an upper-case
letter.

[para]

Alternatively, a Snit [cmd proc] can be used as a private type method; see
[sectref PROCS].

[para]
[emph {Are there any limitations on type method arguments?}]
[para]



Method argument lists are defined just like normal Tcl proc argument
lists; they can include default values, and the [var args] argument.
However, every type method is called with an implicit argument called
[var type] that contains the name of the type command.  In addition,
type methods should by convention avoid using the names of the
arguments implicitly defined for [term {instance methods}].

[para]
[emph {How does an instance or type method call a type method?}]
[para]



If an instance or type method needs to call a type method, it should
use [var type] to do so:

[para]
[example {
snit::type dog {

    typemethod pedigreedDogs {} { ... }

    typemethod printPedigrees {} {
        foreach obj [$type pedigreedDogs] { ... }
    }
}
}]
[para]

[emph {How do I pass a type method as a callback?}]
[para]



It's common in Tcl to pass a snippet of code to another object, for it
to call later.  Because types cannot be renamed, the thing to do is
just use the type name, or, if the callback is registered from within
a type method, [var type].  For example, suppose we want to print a
list of pedigreed dogs when a Tk button is pushed:

[para]
[example {
  button .btn -text "Pedigrees" -command [list dog printPedigrees]
  pack .btn
}]


[section PROCS]

[emph {What is a proc?}]
[para]



A Snit [cmd proc] is really just a Tcl proc defined within the type's
namespace.  You can use procs for private code that isn't related to
any particular instance.  For example, I often find myself writing a
proc to pop the first item off of a list stored in a variable.

[para]
[emph {How do I define a proc?}]
[para]



Procs are defined by including a [cmd proc] statement in the type
definition:

[para]
[example {
snit::type mytype {
    # Pops and returns the first item from the list stored in the
    # listvar, updating the listvar
   proc pop {listvar} { ... }

   # ...
}
}]
[para]

[emph {Are there any limitations on proc names?}]
[para]



Any name can be used, so long as it does not begin with [const Snit_];
names beginning with [const Snit_] are reserved for Snit's own use.
However, the wise programmer will avoid proc names like [cmd set],
[cmd list], [cmd if], and so forth that would shadow standard Tcl
command names.

[para]

By convention, proc names, being private, begin with a capital letter.

[para]
[emph {How does a method call a proc?}]
[para]



Just like it calls any Tcl command.  For example,

[para]

[example {
snit::type mytype {
    # Pops and returns the first item from the list stored in the
    # listvar, updating the listvar
    proc Pop {listvar} { ... }

    variable requestQueue {}

    # Get one request from the queue and process it.
    method processRequest {} {
        set req [Pop requestQueue]
    }
}
}]
[para]

[emph {How can I pass a proc to another object as a callback?}]
[para]



I tend to use type or instance methods for this purpose and ignore
procs altogether.  But if you really need to, the [cmd codename]
command returns the proc's fully qualified name.


[section {TYPE CONSTRUCTORS}]

[emph {What is a type constructor?}]
[para]

A type constructor is a body of code that initializes the type as a
whole, rather like a C++ static initializer.  The body of a type
constructor is executed once when the type is defined, and never
again.

[para]

A type can have at most one type constructor.

[para]
[emph {How do I define a type constructor?}]
[para]


A type constructor is defined by using the [cmd typeconstructor]
statement in the type definition.  For example, suppose the type uses
an array-valued type variable as a look up table:

[para]
[example {
    % snit::type mytype {
	typevariable lookupTable

	typeconstructor {
	    array set lookupTable {key value...}
	}
    }
    ::mytype
    %
}]
[para]



[section CONSTRUCTORS]

[emph {What is a constructor?}]
[para]



In object-oriented programming, an object's constructor is responsible
for initializing the object completely at creation time. The constructor

receives the list of options passed to the [cmd snit::type] command's
create method and can then do whatever it likes.  That might include
computing instance variable values, reading data from files, creating
other objects, updating type variables, and so forth.

[para]

The constructor doesn't return anything.

[para]
[emph {How do I define a constructor?}]
[para]



A constructor is defined by using the [cmd constructor] statement in
the type definition.  Suppose that it's desired to keep a list of all
pedigreed dogs.  The list can be maintained in a type variable and
retrieved by a type method.  Whenever a dog is created, it can add
itself to the list--provided that it's registered with the American
Kennel Club.

[para] 
[example {
% snit::type dog {
    option -akc 0

    typevariable akcList {}

    constructor {args} {
        $self configurelist $args

        if {$options(-akc)} {
            lappend akcList $self
        }
    }

    typemethod akclist {} {
        return $akcList
    }
}
::dog
% dog spot -akc 1
::spot
% dog fido
::fido
% dog akclist
::spot
}]
[para]

[emph {What does the default constructor do?}]
[para]



If you don't provide a constructor explicitly, you get the default
constructor, which looks like this:

[para]
[example {
% snit::type dog {
    option -breed mongrel
    option -color brown
    option -akc 0

    constructor {args} {
        $self configurelist $args
    }
}
::dog
% dog spot -breed dalmatian -color spotted -akc 1
::spot
}]
[para]

When the constructor is called, [var args] will be set to the list of
arguments that follow the object's name.  The constructor is allowed
to interprete this list any way it chooses; the normal convention is
to assume that it's a list of option names and values, as shown in the
example above.  If you simply want to save the option values, you
should use the [method configurelist] method, as shown.

[para]
[emph {Can I choose a different command line syntax for the constructor?}]
[para]



Yes, you can.  For example, suppose we wanted to be sure that the
breed was explicitly stated for every dog at creation time, and
couldn't be changed thereafter.  One way to do that is as follows:

[para]
[example {
% snit::type dog {
    variable breed

    option -color brown
    option -akc 0

    constructor {theBreed args} {
        set breed $theBreed
        $self configurelist $args
    }

    method breed {} {
        return $breed
    }
}
::dog
% dog spot dalmatian -color spotted -akc 1
::spot
% spot breed
dalmatian
}]
[para]

The drawback is that this creation syntax is non-standard, and may
limit the compatibility of your new type with other people's code.
For example, Snit generally assumes that [emph {components}] use the
standard creation syntax.


[para]
[emph {Are there any limitations on constructor arguments?}]
[para]



Constructor argument lists are defined just like normal Tcl proc
argument list; they can include default values, and the [var args]
argument.  However, the constructor is called with a number of
implicit arguments provided by Snit in addition to those explicitly
defined.  The names of these implicit arguments may not used to name
explicit arguments.

[para]
[emph {What implicit arguments are passed to the constructor?}]
[para]



The constructor gets the same implicit arguments that are passed to
[term {instance methods}]: [var type], [var selfns], [var win], and
[var self].



[section DESTRUCTORS]

[emph {What is a destructor?}]
[para]



A destructor is a special kind of method that's called when an object
is destroyed.  It's responsible for doing any necessary clean-up when
the object goes away: destroying components, closing files, and so
forth.

[para]
[emph {How do I define a destructor?}]
[para]



Destructors are defined by using the [cmd destructor] statement in the
type definition.  Suppose we're maintaining a list of pedigreed dogs;
then we'll want to remove dogs from it when they are destroyed.

[para]
[example {
% snit::type dog {
    option -akc 0

    typevariable akcList {}

    constructor {args} {
        $self configurelist $args

        if {$options(-akc)} {
            lappend akcList $self
        }
    }

    destructor {
        set ndx [lsearch $akcList $self]

        if {$ndx != -1} {
            set akcList [lreplace $akcList $ndx $ndx]
        }
    }

    typemethod akclist {} {
        return $akcList
    }
}
::dog
% dog spot -akc 1
::spot
% dog fido -akc 1
::fido
% dog akclist
::spot ::fido
% fido destroy
% dog akclist
::spot
}]
[para]

[emph {Are there any limitations on destructor arguments?}]
[para]



Yes; a destructor has no explicit arguments.

[para]
[emph {What implicit arguments are passed to the destructor?}]
[para]



The destructor gets the same implicit arguments that are passed to
[term {instance methods}]: [var type], [var selfns], [var win], and
[var self].

[para]
[emph {Must components be destroyed explicitly?}]
[para]



Yes and no.

[para]

For a Snit megawidget ([cmd snit::widgets] and

[cmd snit::widgetadaptors]), any widget [term components] created by
it will be destroyed automatically when the megawidget is destroyed,
in keeping with normal Tk behavior (destroying a parent widget
destroys the whole tree).

On the other hand, all non-widget components of a Snit megawidget, and
all components of a normal [cmd snit::type] object, must be destroyed
explicitly in a destructor.



[section COMPONENTS]

[emph {What is a component?}]
[para]



Often an object will create and manage a number of other objects.  One
example is a Snit megawidget that composes a number of Tk widgets.
These objects are part of the main object and are thus are called
components of it.

[para]

But Snit also has a more precise meaning for [term component].  The
components of a Snit object are those objects created by it to which
methods and options can be delegated.  See [sectref DELEGATION] for
more information about delegation.

[para]
[emph {How do I create a component?}]
[para]



First, you must decide what role a component plays within your object,
and give the role a name.  For example, suppose your [cmd dog] object
creates a [cmd tail] object (the better to wag with, no doubt).  The
[cmd tail] object will have some command name, but you tell Snit about
it using its role name, as follows:

[para]
[example {
% snit::type dog {
    # Define component name as an instance variable
    variable mytail

    constructor {args} {
        # Create and save the component's command
        install mytail using tail %AUTO% -partof $self
        $self configurelist $args
    }

    method wag {} {
        $mytail wag
    }
}
::dog
}]
[para]

As shown here, it doesn't matter what the [cmd tail] object's real
name is; the [cmd dog] object refers to it by its component name.

[para]

The above example shows one way to delegate the [method wag] method to
the [var mytail] component; see [sectref DELEGATION] for an easier way.


[para]
[emph {How is a component named?}]
[para]

A component has two names.  The first name represents the role the
component object plays within the Snit object.  This is the component
name proper, and is the name used to refer to the component within
Snit code.  The second name is the name of the actual component object
created by the Snit object's constructor.  This second name is always
a Tcl command name, and is referred to as the component's object name.

[para]

In the example in the previous FAQ, the component name is "mytail";
the "mytail" component's object name is chosen automatically by Snit
since %AUTO% was used when the component object was created.


[para]
[emph {What does the install command do?}]
[para]


The [cmd install] command creates the component using the specified
command ([cmd {tail %AUTO% -partof $self}]), and assigns the result to
the [var mytail] variable.  For [cmd snit::type]s, the [cmd install]
command shown above is equivalent to the following command:

[para]
[example {
    set mytail [tail %AUTO% -partof $self]
}]
[para]

For [cmd snit::widget]s and [cmd snit::widgetadaptor]s, however, the
[cmd install]> command also queries [emph {the Tk option database}]
and initializes the component's options accordingly.  For consistency,
it's a good idea to get in the habit of using [cmd install] for all
components.


[para]
[emph {Are there any limitations on component names?}]
[para]



Yes.  [cmd snit::widget] and [cmd snit::widgetadaptor] have a special
component called the [var hull] component; thus, the name [var hull]
should be used for no other purpose.

[para]

Component names are in fact instance variable names, and so follow the
rules for [term {instance variables}].

[para]
[emph {Are there any limitations on component object names?}]
[para]

Yes.

Component objects which are Tk widgets or megawidgets must have valid
Tk window names.

Component objects which are not widgets or megawidgets must have
fully-qualified command names, i.e., names which include the full
namespace of the command.  Note that Snit always creates objects with
fully qualified names.

Second, component object names must be unique.  This is no problem for
widget components, since widget names are always unique; but consider
the following code:

[para]
[example {
    snit::type tail { ... }

    snit::type dog {
        delegate method wag to mytail

        constructor {} {
            install mytail using tail mytail
        }
    }
}]
[para]

This code uses the component name, "mytail", as the component object
name.  This is not good, and here's why: Snit instance code executes
in the Snit type's namespace.  In this case, the mytail component is
created in the ::dog:: namespace, and will thus have the name
::dog::mytail.

[para]

Now, suppose you create two dogs.  Both will have a mytail component
called ::dog::mytail.  In other words, you've got two dogs with one
tail between them.  This is very bad.  Here are a couple of ways to
avoid it:

[para]

First, if the component type is a [cmd snit::type] you can
specify %AUTO% as its name, and be guaranteed to get a unique name.
This is the safest thing to do:

[para]
[example {
    install mytail using tail %AUTO%
}]
[para]

If the component type isn't a [cmd snit::type] you can base the
component's object name on the type's name in some way:

[para]
[example {
     install mytail using tail $self.mytail
}]
[para]

This isn't as safe, but should usually work out okay.


[para]
[emph {Must I destroy the components I create?}]
[para]



That depends.  When a parent widget is destroyed, all child widgets
are destroyed automatically.

Thus, if your object is a [cmd snit::widget]

or [cmd snit::widgetadaptor] you don't need to destroy any components
that are widgets.

[para]

Any non-widget components, however, and all components of a

[cmd snit::type] object, must be destroyed explicitly.  This is true
whether you assign them a component name or not.

[para]
[example {
% snit::type dog {
    variable mytail

    constructor {args} {
        install mytail using tail %AUTO% -partof $self
        $self configurelist $args
    }

    destructor {
        $mytail destroy
    }
}
::dog
}]
[para]

Note that this code assumes that [cmd tail] is also a

[cmd snit::type]; if not, it might need to be destroyed in some other
way.


[para]
[emph {Can I expose a component's object command as part of my interface?}]
[para]

Yes, and there are two ways to do it.  The most appropriate way is
usually to use [sectref DELEGATION].  Delegation allows you to pass
the options and methods you specify along to particular components.
This effectively hides the components from the users of your type, and
ensures good encapsulation.

[para]

However, there are times when it's appropriate, not to mention
simpler, just to make the entire component part of your type's public
interface.

[para]
[emph {How do I expose a component's object command?}]
[para]


Use the [cmd expose] statement.  For example, suppose you're creating
a combobox megawidget which owns a listbox widget, and you want to
make the listbox's entire interface public.  You can do this:

[para]
[example {
snit::widget combobox {
     expose listbox

     constructor {args} {
         install listbox using listbox $win.listbox ....

         #...
     }

     #...
}

combobox .mycombo
.mycombo listbox configure -width 30
}]
[para]

The [cmd expose] statement exposes the named component by
defining a method of the same name.  The method's arguments are passed
along to the component.  Thus, the above code sets the listbox
component's "-width" to 30.

[para]

If called with no arguments, the method returns the component's object
name:

[para]
[example {
% .mycombo listbox
.mycombo.listbox
}]
[para]

Usually you'll let the method name be the same as the component name;
however, you can rename it if necessary.  The code in the following
listing exposes the same interface as the previous example:

[para]
[example {
snit::widget combobox {
     expose mylistbox as listbox

     constructor {args} {
         install mylistbox using listbox $win.mylistbox ....

         #...
     }

     #...
}
combobox .mycombo
.mycombo listbox configure -width 30
}]
[para]


[section DELEGATION]

[emph {What is delegation?}]
[para]



Delegation, simply put, is when you pass a task you've been given to
one of your assistants.  (You do have assistants, don't you?)  Snit
objects can do the same thing.  The following example shows one way in
which the [cmd dog] object can delegate its [cmd wag] method and its
[option -taillength] option to its [cmd tail] component.

[para] 
[example {
% snit::type dog {
    variable mytail

    option -taillength

    onconfigure -taillength {value} {
         $mytail configure -length $value
    }

    oncget -taillength {
         $mytail cget -length
    }

    constructor {args} {
        install mytail using tail %AUTO% -partof $self
        $self configurelist $args
    }

    method wag {} {
        $mytail wag
    }
}
::dog
% snit::type tail {
    option -length 5
    option -partof
    method wag {} { return "Wag, wag, wag."}
}
::tail
% dog spot -taillength 7
::spot
% spot cget -taillength
7
% spot wag
Wag, wag, wag.
}]
[para]

This is the hard way to do it, by it demonstrates what delegation is
all about.  See the following answers for the easy way to do it.

[para]

Note that the constructor calls the [method configurelist] method
[emph after] it creates its [cmd tail]; otherwise,

if [option -taillength] appeared in the list of [var args] we'd get an
error.

[para]
[emph {How can I delegate a method to a component object?}]
[para]



Delegation occurs frequently enough that Snit makes it easy. Any
method can be delegated to any component by placing a single

[cmd delegate] statement in the type definition.

(See [sectref COMPONENTS] for more information about component names.)

[para]

For example, here's a much better way to delegate the [cmd dog]
object's [cmd wag] method:

[para]
[example {
% snit::type dog {
    delegate method wag to mytail

    constructor {args} {
        install mytail using tail %AUTO% -partof $self
        $self configurelist $args
    }
}
::dog
% snit::type tail {
    option -length 5
    option -partof
    method wag {} { return "Wag, wag, wag."}
}
::tail
% dog spot
::spot
% spot wag
Wag, wag, wag.
}]
[para]

This code has the same affect as the code shown under the previous
question: when a [cmd dog]'s [cmd wag] method is called, the call and
its arguments are passed along automatically to the [cmd tail] object.

[para]

Note that when a component is mentioned in a [cmd delegate] statement,
the component's instance variable is defined implicitly.

[para]

Note also that you can define a method name using the [cmd method]
statement, or you can define it using [cmd delegate]; you can't do
both.

[para]
[emph {Can I delegate to a method with a different name?}]
[para]



Suppose the [cmd tail] object has a [cmd wiggle] method instead of a
[cmd wag] method, and you want to delegate the [cmd dog]'s [cmd wag]
method to the [cmd tail]'s [cmd wiggle] method.  It's easily done:

[para]
[example {
    % snit::type dog {
	delegate method wag to mytail as wiggle

	constructor {args} {
	    install mytail using tail %AUTO% -partof $self
	    $self configurelist $args
	}
    }
    ::dog
    % snit::type tail {
	option -length 5
	option -partof
	method wiggle {} { return "Wag, wag, wag."}
    }
    ::tail
    % dog spot
    ::spot
    % spot wag
    Wag, wag, wag.
}]
[para]


[emph {Can I delegate to a method with additional arguments?}]
[para]

Suppose the [cmd tail] object has a [method wag] method
that takes as an argument the number of times the tail should be
wagged.  You want to delegate the [cmd dog]'s [method wag]
method to the [cmd tail]'s [method wag] method, specifying
that the tail should be wagged three times.  It's easily done:

[para]
[example {
    % snit::type dog {
	delegate method wag to mytail as {wag 3}

	constructor {args} {
	    install mytail using tail %AUTO% -partof $self
	    $self configurelist $args
	}
    }
    ::dog
    % snit::type tail {
	option -length 5
	option -partof
	method wag {count} {
	    return [string repeat "Wag " $count]
	}
    }
    ::tail
    % dog spot
    ::spot
    % spot wag
    Wag Wag Wag 
    %
}]
[para]

[emph {How can I delegate an option to a component object?}]
[para]



The first question in this section (see [sectref DELEGATION]) shows
one way to delegate an option to a component; but this pattern occurs
often enough that Snit makes it easy.  For example, every [cmd tail]
object has a [option -length] option; we want to allow the creator of
a [cmd dog] object to set the tail's length.  We can do this:

[para]
[example {
% snit::type dog {
    delegate option -length to mytail

    constructor {args} {
        install mytail using tail %AUTO% -partof $self
        $self configurelist $args
    }
}
::dog
% snit::type tail {
    option -partof
    option -length 5
}
::tail
% dog spot -length 7
::spot
% spot cget -length
7
}]
[para]

This produces nearly the same result as the [cmd oncget] and

[cmd onconfigure] handlers shown under the first question in this
section: whenever a [cmd dog] object's [option -length] option is set
or retrieved, the underlying [cmd tail] object's option is set or
retrieved in turn.

[para]

Note that you can define an option name using the [cmd option]
statement, or you can define it using [cmd delegate]; you can't do
both.

[para]
[emph {Can I delegate to an option with a different name?}]
[para]



In the previous answer we delegated the [cmd dog]'s [option -length]
option down to its [cmd tail].  This is, of course, wrong.  The dog
has a length, and the tail has a length, and they are different.  What
we'd really like to do is give the [cmd dog] a [option -taillength]
option, but delegate it to the [cmd tail]'s [option -length] option:

[para]
[example {
% snit::type dog {
    delegate option -taillength to mytail as -length

    constructor {args} {
        set mytail [tail %AUTO% -partof $self]
        $self configurelist $args
    }
}
::dog
% snit::type tail {
    option -partof
    option -length 5
}
::tail
% dog spot -taillength 7
::spot
% spot cget -taillength
7
}]
[para]

[emph {How can I delegate any unrecognized method or option to a component object?}]
[para]



It may happen that a Snit object gets most of its behavior from one of
its components.  This often happens with [cmd snit::widgetadaptors],
for example, where we wish to slightly the modify the behavior of an
existing widget.  To carry on with our [cmd dog] example, however, suppose
that we have a [cmd snit::type] called [cmd animal] that implements a
variety of animal behaviors--moving, eating, sleeping, and so forth.

We want our [cmd dog] objects to inherit these same behaviors, while
adding dog-like behaviors of its own.

Here's how we can give a [cmd dog] methods and options of its own
while delegating all other methods and options to its [cmd animal]
component:

[para]
[example {
% snit::type dog {
    delegate option * to animal
    delegate method * to animal

    option -akc 0

    constructor {args} {
        install animal using animal %AUTO% -name $self
        $self configurelist $args
    }

    method wag {} {
        return "$self wags its tail"
    }
}
::dog
}]
[para]

That's it.  A [cmd dog] is now an [cmd animal] which has a

[option -akc] option and can [cmd wag] its tail.

[para]

Note that we don't need to specify the full list of method names or
option names which [cmd animal] will receive.

It gets anything [cmd dog] doesn't recognize--and if it doesn't
recognize it either, it will simply throw an error, just as it should.


[para]
[emph {How can I delegate all but certain methods or options to a component?}]
[para]

In the previous answer, we said that every [cmd dog] is
an [cmd animal] by delegating all unknown methods and options to the
[var animal] component. But what if the [cmd animal] type has some
methods or options that we'd like to suppress?

[para]

One solution is to explicitly delegate all the options and methods,
and forgo the convenience of [cmd {delegate method *}] and
[cmd {delegate option *}].  But if we wish to suppress only a few
options or methods, there's an easier way:

[para]
[example {
    % snit::type dog {
	delegate option * to animal except -legs
	delegate method * to animal except {fly climb}

	# ...

	constructor {args} {
	    install animal using animal %AUTO% -name $self -legs 4
	    $self configurelist $args
	}

	# ...
    }
    ::dog
    %
}]
[para]

Dogs have four legs, so we specify that explicitly when we create the
[var animal] component, and explicitly exclude [option -legs] from the
set of delegated options.  Similarly, dogs can neither fly nor climb,
so we exclude those [cmd animal] methods as shown.


[section WIDGETS]

[emph {What is a snit::widget?}]
[para]



A [cmd snit::widget] is the Snit version of what Tcl programmers
usually call a [term megawidget]: a widget-like object usually
consisting of one or more Tk widgets all contained within a Tk frame.

[para]

A [cmd snit::widget] is also a special kind of [cmd snit::type].  Just
about everything in this FAQ list that relates to [cmd snit::types]
also applies to [cmd snit::widgets].

[para]
[emph {How do I define a snit::widget?}]
[para]



[cmd snit::widgets] are defined using the [cmd snit::widget] command,
just as [cmd snit::types] are defined by the [cmd snit::type] command.

[para]

The body of the definition can contain all of the same kinds of
statements, plus a couple of others which will be mentioned below.

[para]
[emph {How do snit::widgets differ from snit::types?}]
[para]



[list_begin bullet]
[bullet]

The name of an instance of a [cmd snit::type] can be any valid Tcl
command name, in any namespace.

The name of an instance of a [cmd snit::widget] must be a valid Tk
widget name, and its parent widget must already exist.


[bullet]

An instance of a [cmd snit::type] can be destroyed by calling

its [cmd destroy] method.  Instances of a [cmd snit::widget] have no
destroy method; use the Tk [cmd destroy] command instead.


[bullet]

Every instance of a [cmd snit::widget] has one predefined component
called its [var hull] component.

The hull is a Tk [cmd frame] or [cmd toplevel] widget; any other
widgets created as part of the [cmd snit::widget] will usually be
contained within this frame.

[bullet]

[cmd snit::widget]s can have their options receive default values from
[emph {the Tk option database}].

[list_end]


[para]
[emph {What is a hull component?}]
[para]

Snit can't create a Tk widget object; only Tk can do that.

Thus, every instance of a [cmd snit::widget] must be wrapped around a
genuine Tk widget; this Tk widget is called the [term {hull component}].

Snit effectively piggybacks the behavior you define (methods, options,
and so forth) on top of the hull component so that the whole thing
behaves like a standard Tk widget.

[para]

For [cmd snit::widget]s the hull component must be a Tk [cmd frame] or
[cmd toplevel] widget; any other widgets created as part of
the [cmd snit::widget] will be contained within this frame or
toplevel.

[para]

[cmd snit::widgetadaptor]s differ from [cmd snit::widget]s chiefly in
that any kind of widget can be used as the hull component; see
[sectref {WIDGET ADAPTORS}].


[para]
[emph {How can I set the hull type for a snit::widget?}]
[para]

A [cmd snit::widget]'s hull component will usually be a Tk [cmd frame]
widget; however, it may also be a [cmd toplevel] widget.  You can
explicitly choose one or the other by including the [cmd hulltype]
command in the widget definition:

[para]
[example {
    snit::widget mytoplevel {
	hulltype toplevel

	# ...
    }
}]
[para]

If no [cmd hulltype] command appears, the hull will be a [cmd frame].


[para]
[emph {How should I name widgets which are components of a snit::widget?}]
[para]



Every widget, whether a genuine Tk widget or a Snit megawidget, has to
have a valid Tk window name.  When a [cmd snit::widget] is first
created, its instance name, [var self], is a Tk window name;

however, if the [cmd snit::widget] is used as the hull component by a
[cmd snit::widgetadaptor] its instance name will be changed to
something else.  For this reason, every [cmd snit::widget] method,
constructor, destructor, and so forth is passed another implicit
argument, [var win], which is the window name of the megawidget.  Any
children must be named using [var win] as the root.

[para]

Thus, suppose you're writing a toolbar widget, a frame consisting of a
number of buttons placed side-by-side.  It might look something like
this:

[para]
[example {
snit::widget toolbar {
    delegate option * to hull

    constructor {args} {
        button $win.open -text Open -command [mymethod open]
        button $win.save -text Save -command [mymethod save]

        # ....

        $self configurelist $args

    }
}
}]
[para]

See also the question on renaming objects, toward the top of this
file.

[para]

[section WIDGETADAPTORS]

[emph {What is a snit::widgetadaptor?}]
[para]



A [cmd snit::widgetadaptor] is a kind of [cmd snit::widget].  Whereas
a [cmd snit::widget]'s hull is automatically created and is always a
Tk frame, a [cmd snit::widgetadaptor] can be based on any Tk
widget--or on any Snit megawidget, or even (with luck) on megawidgets
defined using some other package.

[para]

It's called a [term {widget adaptor}] because it allows you to take an
existing widget and customize its behavior.

[para]
[emph {How do I define a snit::widgetadaptor?}]
[para]



Using the [cmd snit::widgetadaptor] command.  The definition for a
[cmd snit::widgetadaptor] looks just like that for a [cmd snit::type]
or [cmd snit::widget], except that the constructor must create and
install the hull component.

[para]

For example, the following code creates a read-only text widget by the
simple device of turning its [method insert] and [method delete]
methods into no-ops.  Then, we define new methods, [method ins] and
[method del],

which get delegated to the hull component as [method insert] and
[method delete].  Thus, we've adapted the text widget and given it new
behavior while still leaving it fundamentally a text widget.

[para]
[example {
% ::snit::widgetadaptor rotext {

    constructor {args} {
        # Create the text widget; turn off its insert cursor
        installhull using text -insertwidth 0

        # Apply any options passed at creation time.
        $self configurelist $args
    }

    # Disable the text widget's insert and delete methods, to
    # make this readonly.
    method insert {args} {}
    method delete {args} {}

    # Enable ins and del as synonyms, so the program can insert and
    # delete.
    delegate method ins to hull as insert
    delegate method del to hull as delete
    
    # Pass all other methods and options to the real text widget, so
    # that the remaining behavior is as expected.
    delegate method * to hull
    delegate option * to hull
}
::rotext
}]
[para]

The most important part is in the constructor.

Whereas [cmd snit::widget] creates the hull for you,

[cmd snit::widgetadaptor] cannot -- it doesn't know what kind of
widget you want.  So the first thing the constructor does is create
the hull component (a Tk text widget in this case), and then installs
it using the [cmd installhull] command.

[para]

[emph Note:] There is no instance command until you create one by
installing a hull component.  Any attempt to pass methods to $self
prior to calling [cmd installhull] will fail.


[para]
[emph {Can I adapt a widget created by someone else?}]
[para]

Yes.

[para]

At times, it can be convenient to adapt a widget created by another
party.  For example, the Bwidget [cmd PagesManager] widget manages a
set of [cmd frame] widgets, only one of which is visible at a time.
The application chooses which [cmd frame] is visible.

These [cmd frame]s are created by the [cmd PagesManager] itself, using
its [method add] method.

[para]

In a case like this, the Tk widget will already exist when the
[cmd snit::widgetadaptor] is created.  Snit provides an alternate form
of the [cmd installhull] command for this purpose:

[para]
[example {
    snit::widgetadaptor pageadaptor {
	constructor {args} {
	    # The widget already exists; just install it.
	    installhull $win

	    # ...
	}
    }
}]

[section {THE TK OPTION DATABASE}]

[emph {What is the Tk option database?}]
[para]


The Tk option database is a database of default option values
maintained by Tk itself; every Tk application has one.  The concept of
the option database derives from something called the X Windows
resource database; however, the option database is available in every
Tk implementation, including those which do not use the X Windows
system (e.g., Microsoft Windows).

[para]

Full details about the Tk option database are beyond the scope of this
document; both [emph {Practical Programming in Tcl and Tk}] by Welch,
Jones, and Hobbs, and [emph {Effective Tcl/Tk Programming}] by
Harrison and McClennan., have good introductions to it.

[para]

Snit is implemented so that most of the time it will simply do the
right thing with respect to the option database, provided that the
widget developer does the right thing by Snit.  The body of this
section goes into great deal about what Snit requires.  The following
is a brief statement of the requirements, for reference.

[para]

[list_begin bullet]

[bullet]

If the widget's default widget class is not what is desired, set it
explicitly using the [cmd widgetclass] statement in the widget
definition.

[bullet]

When defining or delegating options, specify the resource and class
names explicitly when necessary.

[bullet]

Use the [cmd {installhull using}] command to create and install the
hull for [cmd snit::widgetadaptor]s.

[bullet]

Use the [cmd install] command to create and install all
other components.

[list_end]
[para]

The interaction of Tk widgets with the option database is a complex
thing; the interaction of Snit with the option database is even more
so, and repays attention to detail.

[para]
[emph {Do snit::types use the Tk option database?}]
[para]


No, they don't; querying the option database requires a Tk window
name, and [cmd snit::type]s don't have one.

[para]

Only [cmd snit::widget]s and [cmd snit::widgetadaptor]s query the
option database.


[para]
[emph {What is my snit::widget's widget class?}]
[para]


Every Tk widget has a "widget class": a name that is used when adding
option settings to the database.  For Tk widgets, the widget class is
the same as the widget command name with an initial capital.  For
example, the widget class of the Tk [cmd button] widget is "Button".

[para]

Similarly, the widget class of a [cmd snit::widget] defaults to the
unqualified type name with the first letter capitalized.  For example,
the widget class of

[para]
[example {
    snit::widget ::mylibrary::scrolledText { ... }
}]
[para]

is "ScrolledText".

[para]

The widget class can also be set explicitly using the
[cmd widgetclass] statement within the [cmd snit::widget] definition:

[para]
[example {
    snit::widget ::mylibrary::scrolledText {
	widgetclass Text

	# ...
    }
}]
[para]

The above definition says that a [cmd scrolledText] megawidget has the
same widget class as an ordinary [cmd text] widget.  This might or
might not be a good idea, depending on how the rest of the megawidget
is defined, and how its options are delegated.

[para]
[emph {What is my snit::widgetadaptor's widget class?}]
[para]


The widget class of a [cmd snit::widgetadaptor] is just the widget
class of its hull widget; Snit has no control over this.

[para]

Note that the widget class can be changed only for [cmd frame] and
[cmd toplevel] widgets, which is why these are the valid hull types
for [cmd snit::widget]s.

[para]

Try to use [cmd snit::widgetadaptor]s only to make small modifications
to another widget's behavior.  Then, it will usually not make sense to
change the widget's widget class anyway.

[para]
[emph {What are option resource and class names?}]
[para]


Every Tk widget option has three names: the option name, the resource
name, and the class name.

The option name begins with a hyphen and is all lowercase; it's used
when creating widgets, and with the [cmd configure] and [cmd cget]
commands.

[para]

The resource and class names are used to initialize option
default values by querying the option database.
The resource name is usually just the option
name minus the hyphen, but may contain uppercase letters at word
boundaries; the class name is usually just the resource
name with an initial capital, but not always.  For example, here are
the option, resource, and class names for several Tk [cmd text]
widget options:

[para]
[example {
    -background         background         Background 
    -borderwidth        borderWidth        BorderWidth 
    -insertborderwidth  insertBorderWidth  BorderWidth 
    -padx               padX               Pad 
}]
[para]

As is easily seen, sometimes the resource and class names can be
inferred from the option name, but not always.

[para]
[emph {What are the resource and class names for my megawidget's options?}]
[para]


For options implicitly delegated to a component using

[cmd {delegate option *}], the resource and class names will be
exactly those defined by the component.  The [cmd configure] method
returns these names, along with the option's default and current
values:

[para]
[example {
    % snit::widget mytext {
	delegate option * to text

	constructor {args} {
	    install text using text .text
	    # ...
	}

	# ...
    }
    ::mytext
    % mytext .text
    .text
    % .text configure -padx
    -padx padX Pad 1 1
    %
}]
[para]

For all other options (whether locally defined or explicitly
delegated), the resource and class names can be defined explicitly, or
they can be allowed to have default values.

[para]

By default, the resource name is just the option name minus the
hyphen; the the class name is just the option name with an initial
capital letter.  For example, suppose we explicitly delegate "-padx":

[para]
[example {
    % snit::widget mytext {
	option -myvalue 5

	delegate option -padx to text
	delegate option * to text

	constructor {args} {
	    install text using text .text
	    # ...
	}

	# ...
    }
    ::mytext
    % mytext .text
    .text
    % .text configure -mytext
    -mytext mytext Mytext 5 5
    % .text configure -padx
    -padx padx Padx 1 1
    %
}]
[para]

Here the resource and class names are chosen using the default rules.
Often these rules are sufficient, but in the case of "-padx" we'd most
likely prefer that the option's resource and class names are the same
as for the built-in Tk widgets.  This is easily done:

[para]
[example {
    % snit::widget mytext {
	delegate option {-padx padX Pad} to text

	# ...
    }
    ::mytext
    % mytext .text
    .text
    % .text configure -padx
    -padx padX Pad 1 1
    %
}]


[para]
[emph {How does Snit initialize my megawidget's locally-defined options?}]
[para]


The option database is queried for each of the megawidget's
locally-defined options, using the option's resource and class name.
If the result isn't "", then it replaces the default value given in
widget definition.  In either case, the default can be overriden by
the caller.  For example,

[para]
[example {
    option add *Mywidget.texture pebbled

    snit::widget mywidget {
	option -texture smooth
	# ...
    }

    mywidget .mywidget -texture greasy
}]
[para]

Here, "-texture" would normally default to "smooth", but because of
the entry added to the option database it defaults to "pebbled".
However, the caller has explicitly overridden the default, and so the
new widget will be "greasy".

[para]
[emph {How does Snit initialize delegated options?}]
[para]


That depends on whether the options are delegated to the hull, or to
some other component.

[para]
[emph {How does Snit initialize options delegated to the hull?}]
[para]


A [cmd snit::widget]'s hull is a widget, and given that its class has
been set it is expected to query the option database for itself.  The
only exception concerns options that are delegated to it with a
different name.  Consider the following code:

[para]
[example {
    option add *Mywidget.borderWidth 5
    option add *Mywidget.relief sunken
    option add *Mywidget.hullbackground red
    option add *Mywidget.background green

    snit::widget mywidget {
	delegate option -borderwidth to hull
	delegate option -hullbackground to hull as -background
	delegate option * to hull
	# ...
    }

    mywidget .mywidget

    set A [.mywidget cget -relief]
    set B [.mywidget cget -hullbackground]
    set C [.mywidget cget -background]
    set D [.mywidget cget -borderwidth]
}]
[para]

The question is, what are the values of variables A, B, C and D?

[para]

The value of A is "sunken".  The hull is a Tk frame which has been
given the widget class "Mywidget"; it will automatically query the
option database and pick up this value.  Since the -relief option is
implicitly delegated to the hull, Snit takes no action.

[para]

The value of B is "red".  The hull will automatically pick up the
value "green" for its -background option, just as it picked up the
-relief value.  However, Snit knows that -hullbackground is mapped to
the hull's -background option; hence, it queries the option database
for -hullbackground and gets "red" and updates the hull accordingly.

[para]

The value of C is also "red", because -background is implicitly
delegated to the hull; thus, retrieving it is the same as retrieving
-hullbackground.  Note that this case is unusual; the -background
option should probably have been excluded using the delegate
statement's "except" clause, or (more likely) delegated to some other
component.

[para]

The value of D is "5", but not for the reason you think.  Note that as
it is defined above, the resource name for -borderwidth defaults to
"borderwidth", whereas the option database entry is "borderWidth", in
accordance with the standard Tk naming for this option.  As with
-relief, the hull picks up its own "-borderwidth" option before Snit
does anything.  Because the option is delegated under its own name,
Snit assumes that the correct thing has happened, and doesn't worry
about it any further.  To avoid confusion, the -borderwidth option
should have been delegated like this:

[para]
[example {
    delegate option {-borderwidth borderWidth BorderWidth} to hull
}]
[para]

For [cmd snit::widgetadaptor]s, the case is somewhat altered.  Widget
adaptors retain the widget class of their hull, and the hull is not
created automatically by Snit.  Instead, the [cmd snit::widgetadaptor]
must call [cmd installhull] in its constructor.  The normal way
to do this is as follows:

[para]
[example {
    snit::widgetadaptor mywidget {
	# ...
	constructor {args} {
	    # ...
	    installhull using text -foreground white
	    #
	}
	#...
    }
}]
[para]

In this case, the [cmd installhull] command will create the hull using
a command like this:

[para]
[example {
    set hull [text $win -foreground white]
}]
[para]

The hull is a [cmd text] widget, so its widget class is "Text".  Just
as with [cmd snit::widget] hulls, Snit assumes that it will pick up
all of its normal option values automatically, without help from Snit.
Options delegated from a different name are initialized from the
option database in the same way as described above.

[para]

In earlier versions of Snit, [cmd snit::widgetadaptor]s were expected
to call [cmd installhull] like this:

[para]
[example {
    installhull [text $win -foreground white]
}]
[para]

This form still works--but Snit will not query the option database as
described above.

[para]
[emph {How does Snit initialize options delegated to other components?}]
[para]


For hull components, Snit assumes that Tk will do most of the work
automatically.  Hull components are somewhat more complicated, because
they are matched against the option database twice.

[para]

A component widget remains a widget still, and is therefore
initialized from the option database in the usual way.  A [cmd text]
widget remains a [cmd text] widget whether it is a component of a
megawidget or not, and will be created as such.

[para]

But then, the option database is queried for all options delegated to
the component, and the component is initialized accordingly--provided
that the [cmd install] command is used to create it.

[para]

Before option database support was added to Snit, the usual way to
create a component was to simply create it in the constructor and
assign its command name to the component variable:

[para]
[example {
    snit::widget mywidget {
	delegate option -background to myComp

	constructor {args} {
	    set myComp [text $win.text -foreground black]
	}
    }
}]
[para]

The drawback of this method is that Snit has no opportunity to
initialize the component properly.  Hence, the following approach is
now used:

[para]
[example {
    snit::widget mywidget {
	delegate option -background to myComp

	constructor {args} {
	    install myComp using text $win.text -foreground black
	}
    }
}]
[para]

The [cmd install] command does the following:

[para]
[list_begin bullet]

[bullet]

Builds a list of the options explicitly included in the [cmd install]
command--in this case, -foreground.

[bullet]

Queries the option database for all options delegated explicitly to
the named component.

[bullet]

Creates the component using the specified command, after inserting
into it a list of options and values read from the option database.
Thus, the explicitly include options (-foreground) will override
anything read from the option database.

[bullet]

If the widget definition implicitly delegated options to the component
using [cmd {delegate option *}], then Snit calls the newly created
component's [cmd configure] method to receive a list of all of the
component's options.  From this Snit builds a list of options
implicitly delegated to the component which were not explicitly
included in the [cmd install] command.  For all such options, Snit
queries the option database and configures the component accordingly.

[list_end]

You don't really need to know all of this; just use [cmd install] to
install your components, and Snit will try to do the right thing.

[para]
[emph {What happens if I install a non-widget as a component of widget?}]
[para]

A [cmd snit::type] never queries the option database.

However, a [cmd snit::widget] can have non-widget components.  And if
options are delegated to those components, and if the [cmd install]
command is used to install those components, then they will be
initialized from the option database just as widget components are.

[para]

However, when used within a megawidget, [cmd install] assumes that the
created component uses a reasonably standard widget-like creation
syntax.  If it doesn't, don't use [cmd install].

[para]


[para]
[emph {Can I adapt widgets from other megawidget packages?}]
[para]

Yes.

[para]

However, you need to be very careful about making sure the
[cmd bindtags] are done properly.  There's no way for Snit to take
into account all the possible weird things other megawidget frameworks
might do wrong.

[para]

For example, some widgets in BWidgets place their own <Destroy>
binding not on a separate bind-tag, but on the widget itself. When
used as the hull of a [cmd snit::widgetadaptor] this causes them to be
called before Snit, removing the widget command. A previous version of
Snit was tripped by this and threw errors because it tried to operate
on and with an already deleted widget command. Snit is now able to
deal with this, despite the fact that the ultimate cause is at least
bad behaviour of Bwidget, possibly even a bug. This however does not
preclude that there might be other issues lurking.


[keywords class {object oriented} object C++]
[keywords {Incr Tcl} BWidget]
[keywords widget adaptors {widget adaptors} {mega widget}]
[manpage_end]
