**Introduction** [DDG] 2020-04-06: If you write mega widgets your widgets become fast to specialized. You add features and behaviours to your widget and some time later you observe that you have to create a similar widget and you are extracting those specializations from your previous widgets. After reading [Designing SNIT widgets as mixins], I decided to do a few sample implementations for the [ttk::treeview] widget to check the concept. The widget adaptors are just adding a small set of functionality and can be added by nesting at object creation to the ttk::treeview widget. Here an hypothetical example which creates a filebrowser with letter search facility and automatic banding stripes added after each insert: ====== set filebrowser [filebrowser [filtersearch [bandtable [ttk::treeview .tv]]] -directory .] ====== The nice thing is, that you can use the bandtable or filtersearch widget adaptors for any other treeview widget. Mixins are an additional concept to inheritance and delegation which allow you to add behaviour on the fly to existing objects. I know that [tclOO] has support for mixins but I have not seen a mega widget written using tclOO using this concept. **Implementation Examples** Below a few sample mixins for the [ttk::treeview] widget. ====== package require Tk package require snit namespace eval dgw {} package provide dgw::tvfilebrowser # widget adaptor which does a banding of the ttk::treeview # widget automatically after each insert command snit::widgetadaptor ::dgw::tvband { delegate option * to hull delegate method * to hull # problem: # can't avoid delegating insert as if it is # overerwritten parent insert can't be called # solved using trace constructor {args} { installhull $win $win tag configure band0 -background #FFFFFF $win tag configure band1 -background #DDEEFF trace add execution $win leave [mymethod wintrace] $self configurelist $args } method wintrace {args} { set path [lindex [lindex $args 0] 0] set meth [lindex [lindex $args 0] 1] if {$meth eq "insert"} { set parent [lindex [lindex $args 0] 2] set index [lindex [lindex $args 0] 3] set item [lindex [$path children $parent] $index] if {$index eq "end"} { set i [llength [$path children $parent]] } else { set i $index } set t [expr { $i % 2 }] $path tag remove band0 $item $path tag remove band1 $item $path tag add band$t $item } } } # widget adaptor which allows forward searching in a ttk::treeview # with typing beginning letters of entries matching first column text # further has bindings of Home and End key snit::widgetadaptor ::dgw::tvksearch { delegate option * to hull delegate method * to hull variable LastKeyTime [clock seconds] variable LastKey "" constructor {args} { installhull $win bind $win [mymethod setSelection 0] bind $win [mymethod setSelection end] bind $win [mymethod ListMatch %A] $self configurelist $args } method setSelection {index} { $self focus [lindex [$self children {}] $index] $self selection set [lindex [$self children {}] $index] focus -force $self $self see [lindex [$self selection] 0] } method ListMatch {key} { if [regexp {[-A-Za-z0-9]} $key] { set ActualTime [clock seconds] if {[expr {$ActualTime-$LastKeyTime}] < 3} { set ActualKey "$LastKey$key" } else { set ActualKey $key } set n 0 foreach i [$win children {}] { set name [lindex [$win item $i -value] 0] if [string match $ActualKey* $name] { $win selection remove [$win selection] $win focus $i $win selection set $i focus -force $win $win see $i set LastKeyTime [clock seconds] set LastKey $ActualKey break } else { incr n } } } } } # a file browser widget as widget adaptor # could may be better a snit::widget # as it is already quite specialized # however writing it as a adaptor allows nesting # so banding widget adaptor can go intern # this is required as within the constructor # $self browseDir is called # the banding must be installed before this is called snit::widgetadaptor ::dgw::tvfilebrowser { option -dummy "" option -filepattern ".+" option -directory "." option -browsecmd "" option -fileimage fileImg delegate option * to hull delegate method * to hull except browseDir variable LastKeyTime [clock seconds] variable LastKey "" constructor {args} { ttk::style configure Treeview.Item -padding {1 1 1 1} installhull $win ;# using ttk::treeview $win configure -columns [list Name Size] -show [list tree headings] $win heading Name -text Name $win heading Size -text Size $win column Name -width 60 $win column Size -width 30 $win column #0 -width 35 -anchor w -stretch false bind $win [mymethod fbOnClick %W %x %y] bind $win [mymethod fbReturn %W] bind $win [mymethod browseDir ..] $win tag configure hilight -foreground blue $self configurelist $args $self browseDir $options(-directory) } typeconstructor { image create photo movie -data { R0lGODlhEAAQAIIAAPwCBARCRAQCBASChATCxATCBASCBAAAACH5BAEAAAAA LAAAAAAQABAAAANHCLrc/izISauYI5NduvlXMIjEQBSnUYCYxnmsSJrouhqh 6J4wLo0mWuqWy5heN58seBrGdEdeMgQsNW0ggXbL7Qog4HDDnwAAIf5oQ3Jl YXRlZCBieSBCTVBUb0dJRiBQcm8gdmVyc2lvbiAyLjUNCqkgRGV2ZWxDb3Ig MTk5NywxOTk4LiBBbGwgcmlnaHRzIHJlc2VydmVkLg0KaHR0cDovL3d3dy5k ZXZlbGNvci5jb20AOw== } image create photo fileImg -data { R0lGODlhEAAOAPcAAAAAADVJYzZKZJOit5WkuZalupqpvpyrwJ6uw6OyyKSzyae2zKm5z6u70a6+ 1K+/1bLC2LrF1L3K4cTP5MnT5svV59HZ6tPb69Xd7Njf7drh7tzj79/l8OHn8ePp8ubr9Ont9evv 9u7x9/Dz+PL1+fX3+vf4+/n6/Pv8/fz9/v7+/v///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAP8ALAAAAAAQAA4A AAh7AP/9g0CwoAMGCgQqFAhhhcOHKw4IWCjwAcSHBCJMXNjgosMBAkIuXOBxBYoBIBcm8KiiBIgB ARYi8HhCRAeYCw1cTEHigwacCgtcNBGCwwWgAgdARDHCQ4YKSP8pddgSxAYLE6JOXVGzAwYKErSi HEs2aoCzaNOeFRgQADs=} image create photo clsdFolderImg -data { R0lGODlhEAAOAPcAAAAAAJycAM7OY//OnP//nP//zvf39wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAP8ALAAAAAAQAA4A AAhjAP8JHEiw4MAACBECMHjQQIECBAgEWGgwgICLGAUkTCgwwMOPIB8SELDQY8STKAkMIPnPZEqV MFm6fDlApUyIKGvqHFkSZ06YK3ue3KkzaMsCRIEOMGoxo1OMFAFInUqV6r+AADs=} } method fbReturn {w} { set row [$win selection] $win tag remove hilight $win tag add hilight $row set fname [lindex [$win item $row -values] 0] if {[file isdirectory $fname]} { $self browseDir $fname } else { if {$options(-browsecmd) ne ""} { $options(-browsecmd) $fname } } } method fbOnClick {w x y} { set row [$win identify item $x $y] $win tag remove hilight $win tag add hilight $row set fname [lindex [$win item $row -values] 0] if {[file isdirectory $fname]} { $self browseDir $fname } else { if {$options(-browsecmd) ne ""} { $options(-browsecmd) $fname } } } onconfigure -directory value { $self browseDir $value set options(-directory) $value } method browseDir {{dir "."}} { if {[llength [$win children {}]] > 0} { $win delete [$win children {}] } if {$dir ne "."} { cd $dir set options(-directory) [pwd] } $win insert {} end -values [list ".." " "] -image clsdFolderImg foreach dir [lsort [glob -types d -nocomplain [file join $options(-directory) *]]] { $win insert {} end -values [list [file tail $dir] " "] -image clsdFolderImg } foreach file [lsort [glob -types f -nocomplain [file join $options(-directory) *]]] { if {[regexp $options(-filepattern) $file]} { $win insert {} end -values [list [file tail $file] \ [format "%3.1fMb" [expr {([file size $file] /1024.0)/1024.0}]]] \ -image $options(-fileimage) } } $win focus [lindex [$win children {}] 0] $win selection set [lindex [$win children {}] 0] focus -force $win } } ====== You can now nest the widget adaptors: ====== # Example usage code set fb [dgw::tvksearch [dgw::tvfilebrowser [dgw::tvband [ttk::treeview .fp]] -directory . -fileimage fileImg]] pack $fb -side top -fill both -expand yes # less specialized but still a file browser set fb2 [dgw::tvfilebrowser [ttk::treeview .fp2] -directory . \ -fileimage movie -filepattern {\.(3gp|mp4|avi|mkv|mp3|ogg)$}] pack $fb2 -side top -fill both -expand yes ====== See below for a screenshot: [treeview-mixin-image] If the nested call looks to complicated to you, you can wrap this piped command calls as well in a new widget or even just a simple proc: ---- **Discussion** Please discuss here. <> GUI | Object Orientation | Snit Widgets | Snit | Design | Discussion