Version 0 of Paning widgets

Updated 1999-08-18 15:11:20

if 0 {A pane is understood here as a frame holding two widgets and a thin borderline with a square button. By grabbing the button, the borderline can be moved in two directions, leading to a relative resize of the two embedded widgets.

This was slightly modified after the example code in the Welch book 8.0, Example 22-5..7: vertical or horizontal panes. Suggested exercise: rewrite the code so the whole divider line can be grabbed ;-)

See also Bag of algorithms and Bag of Tk algorithms . RS }

 proc pane:create {f1 f2 args} {
    # Map optional arguments into array values
    set t(-orient) horizontal        
    set t(-percent) 0.5
    set t(-in) [winfo parent $f1]
    array set t $args

    # Keep state in an array associated with the master frame
    set master $t(-in)
    pack $master -expand true -fill both
    pack propagate $master off
    upvar #0 Pane$master pane
    array set pane [array get t]

    set pane(1) $f1
    set pane(2) $f2
    set pane(grip) [frame $master.grip -background gray75 \
            -width 10 -height 10 -bd 1 -relief raised]
    if {[string match vert* $pane(-orient)]} {
        set pane(D) Y                ;# Adjust boundary in Y direction
        place $pane(1) -in $master -x 0 -rely 0.0 -anchor nw \
                -relwidth 1.0 -height -1
        place $pane(2) -in $master -x 0 -rely 1.0 -anchor sw \
                -relwidth 1.0 -height -1
        place $pane(grip) -in $master -anchor c -relx 0.8
        $pane(grip) configure -cursor sb_v_double_arrow
    } else {
        set pane(D) X                 ;# Adjust boundary in X direction
        place $pane(1) -in $master -relx 0.0 -y 0 -anchor nw \
                -relheight 1.0 -width -1
        place $pane(2) -in $master -relx 1.0 -y 0 -anchor ne \
                -relheight 1.0 -width -1
        place $pane(grip) -in $master -anchor c -rely 0.2
        $pane(grip) configure -cursor sb_h_double_arrow
    }
    $master configure -background grey75

    # bindings for resize, <Configure>, and dragging the grip.
    bind $master <Configure> [list PaneGeometry $master]
    bind $pane(grip) <B1-Motion> \
            [list PaneDrag $master %$pane(D)]
    bind $pane(grip) <ButtonRelease-1> \
            [list PaneStop $master]

    # Do the initial layout
    PaneGeometry $master
 }
 proc pane:set {master value} {
    global Pane$master
    set Pane${master}(-percent) $value
    PaneGeometry $master
 }
 proc PaneDrag {master D} {
    upvar #0 Pane$master pane
    if [info exists pane(lastD)] {
        set delta [expr double($pane(lastD)-$D)/$pane(size)]
        set pane(-percent) [expr $pane(-percent) - $delta]
        if {$pane(-percent) < 0.0} {
            set pane(-percent) 0.0
        } elseif {$pane(-percent) > 1.0} {
            set pane(-percent) 1.0
        }
        PaneGeometry $master
    }
    set pane(lastD) $D
 }
 proc PaneStop {master} {
    upvar #0 Pane$master pane
    catch {unset pane(lastD)}
 }
 proc PaneGeometry {master} {
    upvar #0 Pane$master pane
    if {$pane(D) == "X"} {
        place $pane(1) -relwidth $pane(-percent)
        place $pane(2) -relwidth [expr 1.0 - $pane(-percent)]
        place $pane(grip) -relx $pane(-percent)
        set pane(size) [winfo width $master]
    } else {
        place $pane(1) -relheight $pane(-percent)
        place $pane(2) -relheight [expr 1.0 - $pane(-percent)]
        place $pane(grip) -rely $pane(-percent)
        set pane(size) [winfo height $master]
    }
 }

# Here's a usage example with horiz and vertical panes:

 proc pane:test {{p .p} {orient hori}} {
    catch {destroy $p}
    frame $p -width 200 -height 200 ;# needed: no propagation 
    message $p.1 -bg bisque -text [info procs] -relief ridge
    frame $p.2
    label $p.2.foo -bg pink -text foo -relief ridge
    label $p.2.bar -bg grey75 -text bar -relief ridge
    pack $p -expand true -fill both
    pack propagate $p off
    pane:create $p.2.foo $p.2.bar -orient vert
    pane:create $p.1 $p.2 -orient $orient -percent 0.7
    raise .
 }

--- Wow - that's a lot of code up there. Here's a simpler example written by Stephen Uhler. - DL

 # 2 panes, one on top of the other (>= Tk4.0)

 frame .top
 frame .bottom
 frame .handle -borderwidth 2 -relief raised \
                -bg orange -cursor sb_v_double_arrow
 . configure -bg black

 # fixed placement parameters
 place .top  -relwidth 1 -height -1
 place .bottom -relwidth 1 -rely 1 -anchor sw -height -1
 place .handle -relx 0.9 -anchor e -width 10 -height 10

 bind . <Configure> {set H [winfo height .]; set Y0 [winfo rooty .]}
 bind .handle <B1-Motion> {Place [expr (%Y-$Y0)/$H.0]}

 proc Place {fract} {
        place .top -relheight $fract
        place .handle -rely $fract
        place .bottom -relheight [expr 1.0 - $fract]
 }

 Place .5                ;# initialization
 # now "pack" whatever you like in ".top" and ".bottom"