scwoop

(Simple Composite Widget Object Oriented Package)

Scwoop version 4.1 is a composite widget (also known as mega widget) extension to the great Tk widget library. Scwoop is entirely written in Tcl using the stooop (Simple Tcl Only Object Oriented Programming) extension (stooop provides object oriented facilities modeled after the C++ programming language while following the Tcl language philosophy).

Contents:

About this document

This document contains general information, reference information and live (through the Tcl plug-in) examples designed to help the programmer understand and use the scwoop Tk extension.

A working knowledge of the stooop extension (at http://jfontain.free.fr/) is recommended to understand the code samples in this document.

Introduction

If you have ever tried to build a composite widget using native Tk widgets, you probably found yourself drifting away from the Tk widgets implementation philosophy as you tried to add new configuration options or new features, and adding child widgets or sub-layers would rapidly become unmanageable.

Clearly, what is needed is a way to create composite widgets with a configuration interface as close as possible to the Tk widgets interface. Moreover, a composite widget should be able to be made of Tk native widgets as well as other composite widgets, in a transparent fashion for the programmer. For example, one should be able to embed a combobox composite widget in a file selector composite widget.

Scwoop is an attempt at answering these requirements, using the object oriented composite pattern, which insures that simple and composite objects behave the same. Scwoop is designed to be as easy to use as possible, and tries to provide very good error checking.

Simple example

Let us start with a code sample that will give you some feeling on how scwoop works. Let us create a simple text viewer with 2 scrollbars:
source stooop.tcl ;# or package require stooop 4.1
namespace import stooop::*
source scwoop.tcl ;# or package require scwoop 4.1

class viewer {}

proc viewer::viewer {this parentPath args} composite {
    [new frame $parentPath] $args
} {
    composite::manage $this\
        [new text $widget::($this,path) -state disabled -wrap none] text\
        [new scrollbar $widget::($this,path)] vertical\
        [new scrollbar $widget::($this,path) -orient horizontal] horizontal

    widget::configure $composite::($this,text)\
        -yscrollcommand "$composite::($this,vertical,path) set"\
        -xscrollcommand "$composite::($this,horizontal,path) set"
    set textPath $composite::($this,text,path)
    widget::configure $composite::($this,vertical) -command "$textPath yview"
    widget::configure $composite::($this,horizontal) -command "$textPath xview"

    grid $textPath -sticky nsew
    grid $composite::($this,vertical,path) -column 1 -row 0 -sticky nsew
    grid $composite::($this,horizontal,path) -row 1 -sticky nsew
    grid rowconfigure $widget::($this,path) 0 -weight 1
    grid columnconfigure $widget::($this,path) 0 -weight 1

    composite::complete $this
}

proc viewer::~viewer {this} {}

proc viewer::options {this} {
    return {{-file {} {}} {-text {} {}}}
}

proc viewer::set-text {this text} {
    widget::configure $composite::($this,text) -state normal
    $composite::($this,text,path) delete 1.0 end
    $composite::($this,text,path) insert 1.0 $text
    widget::configure $composite::($this,text) -state disabled
}

proc viewer::set-file {this name} {
    if {[string length $name]==0} return
    set file [open $name]
    viewer::set-text $this [read $file]
    close $file
}

set view [new viewer . -text "please wait..."]
pack $widget::($view,path) -fill both -expand 1
update
widget::configure $view -file /etc/termcap
First we define the viewer constructor. The viewer class must derive from the composite class. The constructor arguments are the Tk parent widget path and optional configuration options, such as "-text foo". The composite base class arguments are the viewer base widget object, in this case a frame, followed by the configuration options. Note that we use the frame class which is a simple wrapper around the native Tk widget frame command (wrappers are included in the scwoop.tcl file).

Next, we let the composite layer manage 1 text object and 2 scrollbar objects. Note that we assign the text, vertical and horizontal names to those widgets, names that we can later use to retrieve their object identifier and Tk path (from here on in this document, the word path refers to the Tk native widget pathname).

Then we configure the text and scrollbar widgets so that they actually work together. Note the use of the widget class configure procedure which provides a Tk configure command like interface (there is also a cget procedure). Also note how we use the widget object identifier (such as $composite::($this,text)) for configuration purposes and the widget path (such as $composite::($this,vertical,path)) for other purposes.

Next, we arrange the widgets using the grid command on the different widget paths.

Finally, we tell the composite layer that it is complete, meaning that no other child widgets will be managed by it.

In this case we built the composite viewer using Tk native widget wrapper objects (text and scrollbar classes), but we could have used any other composite widget class just as easily (for example, the text widget could have been replaced by a htmlText object for HTML document viewing).

The viewer destructor has nothing to do as the composite layer takes care of destroying the widgets that were managed.

The options procedure is mandatory (it is declared as a pure virtual procedure at the composite class level). It must return a list describing all of the available options, with option name, default and current values.

In this case, we chose to handle only the -text and -file switches for demonstration purposes (a real implementation should probably also handle options such as -background, -font, ...).

For each option that the composite widget implements, there must be a corresponding set-option method defined at the derived class (here viewer) level. The set-option methods are automatically invoked as the composite widget is configured. For example, the set-file procedure is invoked with /etc/termcap as argument when the last line of the example script is evaluated.

After the set-text and set-file methods implementation, we start by creating a viewer object using the default Tk toplevel as parent. We pass a -text configuration option at construction time, just like we could do with a native Tk widget. Next we pack the widget using its path. Finally, we display a text file using the -file configuration option switch, again just like we would proceed to configure a native Tk widget.

Class hierarchy

                                widget
            ______________________|__________ _ _ ________
           |         |      |      |     |         |      |
       composite   button canvas entry frame  ...  text toplevel
   ________|______________ _ _
  |        |           |
viewer comboEntry   panner ...
The composite class and the Tk widgets wrapper classes (such as button, label, ...) all derive from the widget base class. Composite widget implementation classes (such as viewer, comboEntry, ...) derive from the composite class.

Widget class

The widget base class provides the configuration interface and the path for native widget wrappers, composite widgets and their descendants:

widget

The widget class should never be instantiated, that is no widget object should be created directly with the new operator.

The configure member procedure is used to query or modify the configuration options of the widget object, very much like a native Tk widget is configured. For example:

$ wish
% source stooop.tcl
% namespace import stooop::*
% source scwoop.tcl
% source viewer.tcl
% set view [new viewer . -text "please wait..."]
1
% widget::configure $view
{-file {} {}} {-text {} please wait...}
% widget::configure $view -file /etc/termcap
% widget::configure $view -file
-file {} /etc/termcap
%
The configure procedure can also be used to query or modify the configuration options of any composite widget descendant (child, grand-child, ...) using its name (see manage procedure in composite class documentation below), as in:
class fileSelector {}
proc fileSelector::fileSelector {this parentPath args} composite {
    ...
} {
    ...
    composite::manage $this\
        [new label $widget::($this,path) -text {Selected file:}] fileLabel\
        [new entry $widget::($this,path) -width 0] fileEntry
    ...
}
...
set f [new fileSelector .]
widget::configure $f fileLabel -background bisque
composite::configure $f fileEntry -width 0 -state normal
Note that it is actually the configure procedure at the composite level that is (and can be) used when configuring descendant widgets, as the configure procedure is declared as virtual at the widget class level. Therefore, you can use either one.

Except for the stooop object specific syntax, the configure member procedure behaves much like any native Tk widget configure command, except that option names and classes are not included in the returned list of a configure invocation with no arguments.
In the case of any Tk widget wrapper class, the valid options are those of the corresponding native Tk widget. For the composite widgets, the valid options are specified in the options procedure (see below).

The cget member procedure is used to query an option current value, as shown below:

$ wish
% source stooop.tcl
% namespace import stooop::*
% source scwoop.tcl
% set l [new label .]
1
% widget::cget $l -font
Helvetica -12 bold
%
As for the configure procedure, the cget procedure can also used to query option values of any composite widget descendant, as in:
% set f [new fileSelector .]
% widget::configure $f fileLabel -background bisque
% puts [widget::cget $f fileLabel -background]
gray
% puts [composite::cget $f fileLabel -background]
gray
%
Note that it is actually the cget procedure at the composite level that is used when retrieving descendant widgets configuration, as the cget procedure is declared as virtual at the widget class level. Therefore, you can use either one.

Except for the stooop object specific syntax, the cget member procedure behaves exactly like any native Tk widget cget command.

The path data member is the Tk pathname for the widget object. It should be considered read-only and is used with Tk geometry managers (such as pack, grid, ...) and other Tk commands, such as bind, ... Note that contrary to the Tk native widget commands, the path is automatically generated.

$ wish
% set l [new label .]
1
% pack $widget::($l,path)
%

Tk widgets wrapper classes

For each Tk native widget command, there is a corresponding scwoop wrapper class that accepts exactly the same options as the original Tk widget.

Before the optional configuration options, the constructor takes the Tk parent path as first parameter, as in:

$ wish
% source stooop.tcl
% namespace import stooop::*
% source scwoop.tcl
% toplevel .top
% set l [new frame .top -relief sunken -background white]
1
%
As with all scwoop widgets, configuration modification or query is done through the configure and cget member procedures.

Note: wrappers for the great tkTable and BLT widgets are also included in the scwoop.tcl file.

Composite class

The composite class serves as base class for the user built composite widgets. It provides facilities for managing child widgets and options through a simple interface.

The composite class constructor takes the composite widget base widget as first parameter, followed by the usual optional configuration option / value pairs. The base widget is usually a frame or a toplevel class object that serves as parent of all the child widgets that make the composite widget. For example:

proc labelledEntry::labelledEntry {this parentPath args} composite {
    [new frame $parentPath -borderwidth 0] $args
} {
    composite::manage $this\
        [new label $widget::($this,path)] label\
        [new entry $widget::($this,path)] entry
    ...
}
The frame parent is accessed using the widget layer path data member.

Note that the base widget can be of any scwoop class. For example, it could be a scroller composite widget.

The composite class members available to the widget programmer are:

composite

The manage procedure is used to register one or more child widgets at the composite layer level. It takes one or more widget / name pairs as argument. The name associated with each widget object identifier can be any name as long as it is unique (actually verified by the manage procedure). It is later used to retrieve the corresponding child widget object identifier and path, as in the following example:
class passwordEntry {}

proc passwordEntry::passwordEntry {this parentPath args} composite {
    [new frame $parentPath -borderwidth 0] $args
} {
    composite::manage $this\
        [new label $widget::($this,path)] string\
        [new entry $widget::($this,path)] input
    pack $composite::($this,string,path) $composite::($this,input,path)
    widget::configure $composite::($this,string) -text "password :"
    widget::configure $composite::($this,input) -show *
    ...
}
After managing a label and an entry widget, we pack them using theirs paths, then configure them using their object identifiers.

The unique name passed to the manage procedure for each widget also serves when configuring the composite child widgets, as described later in this document. Please note that the base name is reserved for the composite widget base widget (the one passed to the composite layer from the derived widget constructor), and can be used to access configuration of the implicitly managed base widget. Also, child widget names may not start with a dash (or minus, -) character.

The complete procedure is used to tell the composite layer that no more child widgets will be managed, meaning that all the elements necessary to build the composite widget have been created. At this time, the initial configuration of the composite widget occurs, using default option values (see options procedure) eventually overridden by the construction time options, passed at the time of the new operator invocation. The complete procedure must be called once only, usually around or at the end of the composite widget constructor, as in the following example:

proc passwordEntry::passwordEntry {this parentPath args} composite {
    [new frame $parentPath -borderwidth 0] $args
} {
    composite::manage $this\
        [new label $widget::($this,path)] string\
        [new entry $widget::($this,path)] input
    ...
    composite::complete $this
}
The complete data member (not to be confused with the complete procedure) is a boolean. Its initial value is false and it is set to true at the very end of the complete procedure. It becomes useful when some options should not be dynamically set (see the frequently asked questions for an example).

The options procedure must return the configuration description for all options that the composite widget will accept. It is a pure virtual member procedure and therefore its implementation is mandatory in the derived class layer.

The returned value is a list of option descriptions, an option description being a list of the option switch name, followed by the default and current values. Note that the current value can be omitted to force initial configuration. For example:

proc arrowButton::options {this} {
    return [list\
        [list\
            -background $widget::option(button,background)\
            $widget::option(button,background)\
        ]\
        [list -command {} {}]\
        [list -state normal]\
        [list -takefocus 1]\
        [list -width $widget::option(scrollbar,width) 20]\
    ]
}
In this case, configuration is forced on the state and takefocus options, that is the corresponding set-option procedures will be invoked when the composite widget is constructed (set set-option procedures below).

For the state option, since there is no initial value, the set-state procedure is called with the default value (normal) as argument. For the width option, since the initial value differs from the default value (the Tk native scrollbar width, from which we use the bottom arrow), the set-width procedure is called with the initial value as argument (20).

For the other options, since the initial values (last elements of the option lists) are identical to their default values, the corresponding set-option procedures will not be invoked. It is the programmer's responsibility to insure that the initial option values are correct.

All the native widgets default values are available in the widget class option array. The index used is the widget type followed by the option name without the dash, separated by a comma.

For example:

proc arrowButton::options {this} {
    return [list\
        ...
        [list -width $widget::option(scrollbar,width) 20]\
    ]
}
In this case, the default width value if the default width of a scrollbar widget.

Note that the default option values depend on the option database settings (see Tcl/Tk option manual) but are determined once only, then cached for performance. What that means is that setting option database values should be done before (through a resource file) or at the beginning of your application, in order to prevent widgets aspect discrepancies.

The set-option procedures may be viewed as dynamic virtual functions. There must be one implementation per supported option, as returned by the options procedure. For example:

proc arrowButton::options {this} {
    return [list\
        [list\
            -background\
            $widget::option(button,background)\
            $widget::option(button,background)\
        ]
        ...
    ]
}

proc arrowButton::set-background {this value} {
    ...
}
Since the -background option was listed in the options procedure, a set-background procedure implementation is provided, which of course would proceed to set the background of some or all of the widgets that make the arrowButton composite widget.

As you add a supported option in the list returned by the options procedure, the corresponding set-option procedure may be called as soon as the composite widget is complete, which occurs when the composite level complete procedure is invoked. For example:

proc arrowButton::arrowButton {this parentPath args} composite {
    ...
} {
    ...
    composite::complete $this
}

...

proc arrowButton::options {this} {
    return [list\
        [list -background $widget::option(button,background)]\
        [list -command {} {}]\
        [list -state normal]\
        [list -takefocus 1]\
        [list -width 15]\
    ]
}

...

proc arrowButton::set-background {this value} {
    ...
}
proc arrowButton::set-command {this value} {
    ...
}
proc arrowButton::set-state {this value} {
    ...
}
proc arrowButton::set-width {this value} {
    ...
}

new arrowButton .
In this case, a new arrowButton is created with no options, which causes the arrowButton constructor to be called, which in turns calls the composite level complete procedure after all child components have been created. At this point, since there are no initial values in any option list in the options procedure, the set-background procedure is called with its default value of $widget::options(button,background) as parameter, followed by the set-command call with {} value, set-state with normal value and finally with set-width with 15 as parameter. This is a good way to test the set-option procedures when debugging, and when done, just fill-in the initial option values.

The composite layer checks that an option is valid (that is, listed in the options procedure) but obviously does not check the validity of the value passed to the set-option procedure, which should throw an error (for example by using the Tcl error command) if the value is invalid.

The composite layer also keeps track of the options current values, so that a set-option procedure is called only when the corresponding option value passed as parameter is different from the current value (see -option data members description).

The try procedure provides a very simple way of supporting an option. It will try to apply the option and its value (passed as parameter) to all the components of the composite widget (base and child widgets). However, no error checking is done since all errors are caught in the try procedure implementation. For example, setting the background color of all elements of a composite widget can be done as follows:

proc passwordEntry::set-background {this value} {
    composite::try $this -background $value
}
The base data member is the base widget object identifier, the widget that is passed to the composite constructor when the composite widget is created, as in the following example:
class viewer {
    proc viewer {this parentPath args} composite {
         [new frame $parentPath] $args
    } {
        ...
    }

    ...

    proc set-relief {this value} {
        widget::configure $composite::($this,base) -relief $value
    }
}
In this case, the set-relief procedure configures the frame base widget of the viewer composite widget. Reminder: the composite base widget path can be accessed using the path member at the widget class layer.

Alternately, one could have written, using the child configuration syntax:

    proc set-relief {this value} {
        widget::configure $this base -relief $value
    }
or even:
    proc set-relief {this value} {
        $composite::($this,base,path) configure -relief $value
        #     equivalent to:
        # $widget::($this,path) configure -relief $value
    }
Note that the base,path member is present for consistency sake, but is not needed.

There is one child object identifier data member for each child widget that is managed by the composite widget. The data member name used is the one passed to the manage procedure when a child widget is managed. It is a read-only data member. For example:

class viewer {
    proc viewer {this parentPath args} composite {
        [new frame $parentPath] $args
    } {
        composite::manage $this\
            [new text $widget::($this,path) -state disabled -wrap none] text\
            [new scrollbar $widget::($this,path)] vertical\
            [new scrollbar $widget::($this,path) -orient horizontal] horizontal
        ...
    }

    ...

    proc set-background {this value} {
        widget::configure $composite::($this,text) -background $value
        widget::configure $composite::($this,vertical) -background $value
        widget::configure $composite::($this,horizontal) -background $value
    }
}
Here, when the background color is changed, the text, vertical and horizontal data members are used to change the corresponding widgets background color.

Alternately, one could have written, using the child configuration syntax:

    proc set-background {this value} {
        widget::configure $this text -background $value
        widget::configure $this vertical -background $value
        widget::configure $this horizontal -background $value
    }
There is also one child,path widget Tk path data member for each child widget that is managed by the composite widget. It is a read-only data member. The data member first part (before the comma) used is the name passed to the manage procedure when a child widget is managed. The second part (after the comma) is always the string path. This data member can be used for non-configuration Tk commands, as in the following example:
proc scrollList::scrollList {this parentPath args} composite {
    [new button $parentPath -borderwidth 0 -state disabled -takefocus 0] $args
} {
    composite::manage $this\
        [new listbox $widget::($this,path) -width 0 -highlightthickness 0] listbox
    ...
    bind $widget::($this,path) <FocusIn> "focus $composite::($this,listbox,path)"
    ...
}
Here, we pass the focus on to the listbox child widget when the composite widget gets the focus. The listbox Tk widget wrapper path is used for this purpose.

The -option data member is the option current value. There is one for each option listed in the options procedure. It is a read-only value which the composite layer checks against when an option is changed. It is rarely used at the composite derived layer, except in the few cases, such as in the following example:

...

proc arrowButton::options {this} {
    return {
        ...
        {-command command Command {}}
        ...
    }
}

proc arrowButton::set-command {this value} {}

proc arrowButton::invokeCommand {this} {
    eval $composite::($this,-command)
}
In this case, the command to be executed when the button is activated is stored at the composite layer level (this is why the set-command has nothing to do) and later retrieved in the invokeCommand procedure, itself invoked by appropriate bindings, for example.

Finally, the componentNames member procedure returns the composite widget components (including the base component) in managing order. It is used in the configurationInterface widget described later in this document.

Some widgets

The scwoop distribution includes a dozen widgets, which you can all test using the following command (this is an example on a UNIX box):
$ wish confdemo.tcl
You can also run separate demonstrations by typing (example for the arrowButton widget):
$ wish arrowbud.src
After clicking on the button corresponding to the widget that you would like to test, a window appears. You can then experiment with the different options by first selecting the composite widget component in the listbox, then typing new values in the corresponding entries and then hitting the Return (or Enter) key. Right after the selection of a child component, it is briefly highlighted with a red border while the option entries are updated. Setting an option also flashes the entry as the corresponding component(s) are configured.

A little documentation is provided below for those options that do not directly derive from native Tk widget implementations.
Should you need more options for the following widgets, please let me know. New composite widget ideas and contributions are also welcomed.

arrowButton

(source: arrowbut.tcl, demonstration: arrowbud.src)

The arrowButton is a square button type widget with an embedded arrow, which resizes nicely as the button size changes. The looks come from the Windows implementation of such widget, since UNIX has none.

The options are pretty much those of a native Tk button.

You may pick a direction for the arrow using the direction options: valid values are left, right, up and down (or any abbreviations). The repeatdelay option behaves as in the Tk scrollbar native widget.

comboButton

(source: combobut.tcl, demonstration: combobud.src)

The comboButton is made of an arrowButton which when activated causes a listbox (actually a scrollList widget) framed in a toplevel to drop down. The user can then select from the proposed choices.

The command option specifies a script which will be evaluated when the user selects a line from the listbox. The selected line string is then appended to the script before evaluation.

The list option takes a list of choices as arguments. They are then used as the listbox elements.

The listheight specifies the number of lines displayed in the listbox. A value of 0 tells the listbox to be as high as there are elements. A scrollbar is automatically added if there are more lines to select than can be visible.

The reference option takes a widget path as argument. The listbox, when popped down will place itself right below the reference widget and match its width (without the highlight borders if there are any). This option is used in the comboEntry implementation.

comboEntry

(source: comboent.tcl, demonstration: comboend.src)

The comboEntry class adds a Tk entry widget to the comboButton widget in order to display and eventually edit the currently selected item.

The command option specifies a script which will be evaluated when the user selects a line from the listbox, or hits the return key while the entry has the focus. The entry string is then appended to the script before evaluation.

The list option is identical to the comboButton option.

The editable boolean option allows or not the entry to be editable.

The justify option sets the entry text justification either left or right.

labelledFrame

(source: labelfra.tcl, demonstration: labelfrd.src)

The labelledFrame widget is a frame with a decoration (groove by default) and a title label placed either above or below the enclosed widget(s).

The anchor option defines the position of the text. The possible values are nw, n, ne, sw, s or se.

The text option defines the text to be displayed as title.

The labelledFrame widget path should be used for managing the geometry of your widgets, as the following example shows:

set frame [new labelledFrame .]
pack $widget::($frame,path)
pack [radiobutton $widget::($frame,path).choice1 -text 1 -variable choice -value 1]
...

notebook

(source: notebook.tcl, demonstration: notebood.src)

The notebook widget is very useful for configuration dialog boxes, for example. It allows the user to select one page from a collection of pages by activating its tag (through mouse or keyboard action). The scwoop notebook widget is capable of handling multiple rows of pages.

The columns option defines the maximum number of pages per row. Rows are automatically created as pages are added. The columns option cannot be set dynamically and defaults to 5. Pages can be added but not deleted.

The columnsoffset option defines the horizontal distance between two consecutive rows, in order to give the notebook a 3D look. The columnsoffset option cannot be set dynamically.

The height and width options define the size of the whole notebook. Zero values tell the notebook to adapt to the size of the contained widgets.

The newPage member procedure returns a frame which can then be used for the geometry management (pack, place, ...) of the user widgets, as the example below shows:

set book [new notebook .]
pack $widget($book,path)
set frame [notebook::newPage $book "page title"]
pack [frame $frame.frame] -padx 10 -pady 10
...
Note that it is easier, for geometry management, to make your widgets child of the frame widget returned by the newPage procedure (but you do not have to).

Problem: a page frame does not resize along with its page: it is simply centered or clipped depending on whether the page is larger or smaller than the enclosed frame. I have yet to find a way to do this without generating infinite resizing loops...

optionMenu

(source: optimenu.tcl, demonstration: optimend.src)

The optionMenu widget emulates the Motif and/or Java choices type widget. A list of choices appears when the user clicks on the little stub button on the widget right side.

The command option specifies a script which will be evaluated when the user selects a line from the choices window. The selected line string is then appended to the script before evaluation.

The choices option takes a list of choices as arguments.

The text option allows the user to force the currently displayed string value.

panner

(source: panner.tcl, demonstration: pannerd.src)

The panner allows horizontally or vertically split frames with handles that can be used for moving the separation between adjacent frames.

The handlesize option defines the size of the square button handle.

The orient option defines how the base frame is split: vertically with a horizontal separator, or horizontally with a vertical separator. vertical and horizontal are the two possible values (abbreviations accepted).

The handleplacement option can be used to position the handle button in the separator. For example, a value of 0.9 would place the handle at 90% of the width of an horizontal separator.

The panes option specifies the number of panes and therefore the number of separators (1 less than the number of panes). This option defaults to 2.

scroll

(source: scroll.tcl, demonstration: scrolld.src)

The scroll widget can display any native Tk widget (such as canvas, listbox, text, tkTable, ... ) with automatic scrollbars (which only appear when needed). Any widget featuring the -xscrollcommand, -yscrollcommand, xview and yview interface is a candidate for the scroll widget.

The scroll class constructor takes the scrollable widget class as first mandatory parameter (text, canvas, ..., see example below).

The height and width options define the size of the container frame (which includes the scrollbars when displayed).

The automatic option is a boolean that specifies whether scrollbars should appear and disappear as needed. It defaults to true.

The horizontal and vertical options are used to force the display status of the corresponding scroll bars. The default is true for both.

The native scroll widget path can be found in the scrolled,path data member, as the following example shows:

set scroll [new scroll listbox . -width 100 -height 100]
pack $widget::($scroll,path) -fill both -expand 1
set list $composite::($scroll,scrolled,path)
$list insert 0 {first entry} {second entry} {third entry} {fourth entry} {last entry}

scroller

(source: scroller.tcl, demonstration: scrolled.src)

The scroller widget can display any Tk widget and handles scrolling with automatic scrollbars (which only appear when needed). Note: for Tk widgets with native scrolling interface (such as canvas, listbox, text, tkTable, ...), use the scroll class instead.

The height and width options define the size of the container frame (which includes the scrollbars when displayed).

The display member procedure is used to tell the scroller widget to handle the widget given as parameter, as the following example shows:

set container [new scroller . -width 300 -height 100]
pack $widget::($container,path)
set message [message $widget::($container,path).message]
scroller::display $container $message
Note that it is recommended to make the scrolled widget a child of the scroller widget, for easier geometry management.

scrollList

(source: scrolist.tcl, demonstration: scrolisd.src)

The scrollList widget only differs from the Tk listbox widget in its automatic side scrollbar (only appears when needed).

The list option defines the items to be displayed inside the listbox..

For convenience sake, all the listbox component native commands (activate, bbox, curselection, delete, get, index, insert, nearest, scan, see, selection and size) are wrapped, as the following example shows:

set list [new scrollList .]
scrollList::activate $list 0                      ; # can be used instead of
$composite::($list,listbox,path) activate $list 0 ; # the native widget command

spinEntry

(source: spinent.tcl, demonstration: spinentd.src)

An entry combined with 2 arrowButton widgets for numeric value selection, from a list or from a range.

The command option specifies a script which will be evaluated whenever the user selects a new value, either by using the arrow buttons or by directly entering the value and hitting the enter key. The entry contents string is then appended to the script before evaluation.

The list option specifies a list of possible values for the entry. Depressing the arrow buttons will make the entry display the next value (either up or down) in the list. This option is incompatible with the range option.

The repeatdelay option specifies a time in milliseconds. If either one of the arrow buttons is depressed for longer than this time, then the entry jumps to the next value.

The editable boolean option allows or not the entry to be directly editable.

The range option specifies a possible range of values for the entry along with an increment used to calculate the next value to display in the entry (either up by adding the increment to the current value, or down by subtracting the increment). The range switch argument is a list of 3 values: the lowest possible value, the highest and the increment.

The side option specifies whether the entry field should be placed to the right or to the left of the arrow buttons. The possible values are left (the default) or right.

There are also 2 member procedures, set and get, for directly setting and retrieving the entry's contents.

Note: if you have Tcl/Tk 8.4, you can the spinbox native widget instead.

widgetTip and topLabel

(source: widgetip.tcl, demonstration: widgetid.src)

The widgetTip widget appears after the cursor has rested on the target widget for about half a second. A short help tip then becomes visible. It then disappears whenever the cursor is moved outside the widget or a mouse button or key is pressed.

The widgetTip object is not really a widget (it is actually derived from the switched class in the stooop package), but rather uses a single topLabel widget (a yellowish label in a toplevel). The topLabel object label string is then changed on the fly depending on the target widget that the mouse pointer lies on.

Contrary to the other scwoop widgets, the widgetTip constructor takes no parent path argument. The widgetTip path option specifies the path of the Tk widget that should receive help.

The widgetTip widget automatically deletes itself when its target is destroyed. There is therefore no need to save its identifier, as the example below shows:

button .exit -text Exit -command exit
switched::configure [new widgetTip -path .exit -text "click here to exit"] -font {Helvetica -10}
Note that it can be configured using the switched interface, with a behavior similar to the composite interface.

configurationInterface

This composite widget is used for debugging and demonstrating. It automatically discovers the widget components and options of the composite widget that it manages. A listbox for selecting a component and a set of entries, one for each component option are available for interacting with the target composite widget and its configuration. All the applets in this document use the configurationInterface widget.

The container member procedure returns a frame widget, which can be used as a container for the target composite widget. The manage member procedure is then used for handling the target widget configuration, as the example below shows:

set options [new configurationInterface .]
set frame [configurationInterface::container $options]
set button [new arrowButton $frame]
pack $widget::($button,path)
configurationInterface::manage $options $button
The configurationInterface widget currently takes no options.

Utility classes

bindings

Provides a way to create a unique new set of bindings in a specific position within the bindtags.

For example, in the widgetTip class implementation:

...
set widgetTip::($this,bindings) [new bindings $targetPath 0]
bindings::set $widgetTip::($this,bindings) <Enter> "widgetTip::poll $this"
bindings::set $widgetTip::($this,bindings) <Destroy> "delete $this"
...

proc widgetTip::~widgetTip {this} {
    delete $widgetTip::($this,bindings)
}
First, a new tag is inserted first (index 0) in the binding tags sequence. Next, a couple of bindings are set, with, for example, an automatic object deletion if the widget is destroyed. Finally, in the destructor, binding tags are restored by deleting the bindings object.

Upgrading from version 3 to 4

They are 2 major changes in scwoop 4.0: What does this practically mean if you want to upgrade your mega widget code? First replace all the occurrences of default,name widget members with corresponding option array values. For example, replace:
$widget::(default,ScrollbarWidth)
by
$widget::option(scrollbar,width)
related to:
.some.scrollbar.widget cget -width

and
$widget::(default,ButtonBackgroundColor)
by
$widget::option(button,background)
related to:
.some.button.widget cget -background
Note that in this last case, the option name is slightly different. But with the new scwoop version, the option name information is readily available in the native widgets manual pages, instead of having to search the option name in the scwoop.tcl source code file.

Next, remove all the option name and classes from the lists in your option procedures. For example:

proc myMegawidget::options {this} {
    return [list\
        [list\
            -foreground foreground Foreground\
            $widget::(default,ScaleForegroundColor)\
            $widget::(default,ScaleForegroundColor)\
        ]\
        [list -command command Command {} {}]\
        [list -state state State normal]\
        [list -takefocus takeFocus TakeFocus 1]\
        [list -width width Width $widget::(default,CanvasWidth) 200]\
    ]
}
becomes:
proc myMegawidget::options {this} {
    return [list\
        [list\
            -foreground $widget::option(scale,foreground)\
            $widget::option(scale,foreground)\
        ]\
        [list -command {} {}]\
        [list -state normal]\
        [list -takefocus 1]\
        [list -width $widget::option(canvas,width) 200]\
    ]
}
And that is it!

Frequently Asked Questions

There are many options, such as background, foreground, ... that are fastidious to implement. Isn't there an easier way?

Yes. For those options that are not critical, such as color options, the composite layer try procedure can be used. It will simply try the option and its value on all the components of the composite widget. For example, non-critical options handling could be implemented as follows:
foreach option {-background -highlightbackground -highlightcolor -troughcolor} {
    proc arrowButton::set$option {this value} "
        composite::try \$this $option \$value
    "
}

I want my composite widget to handle the focus as a whole, how do I do it?

You can use a frame as base widget with a highlight thickness of a button. It will react properly when focus is set through keyboard traversal, that is it will show a highlighted border and pass the focus to the child widget you choose, using a proper bind instruction, as the following example, from the scrollList class, shows:
proc scrollList::scrollList {this parentPath args} composite {
    [new frame $parentPath -highlightthickness $widget::option(button,highlightwidth)]
    $args
} {
    composite::manage $this [new listbox $widget::($this,path) -highlightthickness 0] listbox
    bind $widget($this,path) <FocusIn> "focus $composite::($this,listbox,path)"
    ...
}

I want to create a dialog box composite widget, is it possible to use a toplevel widget as base widget?

Yes. There is absolutely no restriction for the class of your composite base widget. It can be a Tk native widget wrapper, such as a toplevel or another composite widget that you created (look at the dialog class implementation in the moodss application source).

I noticed that the Tk commands bind, pack, ... act on the widget object path instead of the widget identifier, as do the configure and cget methods. Why this difference?

The configure and cget are widget commands at the Tk level, that is they actually are an argument to the native Tk widget path command, so they map nicely to a class method, whereas the bind, pack, ... are Tk commands that either do not always take a Tk widget path as argument or may take more than one Tk widget path as arguments.

The Tk native widget wrappers or the composite widgets implementations take a Tk widget path as construction argument. Why not a widget object identifier?

Otherwise it would be impossible to use a scwoop widget in a Tk native widget only environment. It would be impossible to find a widget object as parent.

Does scwoop provide child widget configuration at any depth level?

Yes. Just use the child widget name (that was passed to the composite::manage procedure) as the configure procedure first parameter, as in:
proc comboButton::comboButton {this parentPath args} composite {
    ...
} {
    ...
    composite::manage $this\
        [new scrollList $shellPath -setgrid 1 -highlightthickness 0] scroll
    ...
}

proc scrollList::scrollList {this parentPath args} composite {
    ...
} {
    ...
    composite::manage $this\
        [new listbox $widget::($this,path) -width 0 -highlightthickness 0] listbox
    ...
}

...

set c [new comboButton .]
composite::configure $c scroll -background bisque
composite::configure $c scroll listbox -selectmode extended -height 5
In the example above, we first set the background of the scrollList child of the comboButton, then we configure the listbox child of the scrollList child (grand-child of the comboButton).

How do I choose the default and initial values in the options procedure?

Just use Tk native widgets option values available in the option array in the widget namespace.

The Tk option database command is not supported, why?

There is no way (to my knowledge :-) to retrieve values from the option database for things other than native widgets. However, any database option setting will have an effect on all the native widget components of a mega widget. For example, as in following resource file extract:
*background: bisque
*Foreground: brown
...
which will affect all component widgets with no hardcoded background and foreground settings.

What is the composite descendant widget configuration feature useful for?

Mainly for options not directly supported at the composite level (in the options procedure). Incidentally, major options should be handled at the composite level, or if they apply to 2 or more component widgets, whereas rarely used or specific options can be handled using the descendant widget configuration syntax. Furthermore, options at the composite level (defined in the options procedure) can be set at construction time, which makes them more practical to use, compared to the descendant widget configuration, which must be done once the composite widget is created.

Platform dependent options, such as colors, ..., should be, in my opinion, left alone: no switches should be provided for them for your composite widget. However the composite descendant widget configuration feature allows the users who still want to play with them to do so (see also the try procedure at the composite level).

I want some options to be settable at construction time only, not dynamically: how can this be done?

You should check the composite class complete data member (a boolean), as in the following example:
proc notebook::set-columnoffset {this value} {
    if {$composite::($this,complete)} {
        error {option -columnoffset cannot be set dynamically}
    }
    ...
}

Why do I need to use the configure widget level procedure to configure my child widgets?

It makes the code more generic, independent of whether you use composite widgets or plain Tk widgets to implement your composite widget. For example:
proc viewer::viewer {this parentPath args} composite {
    [new frame $parentPath] $args
} {
    composite::manage $this\
        [new text $widget::($this,path) -state disabled -wrap none] text\
        [new scrollbar $widget::($this,path)] vertical\
        [new scrollbar $widget::($this,path) -orient horizontal] horizontal

    $composite::($this,text,path) configure\
        -yscrollcommand "$composite::($this,vertical,path) set"\
        -xscrollcommand "$composite::($this,horizontal,path) set"
    ...
Here, we know that the text widget is a native Tk widget, so we directly use the ...,path composite level data member to configure it. Why not? it should be faster than invoking the widget::configure virtual procedure. But if one day we decide that the viewer should also be able to display HTML data, and by chance, we have a htmlText composite widget lying around, the code would have to be changed in 2 places instead of 1, since directly configuring the text,path for the htmlWidget widget would probably produce undefined results:
proc viewer::viewer {this parentPath args} composite {
    [new frame $parentPath] $args
} {
    composite::manage $this\
        [new htmlText $widget::($this,path) -state disabled -wrap none] text\
        [new scrollbar $widget::($this,path)] vertical\
        [new scrollbar $widget::($this,path) -orient horizontal] horizontal

    widget::configure $composite::($this,text)\
        -yscrollcommand "$composite::($this,vertical,path) set"\
        -xscrollcommand "$composite::($this,horizontal,path) set"
    ...
We would also have to track all occurrence of the direct configuration of the text widget and fix them.

However, if you are sure that some of the child widgets will remain native Tk widgets, use their paths to configure them, as it is faster.

Can I use scwoop with the Tcl/Tk netscape Web browser plug-in?

No, as the current version of scwoop only runs on a Tcl 8.3 core.

The 2.6 version of scwoop does work with the plugin though: grab it from my homepage.

Does scwoop support other Tcl extensions?

Yes, for example scwoop has been successfully tested with the great BLT extension (BLT widget wrappers are included in the scwoop.tcl file). If the extension follows the Tk widget command syntax, you only need to add wrappers (use the Tk native widget implementation as model from the scwoop.tcl file) to make them scwoop compatible.

A compatibility problem with Tix (which uses its own object oriented scheme) was found and fixed in the stooop.tcl file (version 2.2.3 and above, left commented out by default).

I like the composite class implementation, with the automatic management of switches and their default options. I would like to use a similar scheme for non graphical classes: is it possible to use the composite class?

Take a look at the switched class in the stooop package (version 3.1 and above): it is a generic version of the composite class and follows exactly the same philosophy, with all graphical related code removed. You may use it as a base class for any switched type of object class. It is for example used in the drag'n'drop implementation within the moodss application (which you may find in my home page).

I do not like that Motif look, how do I make all widget borders thinner?

Just add the following lines at the beginning of your code:
option add *BorderWidth 1 ;# reduce all widgets border width
option add *Canvas.BorderWidth 0 ;# restore original values for canvas
option add *Frame.BorderWidth 0 ;# frame,
option add *Toplevel.BorderWidth 0 ;# and toplevel
option add *ScrollbarWidth 12 ;# scrollbars do not need to be so wide

Note: you could also use an X Window resource file for all your Tcl applications.



Send your comments, bug reports, complaints, ... to Jean-Luc Fontaine