Posted to tcl by jenglish at Wed Nov 17 22:13:45 GMT 2010view raw
- ### scrollview metawidget.
- #
- # Usage:
- # scrollview $sv
- # $sv manage $sv.child
- # $sv see $sv.$descendant...
- #
- package provide scrollview 1.1
- namespace eval scrollview {
- # State variables:
- # win -- renamed original widget command
- # slave -- managed slave.
- # [xy].constrain -- 1 => constrain; 0 => scroll
- # [xy].command -- scroll command
- # [xy].first -- current scroll position
- # [xy].requested -- requested size from slave
- # [xy].available -- available size
- # [xy].increment -- default increment for [$sv scroll $n units]
- #
- variable defaults {
- win ""
- slave ""
- x.constrain 1
- y.constrain 1
- x.command scrollview::nop
- y.command scrollview::nop
- x.first 0
- y.first 0
- x.requested 0
- y.requested 0
- x.increment 10
- y.increment 10
- }
- proc min {a b} { expr {$a < $b ? $a : $b} }
- proc max {a b} { expr {$a > $b ? $a : $b} }
- proc nop {args} {}
- }
- ### Defaults.
- #
- # Default -width 10c and -height 7c are the BWidget ScrollableFrame defaults,
- # which in turn are taken from the core Canvas widget.
- #
- option add *Scrollview.width 10c
- option add *Scrollview.height 7c
- ### Bindings.
- #
- bind Scrollview <Destroy> { scrollview::Destroy %W }
- bind Scrollview <Configure> { scrollview::Resize %W }
- proc scrollview::Destroy {w} {
- upvar #0 $w W
- unset -nocomplain W
- rename $w {}
- }
- proc scrollview::Resize {w} {
- upvar #0 $w W
- Refresh $w
- set W(x.first) [First $w x $W(x.first)]
- set W(y.first) [First $w y $W(y.first)]
- PlaceSlave $w
- Notify $w {x y}
- }
- ### Internal utilities.
- #
- ## PlaceSlave -- adjust position of slave window.
- #
- proc scrollview::PlaceSlave {w} {
- upvar #0 $w W
- if {$W(slave) eq ""} { return }
- place $W(slave) -in $w \
- -x [expr {-$W(x.first)}] \
- -y [expr {-$W(y.first)}] \
- -width [SlaveSize $w x] \
- -height [SlaveSize $w y] \
- ;
- }
- ## SlaveSize --
- #
- proc scrollview::SlaveSize {w axis} {
- upvar #0 $w W
- if {$W($axis.constrain)} {
- return $W($axis.available)
- } else {
- return $W($axis.requested)
- }
- # Other possibilities:
- # stretch -- [max $W($axis.available) $W($axis.requested)]
- # shrink -- [min $W($axis.available) $W($axis.requested)] (not useful)
- }
- ## Refresh -- update sizes
- #
- proc scrollview::Refresh {w} {
- upvar #0 $w W
- set W(x.available) [winfo width $w]
- set W(y.available) [winfo height $w]
- if {$W(slave) ne ""} {
- set W(x.requested) [winfo reqwidth $W(slave)]
- set W(y.requested) [winfo reqheight $W(slave)]
- }
- }
- ## Notify $w ?$axes? -- invoke -[xy]scrollcommands
- #
- proc scrollview::Notify {w {axes "x y"}} {
- upvar #0 $w W
- foreach axis $axes {
- set total [max 1.0 [expr { double($W($axis.requested)) }]]
- set first [expr { double($W($axis.first))/$total } ]
- set last [expr { double($W($axis.first)+$W($axis.available))/$total }]
- uplevel #0 [linsert $W($axis.command) end $first $last]
- }
- }
- ## First --
- #
- proc scrollview::First {w axis pos} {
- upvar #0 $w W
- set lim [expr {$W($axis.requested) - $W($axis.available)}]
- return [max 0 [min $lim $pos]]
- }
- proc scrollview::SetFirst {w axis pos} {
- upvar #0 $w W
- set W($axis.first) [First $w $axis $pos]
- PlaceSlave $w
- Notify $w $axis
- }
- ## Widget constructor.
- #
- interp alias {} ::scrollview {} ::scrollview::Constructor
- proc scrollview::Constructor {w args} {
- upvar #0 $w W
- variable defaults
- array set W $defaults
- frame $w -class Scrollview
- rename $w [set W(win) ::scrollview::_$w]
- interp alias {} $w {} ::scrollview::Dispatch $w
- uplevel 1 [linsert $args 0 $w configure]
- return $w
- }
- proc scrollview::Dispatch {w command args} {
- uplevel 1 [linsert $args 0 [namespace which -command $command] $w]
- }
- ### Widget methods:
- #
- ## $sv configure option value...
- #
- proc scrollview::configure {w args} {
- upvar #0 $w W
- set frameopts [list]
- foreach {option value} $args {
- switch -- $option {
- -xscrollcommand { set W(x.command) $value;set W(x.constrain) 0 }
- -yscrollcommand { set W(y.command) $value;set W(y.constrain) 0 }
- -xscrollincrement { set W(x.increment) $value }
- -yscrollincrement { set W(y.increment) $value }
- -width -
- -height { lappend frameopts $option $value }
- default { error "Unrecognized option $option" }
- }
- }
- if {[llength $frameopts]} {
- eval [linsert $frameopts 0 $W(win) configure]
- }
- }
- ## $sv manage $child
- #
- proc scrollview::manage {w slave} {
- upvar #0 $w W
- if {$W(slave) ne ""} {
- place forget $W(slave)
- }
- set W(slave) $slave
- Resize $w
- }
- ## $sv xview, $sv yview -- standard scrolling methods.
- #
- proc scrollview::xview {w method amount {units ""}} {
- XYview $w x $method $amount $units
- }
- proc scrollview::yview {w method amount {units ""}} {
- XYview $w y $method $amount $units
- }
- proc scrollview::XYview {w axis method amount units} {
- switch -- $method {
- moveto { MoveTo $w $axis $amount }
- scroll { Scroll-$units $w $axis $amount }
- default { error "Unknown scroll directive $method" }
- }
- }
- proc scrollview::MoveTo {w axis fraction} {
- upvar #0 $w W
- SetFirst $w $axis [expr {int($fraction * $W($axis.requested))}]
- }
- proc scrollview::Scroll-units {w axis count} {
- upvar #0 $w W
- SetFirst $w $axis [expr {$W($axis.first) + $W($axis.increment)*$count}]
- }
- proc scrollview::Scroll-pages {w axis count} {
- upvar #0 $w W
- SetFirst $w $axis [expr {$W($axis.first) + $count*$W($axis.available)}]
- }
- ## $sv see $descendant --
- # Ensure that $descendant is visible.
- #
- proc scrollview::see {w descendant} {
- upvar #0 $w W
- set xpos [set ypos 0]
- set d $descendant
- while {$d ne $W(slave)} {
- incr xpos [winfo x $d]
- incr ypos [winfo y $d]
- set d [winfo parent $d]
- if {$d eq "."} {
- return -code error "$descendant not a descendant of $w"
- }
- }
- See $w x $xpos [winfo width $descendant]
- See $w y $ypos [winfo height $descendant]
- }
- proc scrollview::See {w axis pos len} {
- upvar #0 $w W
- # ENSURE: first <= pos <= pos + len <= first + available
- # If both can't be satisfied, ensure first <= pos.
- set min [expr {$pos + $len - $W($axis.available)}]
- if {$W($axis.first) < $min} { SetFirst $w $axis $min }
- if {$pos < $W($axis.first)} { SetFirst $w $axis $pos }
- }
- #*EOF*