Updated 2014-06-01 07:52:39 by dkf

FF 2007-05-20 - Today a little problem came to me, and quicly found a solution. Many thanks to tclers guys.

Issue was: encapsulating and hiding the interface of a widget, instead of just overloading/extending it

Practical application: this widget actually does nothing useful, but it does pretty nice, and maybe it can be useful to rip off the concept (though I could clean it up a bit)

I'm not used to put many comments... proc names this time should be auto-explanatory! O;)
#!/usr/bin/env wish

proc tracker {w args} {
        global tracker_struct

        # set default options
        set tracker_struct($w:-width) 500
        set tracker_struct($w:-height) 300
        set tracker_struct($w:-rows) 16
        set tracker_struct($w:-cols) 6
        set tracker_struct($w:-spacing) 3

        # parse options
        set valid_opts {-width -height -rows -cols}
        foreach {opt val} $args {
                if {[lsearch -exact $valid_opts $opt] == -1} {
                        return -code error -errorinfo \
                                "tracker($w): unknown option: $opt"
                } else {
                        set tracker_struct($w:$opt) $val
        set c [canvas $w \
                -width      $tracker_struct($w:-width)    \
                -height     $tracker_struct($w:-height)   \
                -takefocus  1]
        rename $c ${w}_canvas
        set tracker_struct($w:canvas) ${w}_canvas
        set tracker_struct($w:window) $w

        set tracker_struct($w:font) [font create -family Courier -size 10 -weight bold \
                -slant roman -underline false -overstrike false]

        set tracker_struct($w:font:-ascent) [font metrics $tracker_struct($w:font) -ascent]
        set tracker_struct($w:font:-descent) [font metrics $tracker_struct($w:font) -descent]
        set tracker_struct($w:font:-linespace) [font metrics $tracker_struct($w:font) -linespace]
        set tracker_struct($w:font:-width) [font measure $tracker_struct($w:font) m]
        set tracker_struct($w:-charwidth) $tracker_struct($w:font:-width)
        set tracker_struct($w:-charheight) $tracker_struct($w:font:-linespace)

        set tracker_struct($w:cursor:x) 0
        set tracker_struct($w:cursor:y) 0

        # setup callback proc
        proc $w args "return \[eval tracker_callback $w \$args\]"

        if [tracker_init $w] {
                return $c
        } else {
                return -code error -errorinfo \
                        "tracker($w): init failed"

proc tracker_callback {w command {args {}}} {
        global tracker_struct
        if {[llength [info procs tracker_$command]] > 0} {
                return [eval tracker_$command $w $args]
        } else {
                return -code error -errorinfo \
                        "tracker($w): no such command: $command"

proc tracker_init {w} {
        global tracker_struct
        for {set y 0} {$y < $tracker_struct($w:-rows)} {incr y} {
                for {set x 0} {$x < $tracker_struct($w:-cols)} {incr x} {
                        set rw $tracker_struct($w:-charwidth)
                        set rh $tracker_struct($w:-charheight)
                        set rx [expr 1+$x*($rw+$tracker_struct($w:-spacing))]
                        set ry [expr 1+$y*($rh+$tracker_struct($w:-spacing))]
                        $tracker_struct($w:canvas) create rectangle \
                                $rx $ry [expr $rx+$rw] [expr $ry+$rh] \
                                -fill {} -outline black \
                                -tags [list bg xy$x$y]
        bind $tracker_struct($w:window) <KeyPress> "tracker_keypress $w %K"
        bind $tracker_struct($w:window) <ButtonPress-1> "focus $w"
        tracker_move $w 0 0
        return 1

proc tracker_move {w dx dy} {
        global tracker_struct
        $tracker_struct($w:canvas) itemconfigure bg -fill {}
        incr tracker_struct($w:cursor:x) $dx
        incr tracker_struct($w:cursor:y) $dy
        if {$tracker_struct($w:cursor:x) < 0} {
                incr tracker_struct($w:cursor:x) $tracker_struct($w:-cols)
        if {$tracker_struct($w:cursor:y) < 0} {
                incr tracker_struct($w:cursor:y) $tracker_struct($w:-rows)
        set tracker_struct($w:cursor:x) [expr $tracker_struct($w:cursor:x) \
        set tracker_struct($w:cursor:y) [expr $tracker_struct($w:cursor:y) \
        set xy $tracker_struct($w:cursor:x)$tracker_struct($w:cursor:y)
        $tracker_struct($w:canvas) itemconfigure xy$xy -fill black

proc tracker_keypress {w ks} {
        #puts "[lindex [info level 0] 0]: $w $ks"
        switch $ks {
                Left {tracker_move $w -1 0}
                Right {tracker_move $w 1 0}
                Up {tracker_move $w 0 -1}
                Down {tracker_move $w 0 1}

pack [tracker .t -width 400 -height 400]