[comment {-*- tcl -*- doctools manpage}] [manpage_begin snit n 2.3.2] [keywords adaptors] [keywords BWidget] [keywords C++] [keywords class] [keywords {Incr Tcl}] [keywords {mega widget}] [keywords object] [keywords {object oriented}] [keywords Snit] [keywords type] [keywords widget] [keywords {widget adaptors}] [copyright {2003-2009, by William H. Duquette}] [moddesc {Snit's Not Incr Tcl, OO system}] [titledesc {Snit's Not Incr Tcl}] [category {Programming tools}] [require Tcl 8.5] [require snit [opt 2.3.2]] [description] [para] Snit is a pure Tcl object and megawidget system. It's unique among Tcl object systems in that it's based not on inheritance but on delegation. Object systems based on inheritance only allow you to inherit from classes defined using the same system, which is limiting. In Tcl, an object is anything that acts like an object; it shouldn't matter how the object was implemented. Snit is intended to help you 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 [package Tk] widget, an [package {Incr Tcl}] object, a [package BWidget] or almost anything else. [para] This man page is intended to be a reference only; see the accompanying [cmd snitfaq] for a gentler, more tutorial introduction to Snit concepts. [section {SNIT VERSIONS}] This man page covers both Snit 2.2 and Snit 1.3. The primary difference between the two versions is simply that Snit 2.2 contains speed optimizations based on new features of Tcl 8.5; Snit 1.3 supports all of Tcl 8.3, 8.4 and Tcl 8.5. There are a few minor inconsistencies; they are flagged in the body of the man page with the label "Snit 1.x Incompatibility"; they are also discussed in the [cmd snitfaq]. [para] [section REFERENCE] [subsection {Type and Widget Definitions}] Snit provides the following commands for defining new types: [list_begin definitions] [call [cmd snit::type] [arg name] [arg definition]] Defines a new abstract data type called [arg name]. If [arg name] is not a fully qualified command name, it is assumed to be a name in the namespace in which the [cmd snit::type] command was called (usually the global namespace). It returns the fully qualified name of the new type. [para] The type name is then a command that is used to create objects of the new type, along with other activities. [para] The [cmd snit::type] [arg definition] block is a script that may contain the following definitions: [list_begin definitions] [call [cmd typevariable] [arg name] [opt [const -array]] [opt [arg value]]] Defines a type variable with the specified [arg name], and optionally the specified [arg value]. Type variables are shared by all instances of the type. If the [const -array] option is included, then [arg value] should be a dictionary; it will be assigned to the variable using [cmd "array set"]. [call [cmd typemethod] [arg name] [arg arglist] [arg body]] Defines a type method, a subcommand of the new type command, with the specified name, argument list, and body. The [arg arglist] is a normal Tcl argument list and may contain default arguments and the [var args] argument; however, it may not contain the argument names [var type], [var self], [var selfns], or [var win]. [para] The variable [var type] is automatically defined in the [arg body] to the type's fully-qualified name. In addition, type variables are automatically visible in the [arg body] of every type method. [para] If the [arg name] consists of two or more tokens, Snit handles it specially: [example { typemethod {a b} {arg} { puts "Got $arg" } }] This statement implicitly defines a type method called [const a] which has a subcommand [const b]. [const b] is called like this: [example { $type a b "Hello, world!" }] [const a] may have any number of subcommands. This makes it possible to define a hierarchical command structure; see [cmd method], below, for more examples. [para] Type methods can call commands from the namespace in which the type is defined without importing them, e.g., if the type name is [cmd ::parentns::typename], then the type's type methods can call [cmd ::parentns::someproc] just as [cmd someproc]. [emph {Snit 1.x Incompatibility:}] This does not work in Snit 1.x, as it depends on [cmd "namespace path"], a new command in Tcl 8.5. [para] [emph {Snit 1.x Incompatibility:}] In Snit 1.x, the following following two calls to this type method are equivalent: [example { $type a b "Hello, world!" $type {a b} "Hello, world!" }] In Snit 2.2, the second form is invalid. [call [cmd typeconstructor] [arg body]] The type constructor's [arg body] is executed once when the type is first defined; it is typically used to initialize array-valued type variables and to add entries to [sectref {The Tk Option Database}]. [para] The variable [var type] is automatically defined in the [arg body], and contains the type's fully-qualified name. In addition, type variables are automatically visible in the [arg body] of the type constructor. [para] A type may define at most one type constructor. [para] The type constructor can call commands from the namespace in which the type is defined without importing them, e.g., if the type name is [cmd ::parentns::typename], then the type constructor can call [cmd ::parentns::someproc] just as [cmd someproc]. [emph {Snit 1.x Incompatibility:}] This does not work in Snit 1.x, as it depends on [cmd "namespace path"], a new command in Tcl 8.5. [call [cmd variable] [arg name] [opt [const -array]] [opt [arg value]]] Defines an instance variable, a private variable associated with each instance of this type, and optionally its initial value. If the [const -array] option is included, then [arg value] should be a dictionary; it will be assigned to the variable using [cmd "array set"]. [call [cmd method] [arg name] [arg arglist] [arg body]] Defines an instance method, a subcommand of each instance of this type, with the specified name, argument list and body. The [arg arglist] is a normal Tcl argument list and may contain default arguments and the [var args] argument. [para] The method is implicitly passed the following arguments as well: [var type], which contains the fully-qualified type name; [var self], which contains the current instance command name; [var selfns], which contains the name of the instance's private namespace; and [var win], which contains the original instance name. Consequently, the [arg arglist] may not contain the argument names [const type], [const self], [const selfns], or [const win]. [para] An instance method defined in this way is said to be [term {locally defined}]. [para] Type and instance variables are automatically visible in all instance methods. If the type has locally defined options, the [var options] array is also visible. [para] If the [arg name] consists of two or more tokens, Snit handles it specially: [example { method {a b} {} { ... } }] This statement implicitly defines a method called [const a] which has a subcommand [const b]. [const b] is called like this: [example { $self a b "Hello, world!" }] [const a] may have any number of subcommands. This makes it possible to define a hierarchical command structure: [example {% snit::type dog { method {tail wag} {} {return "Wag, wag"} method {tail droop} {} {return "Droop, droop"} } ::dog % dog spot ::spot % spot tail wag Wag, wag % spot tail droop Droop, droop % }] What we've done is implicitly defined a "tail" method with subcommands "wag" and "droop". Consequently, it's an error to define "tail" explicitly. [para] Methods can call commands from the namespace in which the type is defined without importing them, e.g., if the type name is [cmd ::parentns::typename], then the type's methods can call [cmd ::parentns::someproc] just as [cmd someproc]. [emph {Snit 1.x Incompatibility:}] This does not work in Snit 1.x, as it depends on [cmd "namespace path"], a new command in Tcl 8.5. [para] [emph {Snit 1.x Incompatibility:}] In Snit 1.x, the following following two calls to this method are equivalent: [example { $self a b "Hello, world!" $self {a b} "Hello, world!" }] In Snit 2.2, the second form is invalid. [call [cmd option] [arg namespec] [opt [arg defaultValue]]] [call [cmd option] [arg namespec] [opt [arg options...]]] Defines an option for instances of this type, and optionally gives it an initial value. The initial value defaults to the empty string if no [arg defaultValue] is specified. [para] An option defined in this way is said to be [term {locally defined}]. [para] The [arg namespec] is a list defining the option's name, resource name, and class name, e.g.: [example { option {-font font Font} {Courier 12} }] The option name must begin with a hyphen, and must not contain any upper case letters. The resource name and class name are optional; if not specified, the resource name defaults to the option name, minus the hyphen, and the class name defaults to the resource name with the first letter capitalized. Thus, the following statement is equivalent to the previous example: [example { option -font {Courier 12} }] See [sectref {The Tk Option Database}] for more information about resource and class names. [para] Options are normally set and retrieved using the standard instance methods [method configure] and [method cget]; within instance code (method bodies, etc.), option values are available through the [var options] array: [example { set myfont $options(-font) }] If the type defines any option handlers (e.g., [const -configuremethod]), then it should probably use [method configure] and [method cget] to access its options to avoid subtle errors. [para] The [cmd option] statement may include the following options: [list_begin definitions] [def "[const -default] [arg defvalue]"] Defines the option's default value; the option's default value will be "" otherwise. [def "[const -readonly] [arg flag]"] The [arg flag] can be any Boolean value recognized by Tcl. If [arg flag] is true, then the option is read-only--it can only be set using [method configure] or [method configurelist] at creation time, i.e., in the type's constructor. [def "[const -type] [arg type]"] Every locally-defined option may define its validation type, which may be either the name of a validation type or a specification for a validation subtype [para] For example, an option may declare that its value must be an integer by specifying [cmd snit::integer] as its validation type: [example { option -number -type snit::integer }] It may also declare that its value is an integer between 1 and 10 by specifying a validation subtype: [example { option -number -type {snit::integer -min 1 -max 10} }] If a validation type or subtype is defined for an option, then it will be used to validate the option's value whenever it is changed by the object's [method configure] or [method configurelist] methods. In addition, all such options will have their values validated automatically immediately after the constructor executes. [para] Snit defines a family of validation types and subtypes, and it's quite simple to define new ones. See [sectref "Validation Types"] for the complete list, and [sectref "Defining Validation Types"] for an explanation of how to define your own. [def "[const -cgetmethod] [arg methodName]"] Every locally-defined option may define a [const -cgetmethod]; it is called when the option's value is retrieved using the [method cget] method. Whatever the method's [arg body] returns will be the return value of the call to [method cget]. [para] The named method must take one argument, the option name. For example, this code is equivalent to (though slower than) Snit's default handling of [cmd cget]: [example { option -font -cgetmethod GetOption method GetOption {option} { return $options($option) } }] Note that it's possible for any number of options to share a [const -cgetmethod]. [def "[const -configuremethod] [arg methodName]"] Every locally-defined option may define a [const -configuremethod]; it is called when the option's value is set using the [method configure] or [method configurelist] methods. It is the named method's responsibility to save the option's value; in other words, the value will not be saved to the [var options()] array unless the method saves it there. [para] The named method must take two arguments, the option name and its new value. For example, this code is equivalent to (though slower than) Snit's default handling of [cmd configure]: [example { option -font -configuremethod SetOption method SetOption {option value} { set options($option) $value } }] Note that it's possible for any number of options to share a single [const -configuremethod]. [def "[const -validatemethod] [arg methodName]"] Every locally-defined option may define a [const -validatemethod]; it is called when the option's value is set using the [method configure] or [method configurelist] methods, just before the [const -configuremethod] (if any). It is the named method's responsibility to validate the option's new value, and to throw an error if the value is invalid. [para] The named method must take two arguments, the option name and its new value. For example, this code verifies that [const -flag]'s value is a valid Boolean value: [example { option -font -validatemethod CheckBoolean method CheckBoolean {option value} { if {![string is boolean -strict $value]} { error "option $option must have a boolean value." } } }] Note that it's possible for any number of options to share a single [const -validatemethod]. [list_end] [call [cmd constructor] [arg arglist] [arg body]] The constructor definition specifies a [arg body] of code to be executed when a new instance is created. The [arg arglist] is a normal Tcl argument list and may contain default arguments and the [var args] argument. [para] As with methods, the arguments [var type], [var self], [var selfns], and [var win] are defined implicitly, and all type and instance variables are automatically visible in its [arg body]. [para] If the [arg definition] doesn't explicitly define the constructor, Snit defines one implicitly. If the type declares at least one option (whether locally or by delegation), the default constructor will be defined as follows: [example { constructor {args} { $self configurelist $args } }] For standard Tk widget behavior, the argument list should be the single name [const args], as shown. [para] If the [arg definition] defines neither a constructor nor any options, the default constructor is defined as follows: [example { constructor {} {} }] As with methods, the constructor can call commands from the namespace in which the type is defined without importing them, e.g., if the type name is [cmd ::parentns::typename], then the constructor can call [cmd ::parentns::someproc] just as [cmd someproc]. [emph {Snit 1.x Incompatibility:}] This does not work in Snit 1.x, as it depends on [cmd "namespace path"], a new command in Tcl 8.5. [call [cmd destructor] [arg body]] The destructor is used to code any actions that must take place when an instance of the type is destroyed: typically, the destruction of anything created in the constructor. [para] The destructor takes no explicit arguments; as with methods, the arguments [var type], [var self], [var selfns], and [var win], are defined implicitly, and all type and instance variables are automatically visible in its [arg body]. As with methods, the destructor can call commands from the namespace in which the type is defined without importing them, e.g., if the type name is [cmd ::parentns::typename], then the destructor can call [cmd ::parentns::someproc] just as [cmd someproc]. [emph {Snit 1.x Incompatibility:}] This does not work in Snit 1.x, as it depends on [cmd "namespace path"], a new command in Tcl 8.5. [call [cmd proc] [arg name] [arg args] [arg body]] Defines a new Tcl procedure in the type's namespace. [para] The defined proc differs from a normal Tcl proc in that all type variables are automatically visible. The proc can access instance variables as well, provided that it is passed [var selfns] (with precisely that name) as one of its arguments. [para] Although they are not implicitly defined for procs, the argument names [const type], [const self], and [const win] should be avoided. [para] As with methods and typemethods, procs can call commands from the namespace in which the type is defined without importing them, e.g., if the type name is [cmd ::parentns::typename], then the proc can call [cmd ::parentns::someproc] just as [cmd someproc]. [emph {Snit 1.x Incompatibility:}] This does not work in Snit 1.x, as it depends on [cmd "namespace path"], a new command in Tcl 8.5. [call [cmd delegate] [const method] [arg name] [const to] [arg comp] [opt "[const as] [arg target]"]] Delegates method [arg name] to component [arg comp]. That is, when method [arg name] is called on an instance of this type, the method and its arguments will be passed to the named component's command instead. That is, the following statement [example { delegate method wag to tail }] is roughly equivalent to this explicitly defined method: [example { method wag {args} { uplevel $tail wag $args } }] As with methods, the [arg name] may have multiple tokens; in this case, the last token of the name is assumed to be the name of the component's method. [para] The optional [const as] clause allows you to specify the delegated method name and possibly add some arguments: [example { delegate method wagtail to tail as "wag briskly" }] [para] A method cannot be both locally defined and delegated. [para] [const Note:] All forms of [cmd "delegate method"] can delegate to both instance components and type components. [call [cmd delegate] [const method] [arg name] [opt "[const to] [arg comp]"] [const using] [arg pattern]] In this form of the [cmd delegate] statement, the [const using] clause is used to specify the precise form of the command to which method [arg name] name is delegated. In this form, the [const "to"] clause is optional, since the chosen command might not involve any particular component. [para] The value of the [const using] clause is a list that may contain any or all of the following substitution codes; these codes are substituted with the described value to build the delegated command prefix. Note that the following two statements are equivalent: [example { delegate method wag to tail delegate method wag to tail using "%c %m" }] Each element of the list becomes a single element of the delegated command--it is never reparsed as a string. [para] Substitutions: [list_begin definitions] [def [const %%]] This is replaced with a single "%". Thus, to pass the string "%c" to the command as an argument, you'd write "%%c". [def [const %c]] This is replaced with the named component's command. [def [const %m]] This is replaced with the final token of the method [arg name]; if the method [arg name] has one token, this is identical to [const %M]. [def [const %M]] This is replaced by the method [arg name]; if the [arg name] consists of multiple tokens, they are joined by space characters. [def [const %j]] This is replaced by the method [arg name]; if the [arg name] consists of multiple tokens, they are joined by underscores ("_"). [def [const %t]] This is replaced with the fully qualified type name. [def [const %n]] This is replaced with the name of the instance's private namespace. [def [const %s]] This is replaced with the name of the instance command. [def [const %w]] This is replaced with the original name of the instance command; for Snit widgets and widget adaptors, it will be the Tk window name. It remains constant, even if the instance command is renamed. [list_end] [call [cmd delegate] [const method] [const *] [opt "[const to] [arg comp]"] [opt "[const using] [arg pattern]"] [opt "[const except] [arg exceptions]"]] The form [cmd "delegate method *"] delegates all unknown method names to the specified [arg comp]onent. The [const except] clause can be used to specify a list of [arg exceptions], i.e., method names that will not be so delegated. The [const using] clause is defined as given above. In this form, the statement must contain the [const to] clause, the [const using] clause, or both. [para] In fact, the "*" can be a list of two or more tokens whose last element is "*", as in the following example: [example { delegate method {tail *} to tail }] This implicitly defines the method [cmd tail] whose subcommands will be delegated to the [var tail] component. [call [cmd delegate] [const option] [arg namespec] [const to] [arg comp]] [call [cmd delegate] [const option] [arg namespec] [const to] [arg comp] [const as] [arg target]] [call [cmd delegate] [const option] [const *] [const to] [arg comp]] [call [cmd delegate] [const option] [const *] [const to] [arg comp] [const except] [arg exceptions]] Defines a delegated option; the [arg namespec] is defined as for the [cmd option] statement. When the [method configure], [method configurelist], or [method cget] instance method is used to set or retrieve the option's value, the equivalent [method configure] or [method cget] command will be applied to the component as though the option was defined with the following [const -configuremethod] and [const -cgetmethod]: [example { method ConfigureMethod {option value} { $comp configure $option $value } method CgetMethod {option} { return [$comp cget $option] } }] Note that delegated options never appear in the [var options] array. [para] If the [const as] clause is specified, then the [arg target] option name is used in place of [arg name]. [para] The form [cmd "delegate option *"] delegates all unknown options to the specified [arg comp]onent. The [const except] clause can be used to specify a list of [arg exceptions], i.e., option names that will not be so delegated. [para] Warning: options can only be delegated to a component if it supports the [method configure] and [method cget] instance methods. [para] An option cannot be both locally defined and delegated. TBD: Continue from here. [call [cmd component] [arg comp] \ [opt "[const -public] [arg method]"] \ [opt "[const -inherit] [arg flag]"]] Explicitly declares a component called [arg comp], and automatically defines the component's instance variable. [para] If the [const -public] option is specified, then the option is made public by defining a [arg method] whose subcommands are delegated to the component e.g., specifying [const "-public mycomp"] is equivalent to the following: [example { component mycomp delegate method {mymethod *} to mycomp }] If the [const -inherit] option is specified, then [arg flag] must be a Boolean value; if [arg flag] is true then all unknown methods and options will be delegated to this component. The name [const -inherit] implies that instances of this new type inherit, in a sense, the methods and options of the component. That is, [const "-inherit yes"] is equivalent to: [example { component mycomp delegate option * to mycomp delegate method * to mycomp }] [call [cmd delegate] [const typemethod] [arg name] [const to] [arg comp] [opt "[const as] [arg target]"]] Delegates type method [arg name] to type component [arg comp]. That is, when type method [arg name] is called on this type, the type method and its arguments will be passed to the named type component's command instead. That is, the following statement [example { delegate typemethod lostdogs to pound }] is roughly equivalent to this explicitly defined method: [example { typemethod lostdogs {args} { uplevel $pound lostdogs $args } }] As with type methods, the [arg name] may have multiple tokens; in this case, the last token of the name is assumed to be the name of the component's method. [para] The optional [const as] clause allows you to specify the delegated method name and possibly add some arguments: [example { delegate typemethod lostdogs to pound as "get lostdogs" }] [para] A type method cannot be both locally defined and delegated. [call [cmd delegate] [const typemethod] [arg name] [opt "[const to] [arg comp]"] [const using] [arg pattern]] In this form of the [cmd delegate] statement, the [const using] clause is used to specify the precise form of the command to which type method [arg name] name is delegated. In this form, the [const "to"] clause is optional, since the chosen command might not involve any particular type component. [para] The value of the [const using] clause is a list that may contain any or all of the following substitution codes; these codes are substituted with the described value to build the delegated command prefix. Note that the following two statements are equivalent: [example { delegate typemethod lostdogs to pound delegate typemethod lostdogs to pound using "%c %m" }] Each element of the list becomes a single element of the delegated command--it is never reparsed as a string. [para] Substitutions: [list_begin definitions] [def [const %%]] This is replaced with a single "%". Thus, to pass the string "%c" to the command as an argument, you'd write "%%c". [def [const %c]] This is replaced with the named type component's command. [def [const %m]] This is replaced with the final token of the type method [arg name]; if the type method [arg name] has one token, this is identical to [const %M]. [def [const %M]] This is replaced by the type method [arg name]; if the [arg name] consists of multiple tokens, they are joined by space characters. [def [const %j]] This is replaced by the type method [arg name]; if the [arg name] consists of multiple tokens, they are joined by underscores ("_"). [def [const %t]] This is replaced with the fully qualified type name. [list_end] [call [cmd delegate] [const typemethod] [const *] [opt "[const to] [arg comp]"] \ [opt "[const using] [arg pattern]"] [opt "[const except] [arg exceptions]"]] The form [cmd "delegate typemethod *"] delegates all unknown type method names to the specified type component. The [const except] clause can be used to specify a list of [arg exceptions], i.e., type method names that will not be so delegated. The [const using] clause is defined as given above. In this form, the statement must contain the [const to] clause, the [const using] clause, or both. [para] [const Note:] By default, Snit interprets [cmd "\$type foo"], where [const "foo"] is not a defined type method, as equivalent to [cmd "\$type create foo"], where [const "foo"] is the name of a new instance of the type. If you use [const "delegate typemethod *"], then the [method "create"] type method must always be used explicitly. [para] The "*" can be a list of two or more tokens whose last element is "*", as in the following example: [example { delegate typemethod {tail *} to tail }] This implicitly defines the type method [cmd tail] whose subcommands will be delegated to the [var tail] type component. [call [cmd typecomponent] [arg comp] \ [opt "[const -public] [arg typemethod]"] \ [opt "[const -inherit] [arg flag]"]] Explicitly declares a type component called [arg comp], and automatically defines the component's type variable. A type component is an arbitrary command to which type methods and instance methods can be delegated; the command's name is stored in a type variable. [para] If the [const -public] option is specified, then the type component is made public by defining a [arg typemethod] whose subcommands are delegated to the type component, e.g., specifying [const "-public mytypemethod"] is equivalent to the following: [example { typecomponent mycomp delegate typemethod {mytypemethod *} to mycomp }] If the [const -inherit] option is specified, then [arg flag] must be a Boolean value; if [arg flag] is true then all unknown type methods will be delegated to this type component. (See the note on "delegate typemethod *", above.) The name [const -inherit] implies that this type inherits, in a sense, the behavior of the type component. That is, [const "-inherit yes"] is equivalent to: [example { typecomponent mycomp delegate typemethod * to mycomp }] [call [cmd pragma] [opt [arg options...]]] The [cmd pragma] statement provides control over how Snit generates a type. It takes the following options; in each case, [arg flag] must be a Boolean value recognized by Tcl, e.g., [const 0], [const 1], [const "yes"], [const "no"], and so on. [para] By setting the [const -hastypeinfo], [const -hastypedestroy], and [const -hasinstances] pragmas to false and defining appropriate type methods, you can create an ensemble command without any extraneous behavior. [list_begin definitions] [def "[const -canreplace] [arg flag]"] If false (the default) Snit will not create an instance of a [cmd snit::type] that has the same name as an existing command; this prevents subtle errors. Setting this pragma to true restores the behavior of Snit V0.93 and earlier versions. [def "[const -hastypeinfo] [arg flag]"] If true (the default), the generated type will have a type method called [cmd info] that is used for type introspection; the [cmd info] type method is documented below. If false, it will not. [def "[const -hastypedestroy] [arg flag]"] If true (the default), the generated type will have a type method called [cmd destroy] that is used to destroy the type and all of its instances. The [cmd destroy] type method is documented below. If false, it will not. [def "[const -hastypemethods] [arg flag]"] If true (the default), the generated type's type command will have subcommands (type methods) as usual. If false, the type command will serve only to create instances of the type; the first argument is the instance name. [para] This pragma and [const -hasinstances] cannot both be set false. [def "[const -hasinstances] [arg flag]"] If true (the default), the generated type will have a type method called [cmd create] that is used to create instances of the type, along with a variety of instance-related features. If false, it will not. [para] This pragma and [const -hastypemethods] cannot both be set false. [def "[const -hasinfo] [arg flag]"] If true (the default), instances of the generated type will have an instance method called [method info] that is used for instance introspection; the [method info] method is documented below. If false, it will not. [def "[const -simpledispatch] [arg flag]"] This pragma is intended to make simple, heavily-used abstract data types (e.g., stacks and queues) more efficient. [para] If false (the default), instance methods are dispatched normally. If true, a faster dispatching scheme is used instead. The speed comes at a price; with [const "-simpledispatch yes"] you get the following limitations: [list_begin itemized] [item] Methods cannot be delegated. [item] [cmd uplevel] and [cmd upvar] do not work as expected: the caller's scope is two levels up rather than one. [item] The option-handling methods ([cmd cget], [cmd configure], and [cmd configurelist]) are very slightly slower. [list_end] [list_end] [call [cmd expose] [arg comp]] [call [cmd expose] [arg comp] [const as] [arg method]] [comment { The word "Deprecated" really needs to be boldface, and there's no good way to do it, so I'm using "const". }] [const Deprecated.] To expose component [arg comp] publicly, use [cmd component]'s [const -public] option. [call [cmd onconfigure] [arg name] [arg arglist] [arg body]] [const Deprecated.] Define [cmd option]'s [const -configuremethod] option instead. [para] As of version 0.95, the following definitions, [example { option -myoption onconfigure -myoption {value} { # Code to save the option's value } }] are implemented as follows: [example { option -myoption -configuremethod _configure-myoption method _configure-myoption {_option value} { # Code to save the option's value } }] [call [cmd oncget] [arg name] [arg body]] [const Deprecated.] Define [cmd option]'s [const -cgetmethod] option instead. [para] As of version 0.95, the following definitions, [example { option -myoption oncget -myoption { # Code to return the option's value } }] are implemented as follows: [example { option -myoption -cgetmethod _cget-myoption method _cget-myoption {_option} { # Code to return the option's value } }] [list_end] [call [cmd snit::widget] [arg name] [arg definition]] This command defines a Snit megawidget type with the specified [arg name]. The [arg definition] is defined as for [cmd snit::type]. A [cmd snit::widget] differs from a [cmd snit::type] in these ways: [list_begin itemized] [item] Every instance of a [cmd snit::widget] has an automatically-created component called [var hull], which is normally a Tk frame widget. Other widgets created as part of the megawidget will be created within this widget. [para] The hull component is initially created with the requested widget name; then Snit does some magic, renaming the hull component and installing its own instance command in its place. The hull component's new name is saved in an instance variable called [var hull]. [item] The name of an instance must be valid Tk window name, and the parent window must exist. [list_end] A [cmd snit::widget] definition can include any of statements allowed in a [cmd snit::type] definition, and may also include the following: [list_begin definitions] [call [cmd widgetclass] [arg name]] Sets the [cmd snit::widget]'s widget class to [arg name], overriding the default. See [sectref {The Tk Option Database}] for more information. [call [cmd hulltype] [arg type]] Determines the kind of widget used as the [cmd snit::widget]'s hull. The [arg type] may be [const frame] (the default), [const toplevel], [const labelframe]; the qualified equivalents of these, [const tk::frame], [const tk::toplevel], and [const tk::labelframe]; or, if available, the equivalent Tile widgets: [const ttk::frame], [const ttk::toplevel], and [const ttk::labelframe]. In practice, any widget that supports the [const -class] option can be used as a hull widget by [cmd lappend]'ing its name to the variable [var snit::hulltypes]. [list_end] [call [cmd snit::widgetadaptor] [arg name] [arg definition]] This command defines a Snit megawidget type with the specified name. It differs from [cmd snit::widget] in that the instance's [var hull] component is not created automatically, but is created in the constructor and installed using the [cmd installhull] command. Once the hull is installed, its instance command is renamed and replaced as with normal [cmd snit::widget]s. The original command is again accessible in the instance variable [var hull]. [para] Note that in general it is not possible to change the [emph {widget class}] of a [cmd snit::widgetadaptor]'s hull widget. [para] See [sectref {The Tk Option Database}] for information on how [cmd snit::widgetadaptor]s interact with the option database. [call [cmd snit::typemethod] [arg type] [arg name] [arg arglist] [arg body]] Defines a new type method (or redefines an existing type method) for a previously existing [arg type]. [call [cmd snit::method] [arg type] [arg name] [arg arglist] [arg body]] Defines a new instance method (or redefines an existing instance method) for a previously existing [arg type]. Note that delegated instance methods can't be redefined. [call [cmd snit::macro] [arg name] [arg arglist] [arg body]] Defines a Snit macro with the specified [arg name], [arg arglist], and [arg body]. Macros are used to define new type and widget definition statements in terms of the statements defined in this man page. [para] A macro is simply a Tcl proc that is defined in the slave interpreter used to compile type and widget definitions. Thus, macros have access to all of the type and widget definition statements. See [sectref "Macros and Meta-programming"] for more details. [para] The macro [arg name] cannot be the same as any standard Tcl command, or any Snit type or widget definition statement, e.g., you can't redefine the [cmd method] or [cmd delegate] statements, or the standard [cmd set], [cmd list], or [cmd string] commands. [call [cmd snit::compile] [arg which] [arg type] [arg body]] Snit defines a type, widget, or widgetadaptor by "compiling" the definition into a Tcl script; this script is then evaluated in the Tcl interpreter, which actually defines the new type. [para] This command exposes the "compiler". Given a definition [arg body] for the named [arg type], where [arg which] is [const type], [const widget], or [const widgetadaptor], [cmd snit::compile] returns a list of two elements. The first element is the fully qualified type name; the second element is the definition script. [para] [cmd snit::compile] is useful when additional processing must be done on the Snit-generated code--if it must be instrumented, for example, or run through the TclDevKit compiler. In addition, the returned script could be saved in a ".tcl" file and used to define the type as part of an application or library, thus saving the compilation overhead at application start-up. Note that the same version of Snit must be used at run-time as at compile-time. [list_end] [subsection {The Type Command}] A type or widget definition creates a type command, which is used to create instances of the type. The type command has this form: [para] [list_begin definitions] [call [cmd {$type}] [arg typemethod] [arg args]...] The [arg typemethod] can be any of the [sectref "Standard Type Methods"] (e.g., [method create]), or any type method defined in the type definition. The subsequent [arg args] depend on the specific [arg typemethod] chosen. [para] The type command is most often used to create new instances of the type; hence, the [method create] method is assumed if the first argument to the type command doesn't name a valid type method, unless the type definition includes [cmd "delegate typemethod *"] or the [const -hasinstances] pragma is set to false. [para] Furthermore, if the [const -hastypemethods] pragma is false, then Snit type commands can be called with no arguments at all; in this case, the type command creates an instance with an automatically generated name. In other words, provided that the [const -hastypemethods] pragma is false and the type has instances, the following commands are equivalent: [example {snit::type dog { ... } set mydog [dog create %AUTO%] set mydog [dog %AUTO%] set mydog [dog] }] This doesn't work for Snit widgets, for obvious reasons. [para] [emph "Snit 1.x Incompatibility:"] In Snit 1.x, the above behavior is available whether [const -hastypemethods] is true (the default) or false. [list_end] [subsection {Standard Type Methods}] In addition to any type methods in the type's definition, all type and widget commands will usually have at least the following subcommands: [para] [list_begin definitions] [call [cmd {$type}] [method create] [arg name] [opt "[arg option] [arg value] ..."]] Creates a new instance of the type, giving it the specified [arg name] and calling the type's constructor. [para] For [cmd snit::type]s, if [arg name] is not a fully-qualified command name, it is assumed to be a name in the namespace in which the call to [cmd snit::type] appears. The method returns the fully-qualified instance name. [para] For [cmd snit::widget]s and [cmd snit::widgetadaptor]s, [arg name] must be a valid widget name; the method returns the widget name. [para] So long as [arg name] does not conflict with any defined type method name the [method create] keyword may be omitted, unless the type definition includes [cmd "delegate typemethod *"] or the [const -hasinstances] pragma is set to false. [para] If the [arg name] includes the string [const %AUTO%], it will be replaced with the string [const {$type$counter}] where [const {$type}] is the type name and [const {$counter}] is a counter that increments each time [const %AUTO%] is used for this type. [para] By default, any arguments following the [arg name] will be a list of [arg option] names and their [arg value]s; however, a type's constructor can specify a different argument list. [para] As of Snit V0.95, [method create] will throw an error if the [arg name] is the same as any existing command--note that this was always true for [cmd snit::widget]s and [cmd snit::widgetadaptor]s. You can restore the previous behavior using the [const -canreplace] pragma. [call [cmd {$type}] [method {info typevars}] [opt [arg pattern]]] Returns a list of the type's type variables (excluding Snit internal variables); all variable names are fully-qualified. [para] If [arg pattern] is given, it's used as a [cmd {string match}] pattern; only names that match the pattern are returned. [call [cmd {$type}] [method {info typemethods}] [opt [arg pattern]]] Returns a list of the names of the type's type methods. If the type has hierarchical type methods, whether locally-defined or delegated, only the first word of each will be included in the list. [para] If the type definition includes [cmd "delegate typemethod *"], the list will include only the names of those implicitly delegated type methods that have been called at least once and are still in the type method cache. [para] If [arg pattern] is given, it's used as a [cmd {string match}] pattern; only names that match the pattern are returned. [call [cmd {$type}] [method {info args}] [arg method]] Returns a list containing the names of the arguments to the type's [arg method], in order. This method cannot be applied to delegated type methods. [call [cmd {$type}] [method {info body}] [arg method]] Returns the body of typemethod [arg method]. This method cannot be applied to delegated type methods. [call [cmd {$type}] [method {info default}] [arg method] [arg aname] [arg varname]] Returns a boolean value indicating whether the argument [arg aname] of the type's [arg method] has a default value ([const true]) or not ([const false]). If the argument has a default its value is placed into the variable [arg varname]. [call [cmd {$type}] [method {info instances}] [opt [arg pattern]]] Returns a list of the type's instances. For [cmd snit::type]s, it will be a list of fully-qualified instance names; for [cmd snit::widget]s, it will be a list of Tk widget names. [para] If [arg pattern] is given, it's used as a [cmd {string match}] pattern; only names that match the pattern are returned. [para] [emph "Snit 1.x Incompatibility:"] In Snit 1.x, the full multi-word names of hierarchical type methods are included in the return value. [call [cmd {$type}] [method destroy]] Destroys the type's instances, the type's namespace, and the type command itself. [list_end] [subsection {The Instance Command}] A Snit type or widget's [method create] type method creates objects of the type; each object has a unique name that is also a Tcl command. This command is used to access the object's methods and data, and has this form: [para] [list_begin definitions] [call [cmd {$object}] [arg method] [arg args...]] The [arg method] can be any of the [sectref "Standard Instance Methods"], or any instance method defined in the type definition. The subsequent [arg args] depend on the specific [arg method] chosen. [list_end] [subsection {Standard Instance Methods}] In addition to any delegated or locally-defined instance methods in the type's definition, all Snit objects will have at least the following subcommands: [para] [list_begin definitions] [call [cmd {$object}] [method configure] [opt [arg option]] [opt [arg value]] ...] Assigns new values to one or more options. If called with one argument, an [arg option] name, returns a list describing the option, as Tk widgets do; if called with no arguments, returns a list of lists describing all options, as Tk widgets do. [para] Warning: This information will be available for delegated options only if the component to which they are delegated has a [method configure] method that returns this same kind of information. [para] Note: Snit defines this method only if the type has at least one option. [call [cmd {$object}] [method configurelist] [arg optionlist]] Like [method configure], but takes one argument, a list of options and their values. It's mostly useful in the type constructor, but can be used anywhere. [para] Note: Snit defines this method only if the type has at least one option. [call [cmd {$object}] [method cget] [arg option]] Returns the option's value. [para] Note: Snit defines this method only if the type has at least one option. [call [cmd {$object}] [method destroy]] Destroys the object, calling the [cmd destructor] and freeing all related memory. [para] [emph Note:] The [method destroy] method isn't defined for [cmd snit::widget] or [cmd snit::widgetadaptor] objects; instances of these are destroyed by calling [package Tk]'s [cmd destroy] command, just as normal widgets are. [call [cmd {$object}] [method {info type}]] Returns the instance's type. [call [cmd {$object}] [method {info vars}] [opt [arg pattern]]] Returns a list of the object's instance variables (excluding Snit internal variables). The names are fully qualified. [para] If [arg pattern] is given, it's used as a [cmd {string match}] pattern; only names that match the pattern are returned. [call [cmd {$object}] [method {info typevars}] [opt [arg pattern]]] Returns a list of the object's type's type variables (excluding Snit internal variables). The names are fully qualified. [para] If [arg pattern] is given, it's used as a [cmd {string match}] pattern; only names that match the pattern are returned. [call [cmd {$object}] [method {info typemethods}] [opt [arg pattern]]] Returns a list of the names of the type's type methods. If the type has hierarchical type methods, whether locally-defined or delegated, only the first word of each will be included in the list. [para] If the type definition includes [cmd "delegate typemethod *"], the list will include only the names of those implicitly delegated type methods that have been called at least once and are still in the type method cache. [para] If [arg pattern] is given, it's used as a [cmd {string match}] pattern; only names that match the pattern are returned. [para] [emph "Snit 1.x Incompatibility:"] In Snit 1.x, the full multi-word names of hierarchical type methods are included in the return value. [call [cmd {$object}] [method {info options}] [opt [arg pattern]]] Returns a list of the object's option names. This always includes local options and explicitly delegated options. If unknown options are delegated as well, and if the component to which they are delegated responds to [cmd {$object configure}] like Tk widgets do, then the result will include all possible unknown options that can be delegated to the component. [para] If [arg pattern] is given, it's used as a [cmd {string match}] pattern; only names that match the pattern are returned. [para] Note that the return value might be different for different instances of the same type, if component object types can vary from one instance to another. [call [cmd {$object}] [method {info methods}] [opt [arg pattern]]] Returns a list of the names of the instance's methods. If the type has hierarchical methods, whether locally-defined or delegated, only the first word of each will be included in the list. [para] If the type definition includes [cmd "delegate method *"], the list will include only the names of those implicitly delegated methods that have been called at least once and are still in the method cache. [para] If [arg pattern] is given, it's used as a [cmd {string match}] pattern; only names that match the pattern are returned. [para] [emph "Snit 1.x Incompatibility:"] In Snit 1.x, the full multi-word names of hierarchical type methods are included in the return value. [call [cmd {$object}] [method {info args}] [arg method]] Returns a list containing the names of the arguments to the instance's [arg method], in order. This method cannot be applied to delegated methods. [call [cmd {$object}] [method {info body}] [arg method]] Returns the body of the instance's method [arg method]. This method cannot be applied to delegated methods. [call [cmd {$object}] [method {info default}] [arg method] [arg aname] [arg varname]] Returns a boolean value indicating whether the argument [arg aname] of the instance's [arg method] has a default value ([const true]) or not ([const false]). If the argument has a default its value is placed into the variable [arg varname]. [list_end] [subsection {Commands for use in Object Code}] Snit defines the following commands for use in your object code: that is, for use in type methods, instance methods, constructors, destructors, onconfigure handlers, oncget handlers, and procs. They do not reside in the ::snit:: namespace; instead, they are created with the type, and can be used without qualification. [list_begin definitions] [call [cmd mymethod] [arg name] [opt [arg args...]]] The [cmd mymethod] command is used for formatting callback commands to be passed to other objects. It returns a command that when called will invoke method [arg name] with the specified arguments, plus of course any arguments added by the caller. In other words, both of the following commands will cause the object's [method dosomething] method to be called when the [cmd {$button}] is pressed: [example { $button configure -command [list $self dosomething myargument] $button configure -command [mymethod dosomething myargument] }] The chief distinction between the two is that the latter form will not break if the object's command is renamed. [call [cmd mytypemethod] [arg name] [opt [arg args...]]] The [cmd mytypemethod] command is used for formatting callback commands to be passed to other objects. It returns a command that when called will invoke type method [arg name] with the specified arguments, plus of course any arguments added by the caller. In other words, both of the following commands will cause the object's [method dosomething] type method to be called when [cmd {$button}] is pressed: [example { $button configure -command [list $type dosomething myargument] $button configure -command [mytypemethod dosomething myargument] }] Type commands cannot be renamed, so in practice there's little difference between the two forms. [cmd mytypemethod] is provided for parallelism with [cmd mymethod]. [call [cmd myproc] [arg name] [opt [arg args...]]] The [cmd myproc] command is used for formatting callback commands to be passed to other objects. It returns a command that when called will invoke the type proc [arg name] with the specified arguments, plus of course any arguments added by the caller. In other words, both of the following commands will cause the object's [method dosomething] proc to be called when [cmd {$button}] is pressed: [example { $button configure -command [list ${type}::dosomething myargument] $button configure -command [myproc dosomething myargument] }] [call [cmd myvar] [arg name]] Given an instance variable name, returns the fully qualified name. Use this if you're passing the variable to some other object, e.g., as a [option -textvariable] to a Tk label widget. [call [cmd mytypevar] [arg name]] Given an type variable name, returns the fully qualified name. Use this if you're passing the variable to some other object, e.g., as a [option -textvariable] to a Tk label widget. [call [cmd from] [arg argvName] [arg option] [opt [arg defvalue]]] The [cmd from] command plucks an option value from a list of options and their values, such as is passed into a type's [cmd constructor]. [arg argvName] must be the name of a variable containing such a list; [arg option] is the name of the specific option. [para] [cmd from] looks for [arg option] in the option list. If it is found, it and its value are removed from the list, and the value is returned. If [arg option] doesn't appear in the list, then the [arg defvalue] is returned. If the option is locally-defined option, and [arg defvalue] is not specified, then the option's default value as specified in the type definition will be returned instead. [call [cmd install] [arg compName] [const using] [arg objType] [arg objName] [arg args...]] Creates a new object of type [arg objType] called [arg objName] and installs it as component [arg compName], as described in [sectref {Components and Delegation}]. Any additional [arg args...] are passed along with the name to the [arg objType] command. If this is a [cmd snit::type], then the following two commands are equivalent: [example { install myComp using myObjType $self.myComp args... set myComp [myObjType $self.myComp args...] }] Note that whichever method is used, [arg compName] must still be declared in the type definition using [cmd component], or must be referenced in at least one [cmd delegate] statement. [para] If this is a [cmd snit::widget] or [cmd snit::widgetadaptor], and if options have been delegated to component [arg compName], then those options will receive default values from the Tk option database. Note that it doesn't matter whether the component to be installed is a widget or not. See [sectref {The Tk Option Database}] for more information. [para] [cmd install] cannot be used to install type components; just assign the type component's command name to the type component's variable instead. [call [cmd installhull] [const using] [arg widgetType] [arg args...]] [call [cmd installhull] [arg name]] The constructor of a [cmd snit::widgetadaptor] must create a widget to be the object's hull component; the widget is installed as the hull component using this command. Note that the installed widget's name must be [const {$win}]. This command has two forms. [para] The first form specifies the [arg widgetType] and the [arg args...] (that is, the hardcoded option list) to use in creating the hull. Given this form, [cmd installhull] creates the hull widget, and initializes any options delegated to the hull from the Tk option database. [para] In the second form, the hull widget has already been created; note that its name must be "$win". In this case, the Tk option database is [emph not] queried for any options delegated to the hull. The longer form is preferred; however, the shorter form allows the programmer to adapt a widget created elsewhere, which is sometimes useful. For example, it can be used to adapt a "page" widget created by a [package BWidgets] tabbed notebook or pages manager widget. [para] See [sectref {The Tk Option Database}] for more information about [cmd snit::widgetadaptor]s and the option database. [call [cmd variable] [arg name]] Normally, instance variables are defined in the type definition along with the options, methods, and so forth; such instance variables are automatically visible in all instance code (e.g., method bodies). However, instance code can use the [cmd variable] command to declare instance variables that don't appear in the type definition, and also to bring variables from other namespaces into scope in the usual way. [para] It's generally clearest to define all instance variables in the type definition, and omit declaring them in methods and so forth. [para] Note that this is an instance-specific version of the standard Tcl [cmd ::variable] command. [call [cmd typevariable] [arg name]] Normally, type variables are defined in the type definition, along with the instance variables; such type variables are automatically visible in all of the type's code. However, type methods, instance methods and so forth can use [cmd typevariable] to declare type variables that don't appear in the type definition. [para] It's generally clearest to declare all type variables in the type definition, and omit declaring them in methods, type methods, etc. [call [cmd varname] [arg name]] [const Deprecated.] Use [cmd myvar] instead. [para] Given an instance variable name, returns the fully qualified name. Use this if you're passing the variable to some other object, e.g., as a [option -textvariable] to a Tk label widget. [call [cmd typevarname] [arg name]] [const Deprecated.] Use [cmd mytypevar] instead. [para] Given a type variable name, returns the fully qualified name. Use this if you're passing the type variable to some other object, e.g., as a [option -textvariable] to a Tk label widget. [call [cmd codename] [arg name]] [const Deprecated.] Use [cmd myproc] instead. Given the name of a proc (but not a type or instance method), returns the fully-qualified command name, suitable for passing as a callback. [list_end] [para] [subsection {Components and Delegation}] When an object includes other objects, as when a toolbar contains buttons or a GUI object contains an object that references a database, the included object is called a component. The standard way to handle component objects owned by a Snit object is to declare them using [cmd component], which creates a component instance variable. In the following example, a [cmd dog] object has a [cmd tail] object: [para] [example { snit::type dog { component mytail constructor {args} { set mytail [tail %AUTO% -partof $self] $self configurelist $args } method wag {} { $mytail wag } } snit::type tail { option -length 5 option -partof method wag {} { return "Wag, wag, wag."} } }] [para] Because the [cmd tail] object's name is stored in an instance variable, it's easily accessible in any method. [para] The [cmd install] command provides an alternate way to create and install the component: [para] [example { snit::type dog { component mytail constructor {args} { install mytail using tail %AUTO% -partof $self $self configurelist $args } method wag {} { $mytail wag } } }] [para] For [cmd snit::type]s, the two methods are equivalent; for [cmd snit::widget]s and [cmd snit::widgetadaptor]s, the [cmd install] command properly initializes the widget's options by querying [sectref {The Tk Option Database}]. [para] In the above examples, the [cmd dog] object's [method wag] method simply calls the [cmd tail] component's [method wag] method. In OO jargon, this is called delegation. Snit provides an easier way to do this: [para] [example { snit::type dog { delegate method wag to mytail constructor {args} { install mytail using tail %AUTO% -partof $self $self configurelist $args } } }] [para] The [cmd delegate] statement in the type definition implicitly defines the instance variable [var mytail] to hold the component's name (though it's good form to use [cmd component] to declare it explicitly); it also defines the [cmd dog] object's [method wag] method, delegating it to the [var mytail] component. [para] If desired, all otherwise unknown methods can be delegated to a specific component: [para] [example { snit::type dog { delegate method * to mytail constructor {args} { set mytail [tail %AUTO% -partof $self] $self configurelist $args } method bark { return "Bark, bark, bark!" } } }] [para] In this case, a [cmd dog] object will handle its own [method bark] method; but [method wag] will be passed along to [cmd mytail]. Any other method, being recognized by neither [cmd dog] nor [cmd tail], will simply raise an error. [para] Option delegation is similar to method delegation, except for the interactions with the Tk option database; this is described in [sectref "The Tk Option Database"]. [subsection {Type Components and Delegation}] The relationship between type components and instance components is identical to that between type variables and instance variables, and that between type methods and instance methods. Just as an instance component is an instance variable that holds the name of a command, so a type component is a type variable that holds the name of a command. In essence, a type component is a component that's shared by every instance of the type. [para] Just as [cmd "delegate method"] can be used to delegate methods to instance components, as described in [sectref "Components and Delegation"], so [cmd "delegate typemethod"] can be used to delegate type methods to type components. [para] Note also that as of Snit 0.95 [cmd "delegate method"] can delegate methods to both instance components and type components. [subsection {The Tk Option Database}] This section describes how Snit interacts with the Tk option database, and assumes the reader has a working knowledge of the option database and its uses. The book [emph {Practical Programming in Tcl and Tk}] by Welch et al has a good introduction to the option database, as does [emph {Effective Tcl/Tk Programming}]. [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 itemized] [item] If the [cmd snit::widget]'s default widget class is not what is desired, set it explicitly using [cmd widgetclass] in the widget definition. [item] When defining or delegating options, specify the resource and class names explicitly when if the defaults aren't what you want. [item] Use [cmd {installhull using}] to install the hull for [cmd snit::widgetadaptor]s. [item] Use [cmd install] to 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] [const {Setting the widget class:}] Every Tk widget has a widget class. For Tk widgets, the widget class name is the just the widget type name with an initial capital letter, e.g., the widget class for [cmd button] widgets 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". The widget class can also be set explicitly using the [cmd widgetclass] statement within the [cmd snit::widget] definition. [para] Any widget can be used as the [cmd hulltype] provided that it supports the [const -class] option for changing its widget class name. See the discussion of the [cmd hulltype] command, above. The user may pass [const -class] to the widget at instantion. [para] The widget class of a [cmd snit::widgetadaptor] is just the widget class of its hull widget; this cannot be changed unless the hull widget supports [const -class], in which case it will usually make more sense to use [cmd snit::widget] rather than [cmd snit::widgetadaptor]. [para] [const {Setting option resource names and classes:}] In Tk, every 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 Tk 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 [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] Snit options also have a resource name and a class name. By default, these names follow the rule given above: the resource name is the option name without the hyphen, and the class name is the resource name with an initial capital. This is true for both locally-defined options and explicitly delegated options: [para] [example { snit::widget mywidget { option -background delegate option -borderwidth to hull delegate option * to text # ... } }] [para] In this case, the widget class name is "Mywidget". The widget has the following options: [option -background], which is locally defined, and [option -borderwidth], which is explicitly delegated; all other widgets are delegated to a component called "text", which is probably a Tk [cmd text] widget. If so, [cmd mywidget] has all the same options as a [cmd text] widget. The option, resource, and class names are as follows: [para] [example { -background background Background -borderwidth borderwidth Borderwidth -padx padX Pad }] [para] Note that the locally defined option, [option -background], happens to have the same three names as the standard Tk [option -background] option; and [option -pad], which is delegated implicitly to the [var text] component, has the same three names for [cmd mywidget] as it does for the [cmd text] widget. [option -borderwidth], on the other hand, has different resource and class names than usual, because the internal word "width" isn't capitalized. For consistency, it should be; this is done as follows: [para] [example { snit::widget mywidget { option -background delegate option {-borderwidth borderWidth} to hull delegate option * to text # ... } }] [para] The class name will default to "BorderWidth", as expected. [para] Suppose, however, that [cmd mywidget] also delegated [option -padx] and [option -pady] to the hull. In this case, both the resource name and the class name must be specified explicitly: [para] [example { snit::widget mywidget { option -background delegate option {-borderwidth borderWidth} to hull delegate option {-padx padX Pad} to hull delegate option {-pady padY Pad} to hull delegate option * to text # ... } }] [para] [const {Querying the option database:}] If you set your widgetclass and option names as described above, Snit will query the option database when each instance is created, and will generally do the right thing when it comes to querying the option database. The remainder of this section goes into the gory details. [para] [const {Initializing locally defined options:}] When an instance of a snit::widget is created, its locally defined options are initialized as follows: each option's resource and class names are used to query the Tk option database. If the result is non-empty, it is used as the option's default; otherwise, the default hardcoded in the type definition is used. In either case, the default can be overridden by the caller. For example, [para] [example { option add *Mywidget.texture pebbled snit::widget mywidget { option -texture smooth # ... } mywidget .mywidget -texture greasy }] [para] Here, [option -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] [const {Initializing options delegated to the hull:}] 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 that has been given the widget class "Mywidget"; it will automatically query the option database and pick up this value. Since the [option -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 [option -background] option, just as it picked up the [option -relief] value. However, Snit knows that [option -hullbackground] is mapped to the hull's [option -background] option; hence, it queries the option database for [option -hullbackground] and gets "red" and updates the hull accordingly. [para] The value of C is also "red", because [option -background] is implicitly delegated to the hull; thus, retrieving it is the same as retrieving [option -hullbackground]. Note that this case is unusual; in practice, [option -background] would probably be explicitly 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 [option -borderwidth] defaults to "borderwidth", whereas the option database entry is "borderWidth". As with [option -relief], the hull picks up its own [option -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. [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; options delegated from a different name are initialized from the option database in the same way. [para] [const {Initializing options delegated to other components:}] Non-hull components are matched against the option database in two ways. First, a component widget remains a widget still, and therefore is initialized from the option database in the usual way. Second, 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 itemized] [item] Builds a list of the options explicitly included in the [cmd install] command -- in this case, [option -foreground]. [item] Queries the option database for all options delegated explicitly to the named component. [item] 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 included options ([option -foreground]) will override anything read from the option database. [item] 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 that 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] [para] [const {Non-widget components:}] The option database is never queried for [cmd snit::type]s, since it can only be queried given a Tk widget name. However, [cmd snit::widget]s 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] [subsection {Macros and Meta-programming}] The [cmd snit::macro] command enables a certain amount of meta-programming with Snit classes. For example, suppose you like to define properties: instance variables that have set/get methods. Your code might look like this: [example { snit::type dog { variable mood happy method getmood {} { return $mood } method setmood {newmood} { set mood $newmood } } }] That's nine lines of text per property. Or, you could define the following [cmd snit::macro]: [example { snit::macro property {name initValue} { variable $name $initValue method get$name {} "return $name" method set$name {value} "set $name \$value" } }] Note that a [cmd snit::macro] is just a normal Tcl proc defined in the slave interpreter used to compile type and widget definitions; as a result, it has access to all the commands used to define types and widgets. [para] Given this new macro, you can define a property in one line of code: [example { snit::type dog { property mood happy } }] Within a macro, the commands [cmd variable] and [cmd proc] refer to the Snit type-definition commands, not the standard Tcl commands. To get the standard Tcl commands, use [cmd _variable] and [cmd _proc]. [para] Because a single slave interpreter is used for compiling all Snit types and widgets in the application, there's the possibility of macro name collisions. If you're writing a reuseable package using Snit, and you use some [cmd snit::macro]s, define them in your package namespace: [example { snit::macro mypkg::property {name initValue} { ... } snit::type dog { mypkg::property mood happy } }] This leaves the global namespace open for application authors. [para] [subsection "Validation Types"] A validation type is an object that can be used to validate Tcl values of a particular kind. For example, [cmd snit::integer] is used to validate that a Tcl value is an integer. [para] Every validation type has a [method validate] method which is used to do the validation. This method must take a single argument, the value to be validated; further, it must do nothing if the value is valid, but throw an error if the value is invalid: [example { snit::integer validate 5 ;# Does nothing snit::integer validate 5.0 ;# Throws an error (not an integer!) }] [para] The [method validate] method will always return the validated value on success, and throw the [cmd -errorcode] INVALID on error. [para] Snit defines a family of validation types, all of which are implemented as [cmd snit::type]'s. They can be used as is; in addition, their instances serve as parameterized subtypes. For example, a probability is a number between 0.0 and 1.0 inclusive: [example { snit::double probability -min 0.0 -max 1.0 }] The example above creates an instance of [cmd snit::double]--a validation subtype--called [cmd probability], which can be used to validate probability values: [example { probability validate 0.5 ;# Does nothing probability validate 7.9 ;# Throws an error }] Validation subtypes can be defined explicitly, as in the above example; when a locally-defined option's [const -type] is specified, they may also be created on the fly: [example { snit::enum ::dog::breed -values {mutt retriever sheepdog} snit::type dog { # Define subtypes on the fly... option -breed -type { snit::enum -values {mutt retriever sheepdog} } # Or use predefined subtypes... option -breed -type ::dog::breed } }] [para] Any object that has a [method validate] method with the semantics described above can be used as a validation type; see [sectref "Defining Validation Types"] for information on how to define new ones. [para] Snit defines the following validation types: [list_begin definitions] [call [cmd snit::boolean] [const validate] [opt [arg value]]] [call [cmd snit::boolean] [arg name]] Validates Tcl boolean values: 1, 0, [const on], [const off], [const yes], [const no], [const true], [const false]. It's possible to define subtypes--that is, instances--of [cmd snit::boolean], but as it has no options there's no reason to do so. [call [cmd snit::double] [const validate] [opt [arg value]]] [call [cmd snit::double] [arg name] [opt "[arg option] [arg value]..."]] Validates floating-point values. Subtypes may be created with the following options: [list_begin definitions] [def "[const -min] [arg min]"] Specifies a floating-point minimum bound; a value is invalid if it is strictly less than [arg min]. [def "[const -max] [arg max]"] Specifies a floating-point maximum bound; a value is invalid if it is strictly greater than [arg max]. [list_end] [call [cmd snit::enum] [const validate] [opt [arg value]]] [call [cmd snit::enum] [arg name] [opt "[arg option] [arg value]..."]] Validates that a value comes from an enumerated list. The base type is of little use by itself, as only subtypes actually have an enumerated list to validate against. Subtypes may be created with the following options: [list_begin definitions] [def "[const -values] [arg list]"] Specifies a list of valid values. A value is valid if and only if it's included in the list. [list_end] [call [cmd snit::fpixels] [const validate] [opt [arg value]]] [call [cmd snit::fpixels] [arg name] [opt "[arg option] [arg value]..."]] [emph "Tk programs only."] Validates screen distances, in any of the forms accepted by [cmd "winfo fpixels"]. Subtypes may be created with the following options: [list_begin definitions] [def "[const -min] [arg min]"] Specifies a minimum bound; a value is invalid if it is strictly less than [arg min]. The bound may be expressed in any of the forms accepted by [cmd "winfo fpixels"]. [def "[const -max] [arg max]"] Specifies a maximum bound; a value is invalid if it is strictly greater than [arg max]. The bound may be expressed in any of the forms accepted by [cmd "winfo fpixels"]. [list_end] [call [cmd snit::integer] [const validate] [opt [arg value]]] [call [cmd snit::integer] [arg name] [opt "[arg option] [arg value]..."]] Validates integer values. Subtypes may be created with the following options: [list_begin definitions] [def "[const -min] [arg min]"] Specifies an integer minimum bound; a value is invalid if it is strictly less than [arg min]. [def "[const -max] [arg max]"] Specifies an integer maximum bound; a value is invalid if it is strictly greater than [arg max]. [list_end] [call [cmd snit::listtype] [const validate] [opt [arg value]]] [call [cmd snit::listtype] [arg name] [opt "[arg option] [arg value]..."]] Validates Tcl lists. Subtypes may be created with the following options: [list_begin definitions] [def "[const -minlen] [arg min]"] Specifies a minimum list length; the value is invalid if it has fewer than [arg min] elements. Defaults to 0. [def "[const -maxlen] [arg max]"] Specifies a maximum list length; the value is invalid if it more than [arg max] elements. [def "[const -type] [arg type]"] Specifies the type of the list elements; [arg type] must be the name of a validation type or subtype. In the following example, the value of [const -numbers] must be a list of integers. [example { option -numbers -type {snit::listtype -type snit::integer} }] Note that this option doesn't support defining new validation subtypes on the fly; that is, the following code will not work (yet, anyway): [example { option -numbers -type { snit::listtype -type {snit::integer -min 5} } }] Instead, define the subtype explicitly: [example { snit::integer gt4 -min 5 snit::type mytype { option -numbers -type {snit::listtype -type gt4} } }] [list_end] [call [cmd snit::pixels] [const validate] [opt [arg value]]] [call [cmd snit::pixels] [arg name] [opt "[arg option] [arg value]..."]] [emph "Tk programs only."] Validates screen distances, in any of the forms accepted by [cmd "winfo pixels"]. Subtypes may be created with the following options: [list_begin definitions] [def "[const -min] [arg min]"] Specifies a minimum bound; a value is invalid if it is strictly less than [arg min]. The bound may be expressed in any of the forms accepted by [cmd "winfo pixels"]. [def "[const -max] [arg max]"] Specifies a maximum bound; a value is invalid if it is strictly greater than [arg max]. The bound may be expressed in any of the forms accepted by [cmd "winfo pixels"]. [list_end] [call [cmd snit::stringtype] [const validate] [opt [arg value]]] [call [cmd snit::stringtype] [arg name] [opt "[arg option] [arg value]..."]] Validates Tcl strings. The base type is of little use by itself, since very Tcl value is also a valid string. Subtypes may be created with the following options: [list_begin definitions] [def "[const -minlen] [arg min]"] Specifies a minimum string length; the value is invalid if it has fewer than [arg min] characters. Defaults to 0. [def "[const -maxlen] [arg max]"] Specifies a maximum string length; the value is invalid if it has more than [arg max] characters. [def "[const -glob] [arg pattern]"] Specifies a [cmd "string match"] pattern; the value is invalid if it doesn't match the pattern. [def "[const -regexp] [arg regexp]"] Specifies a regular expression; the value is invalid if it doesn't match the regular expression. [def "[const -nocase] [arg flag]"] By default, both [const -glob] and [const -regexp] matches are case-sensitive. If [const -nocase] is set to true, then both [const -glob] and [const -regexp] matches are case-insensitive. [list_end] [call [cmd snit::window] [const validate] [opt [arg value]]] [call [cmd snit::window] [arg name]] [emph "Tk programs only."] Validates Tk window names. The value must cause [cmd "winfo exists"] to return true; otherwise, the value is invalid. It's possible to define subtypes--that is, instances--of [cmd snit::window], but as it has no options at present there's no reason to do so. [list_end] [para] [subsection "Defining Validation Types"] There are three ways to define a new validation type: as a subtype of one of Snit's validation types, as a validation type command, and as a full-fledged validation type similar to those provided by Snit. Defining subtypes of Snit's validation types is described above, under [sectref "Validation Types"]. [para] The next simplest way to create a new validation type is as a validation type command. A validation type is simply an object that has a [method validate] method; the [method validate] method must take one argument, a value, return the value if it is valid, and throw an error with [cmd -errorcode] INVALID if the value is invalid. This can be done with a simple [cmd proc]. For example, the [cmd snit::boolean] validate type could have been implemented like this: [example { proc ::snit::boolean {"validate" value} { if {![string is boolean -strict $value]} { return -code error -errorcode INVALID \ "invalid boolean \"$value\", should be one of: 1, 0, ..." } return $value } }] A validation type defined in this way cannot be subtyped, of course; but for many applications this will be sufficient. [para] Finally, one can define a full-fledged, subtype-able validation type as a [cmd snit::type]. Here's a skeleton to get you started: [example { snit::type myinteger { # First, define any options you'd like to use to define # subtypes. Give them defaults such that they won't take # effect if they aren't used, and marked them "read-only". # After all, you shouldn't be changing their values after # a subtype is defined. # # For example: option -min -default "" -readonly 1 option -max -default "" -readonly 1 # Next, define a "validate" type method which should do the # validation in the basic case. This will allow the # type command to be used as a validation type. typemethod validate {value} { if {![string is integer -strict $value]} { return -code error -errorcode INVALID \ "invalid value \"$value\", expected integer" } return $value } # Next, the constructor should validate the subtype options, # if any. Since they are all readonly, we don't need to worry # about validating the options on change. constructor {args} { # FIRST, get the options $self configurelist $args # NEXT, validate them. # I'll leave this to your imagination. } # Next, define a "validate" instance method; its job is to # validate values for subtypes. method validate {value} { # First, call the type method to do the basic validation. $type validate $value # Now we know it's a valid integer. if {("" != $options(-min) && $value < $options(-min)) || ("" != $options(-max) && $value > $options(-max))} { # It's out of range; format a detailed message about # the error, and throw it. set msg "...." return -code error -errorcode INVALID $msg } # Otherwise, if it's valid just return it. return $valid } } }] And now you have a type that can be subtyped. [para] The file "validate.tcl" in the Snit distribution defines all of Snit's validation types; you can find the complete implementation for [cmd snit::integer] and the other types there, to use as examples for your own types. [para] [section CAVEATS] If you have problems, find bugs, or new ideas you are hereby cordially invited to submit a report of your problem, bug, or idea as explained in the section [sectref {Bugs, Ideas, Feedback}] below. [para] Additionally, you might wish to join the Snit mailing list; see [uri http://www.wjduquette.com/snit] for details. [para] One particular area to watch is using [cmd snit::widgetadaptor] to adapt megawidgets created by other megawidget packages; correct widget destruction depends on the order of the bindings. The wisest course is simply not to do this. [section {KNOWN BUGS}] [list_begin itemized] [item] Error stack traces returned by Snit 1.x are extremely ugly and typically contain far too much information about Snit internals. The error messages are much improved in Snit 2.2. [item] Also see the Project Trackers as explained in the section [sectref {Bugs, Ideas, Feedback}] below. [list_end] [section HISTORY] During the course of developing Notebook (See [uri http://www.wjduquette.com/notebook]), my Tcl-based personal notebook application, I found I was writing it as a collection of objects. I wasn't using any particular object-oriented framework; I was just writing objects in pure Tcl following the guidelines in my Guide to Object Commands (see [uri http://www.wjduquette.com/tcl/objects.html]), along with a few other tricks I'd picked up since. And though it was working well, it quickly became tiresome because of the amount of boilerplate code associated with each new object type. [para] So that was one thing--tedium is a powerful motivator. But the other thing I noticed is that I wasn't using inheritance at all, and I wasn't missing it. Instead, I was using delegation: objects that created other objects and delegated methods to them. [para] And I said to myself, "This is getting tedious...there has got to be a better way." And one afternoon, on a whim, I started working on Snit, an object system that works the way Tcl works. Snit doesn't support inheritance, but it's great at delegation, and it makes creating megawidgets easy. [para] If you have any comments or suggestions (or bug reports!) don't hesitate to send me e-mail at [uri will@wjduquette.com]. In addition, there's a Snit mailing list; you can find out more about it at the Snit home page (see [uri http://www.wjduquette.com/snit]). [para] [section CREDITS] Snit has been designed and implemented from the very beginning by William H. Duquette. However, much credit belongs to the following people for using Snit and providing me with valuable feedback: Rolf Ade, Colin McCormack, Jose Nazario, Jeff Godfrey, Maurice Diamanti, Egon Pasztor, David S. Cargo, Tom Krehbiel, Michael Cleverly, Andreas Kupries, Marty Backe, Andy Goth, Jeff Hobbs, Brian Griffin, Donal Fellows, Miguel Sofer, Kenneth Green, and Anton Kovalenko. If I've forgotten anyone, my apologies; let me know and I'll add your name to the list. [vset CATEGORY snit] [include ../common-text/feedback.inc] [manpage_end]