source: tags/20110826/gui/scripts/sidebarframe.tcl @ 4643

Last change on this file since 4643 was 1694, checked in by dkearney, 14 years ago

spacing, tabs, blahh

File size: 17.7 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: SidebarFrame - pop-out controls for visualization widgets
3#
4#  The sidebar provides a way to put a thin strip of controls along the
5#  side of a visualization widget, with tabs that cause control panels
6#  to pop out.  The SidebarFrame has an empty frame (component "frame")
7#  on the left and a sidebar that pops out on the right.
8# ======================================================================
9#  AUTHOR:  George Howlett, Michael McLennan, Purdue University
10#  Copyright (c) 2004-2009  Purdue Research Foundation
11#
12#  See the file "license.terms" for information on usage and
13#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14# ======================================================================
15package require Itk
16
17option add *SidebarFrame.width 3i widgetDefault
18option add *SidebarFrame.height 3i widgetDefault
19option add *SidebarFrame.titlebarBackground #6666cc widgetDefault
20option add *SidebarFrame.titlebarForeground white widgetDefault
21option add *SidebarFrame.controlBackground gray widgetDefault
22option add *SidebarFrame*cntls*highlightBackground gray widgetDefault
23option add *SidebarFrame.sashRelief flat widgetDefault
24option add *SidebarFrame.sashActiveRelief solid widgetDefault
25option add *SidebarFrame.sashColor gray widgetDefault
26option add *SidebarFrame.sashWidth 2 widgetDefault
27option add *SidebarFrame.sashPadding 2 widgetDefault
28option add *SidebarFrame.sashCursor sb_h_double_arrow
29
30itcl::class Rappture::SidebarFrame {
31    inherit itk::Widget
32
33    itk_option define -sashrelief sashRelief Relief ""
34    itk_option define -sashactiverelief sashActiveRelief SashActiveRelief ""
35    itk_option define -sashcolor sashColor SashColor ""
36    itk_option define -sashwidth sashWidth SashWidth 0
37    itk_option define -sashpadding sashPadding SashPadding 0
38    itk_option define -sashcursor sashCursor Cursor ""
39
40    constructor {args} { # defined below }
41
42    public method insert {pos args}
43    public method panel {which}
44    public method select {which}
45    public method pop {what}
46
47    protected method _toggleTab {which}
48    protected method _sash {op x}
49    protected method _fixLayout {args}
50
51    private variable _dispatcher ""  ;# dispatcher for !events
52    private variable _state "closed" ;# sidebar open/closed
53    private variable _panels         ;# maps panel => title, etc.
54    private variable _selected ""    ;# selected panel
55    private variable _width "auto"   ;# width adjusted by sash or "auto"
56    private variable _counter 0      ;# counter for auto-generated names
57}
58
59itk::usual SidebarFrame {
60    keep -background -cursor
61    keep -titlebarbackground -titlebarforeground
62}
63
64# ----------------------------------------------------------------------
65# CONSTRUCTOR
66# ----------------------------------------------------------------------
67itcl::body Rappture::SidebarFrame::constructor {args} {
68    itk_option add hull.width hull.height
69
70    # create a dispatcher for events
71    Rappture::dispatcher _dispatcher
72    $_dispatcher register !layout
73    $_dispatcher dispatch $this !layout [itcl::code $this _fixLayout]
74
75    # fix the layout whenever the window size changes
76    bind SidebarFrame <Configure> [itcl::code %W _fixLayout]
77
78    #
79    # Empty frame for main widget
80    #
81    itk_component add frame {
82        frame $itk_interior.f
83    }
84
85    #
86    # Sash along the left side of sidebar
87    #
88    itk_component add sashbg {
89        frame $itk_interior.sashbg
90    } {
91        usual
92        rename -cursor -sashcursor sashCursor Cursor
93    }
94
95    itk_component add sash {
96        frame $itk_component(sashbg).sash -borderwidth 1
97    } {
98        usual
99        ignore -background -borderwidth
100        rename -relief -sashrelief sashRelief Relief
101        rename -width -sashwidth sashWidth SashWidth
102        rename -cursor -sashcursor sashCursor Cursor
103    }
104    pack $itk_component(sash) -side left -fill y
105
106    foreach c {sash sashbg} {
107        bind $itk_component($c) <Enter> \
108            [itcl::code $this _sash enter %X]
109        bind $itk_component($c) <Leave> \
110            [itcl::code $this _sash leave %X]
111        bind $itk_component($c) <ButtonPress-1> \
112            [itcl::code $this _sash grab %X]
113        bind $itk_component($c) <B1-Motion> \
114            [itcl::code $this _sash drag %X]
115        bind $itk_component($c) <ButtonRelease-1> \
116            [itcl::code $this _sash release %X]
117    }
118
119    itk_component add sidebar {
120        frame $itk_interior.sbar
121    }
122
123    #
124    # Title bar along top of sidebar
125    #
126    itk_component add titlebar {
127        frame $itk_component(sidebar).tbar -highlightthickness 0
128    } {
129        usual
130        ignore -highlightthickness
131        rename -background -titlebarbackground titlebarBackground Background
132    }
133    pack $itk_component(titlebar) -side top -fill x
134
135    itk_component add popbutton {
136        button $itk_component(titlebar).popb \
137            -borderwidth 1 -relief flat -overrelief raised \
138            -highlightthickness 0 \
139            -image [Rappture::icon sbar-open] \
140            -command [itcl::code $this pop toggle]
141    } {
142        usual
143        ignore -borderwidth -relief -overrelief -highlightthickness
144        rename -background -titlebarbackground titlebarBackground Background
145        rename -activebackground -titlebarbackground titlebarBackground Background
146    }
147    pack $itk_component(popbutton) -side left -padx 6 -pady 2
148    Rappture::Tooltip::for $itk_component(popbutton) \
149        "Open/close the sidebar"
150
151    itk_component add title {
152        label $itk_component(titlebar).title -anchor w -font "Arial 10"
153    } {
154        usual
155        ignore -font
156        rename -foreground -titlebarforeground titlebarForeground Foreground
157        rename -background -titlebarbackground titlebarBackground Background
158    }
159    pack $itk_component(title) -side left -expand yes -fill both -padx 1 -pady 1
160
161    #
162    # Area for active panel
163    #
164    itk_component add area {
165        Rappture::Scroller $itk_component(sidebar).area \
166            -xscrollmode auto -yscrollmode auto \
167            -highlightthickness 0
168    }
169    $itk_component(area) contents frame
170
171    itk_component add controlbar {
172        frame $itk_component(sidebar).cbar
173    } {
174        usual
175        rename -background -controlbackground controlBackground Background
176    }
177    pack $itk_component(controlbar) -side left -fill y
178
179    #
180    # Control area above the tabs
181    #
182    itk_component add controls {
183        frame $itk_component(controlbar).cntls -height 20
184    } {
185        usual
186        rename -background -controlbackground controlBackground Background
187    }
188    pack $itk_component(controls) -side top -pady {8 20}
189
190    #
191    # Tabs used to select sidebar panels. 
192    #
193    # Note:  Bugs in BLT 2.4 tabset/VNC server crashes the server
194    #             when -outerpad is set to 0.
195    #
196    itk_component add tabs {
197        blt::tabset $itk_component(controlbar).tabs \
198            -highlightthickness 0 -tearoff 0 -side left \
199            -bd 0 -gap 0 -tabborderwidth 1 \
200            -outerpad 1
201    } {
202        keep -background -cursor
203        ignore -highlightthickness -borderwidth
204        rename -highlightbackground -controlbackground controlBackground \
205            Background
206        rename -background -controlbackground controlBackground \
207            Background
208    }
209    pack $itk_component(tabs) -side top -expand yes -anchor e -padx {4 0} -fill y
210
211    eval itk_initialize $args
212
213    # make sure we fix up the layout at some point
214    $_dispatcher event -idle !layout
215}
216
217# ----------------------------------------------------------------------
218# USAGE: insert <pos> ?-title t? ?-icon i?
219#
220# Adds a new panel into this widget at the given position <pos>.  The
221# panel has a tab with the specified -icon, and is labeled by the
222# -title string in the titlebar area when it is selected.
223# ----------------------------------------------------------------------
224itcl::body Rappture::SidebarFrame::insert {pos args} {
225    Rappture::getopts args panel "
226        value -title Options
227        value -icon [Rappture::icon cboff]
228    "
229    if {[llength $args] > 0} {
230        error "wrong # args: should be \"insert pos ?-title t? ?-icon i?\""
231    }
232
233    set f [$itk_component(area) contents]
234    set pname "panel[incr _counter]"
235    itk_component add $pname {
236        frame $f.$pname
237    }
238
239    $itk_component(tabs) insert end $pname \
240        -image $panel(-icon) -text "" -padx 0 -pady 0 \
241        -command [itcl::code $this _toggleTab $pname]
242
243    Rappture::Tooltip::text $itk_component(tabs)-$pname \
244        "Open/close sidebar for $panel(-title)"
245    $itk_component(tabs) bind $pname <Enter> \
246        [list ::Rappture::Tooltip::tooltip pending %W-$pname @%X,%Y]
247    $itk_component(tabs) bind $pname <Leave> \
248        [list ::Rappture::Tooltip::tooltip cancel]
249    $itk_component(tabs) bind $pname <ButtonPress> \
250        [list ::Rappture::Tooltip::tooltip cancel]
251    $itk_component(tabs) bind $pname <KeyPress> \
252        [list ::Rappture::Tooltip::tooltip cancel]
253
254    set _panels($pname-title) $panel(-title)
255    lappend _panels(all) $pname
256    if {$_selected == ""} {
257        set _selected $pname
258        if {$_state == "open"} {
259            $itk_component(title) configure -text $panel(-title)
260        }
261    }
262
263    return $itk_component($pname)
264}
265
266# ----------------------------------------------------------------------
267# USAGE: panel <which>
268#
269# Returns the frame representing the requested panel.  The <which>
270# argument can be a panel index, name, or title, or the keyword
271# "current" for the selected panel.
272# ----------------------------------------------------------------------
273itcl::body Rappture::SidebarFrame::panel {which} {
274    switch -glob -- $which {
275        current {
276            return $itk_component($_selected)
277        }
278        [0-9]* {
279            set pname [lindex $_panels(all) $which]
280            return $itk_component($pname)
281        }
282        panel[0-9]* {
283            if {[info exists itk_component($which)]} {
284                return $itk_component($which)
285            }
286            error "bad panel name \"$which\""
287        }
288        default {
289            foreach pname $_panels(all) {
290                if {[string equal $_panels($pname-title) $which]} {
291                    return $itk_component($pname)
292                }
293            }
294            error "bad panel title \"$which\""
295        }
296    }
297}
298
299# ----------------------------------------------------------------------
300# USAGE: select <which>
301#
302# Pops open the sidebar and selects the specified panel.  The <which>
303# argument can be a panel index, name, or title.
304# ----------------------------------------------------------------------
305itcl::body Rappture::SidebarFrame::select {which} {
306    set pname ""
307    switch -glob -- $which {
308        [0-9]* {
309            set pname [lindex $_panels(all) $which]
310        }
311        panel[0-9]* {
312            if {[info exists itk_component($which)]} {
313                set pname $which
314            }
315        }
316        default {
317            foreach p $_panels(all) {
318                if {[string equal $_panels($p-title) $which]} {
319                    set pname $p
320                    break
321                }
322            }
323        }
324    }
325    if {$pname == ""} {
326        error "bad panel name \"$which\": should be panel id, title, or index"
327    }
328
329    if {$_state == "closed"} {
330        pop open
331    }
332
333    set minw [winfo reqwidth $itk_component(controlbar)]
334    if {$_width != "auto" && $_width < $minw+50} {
335        set _width [expr {$minw+50}]
336        $_dispatcher event -idle !layout
337    }
338    set n [$itk_component(tabs) index -name $pname]
339    $itk_component(tabs) select $n
340
341    $itk_component(title) configure -text $_panels($pname-title)
342
343    set f [$itk_component(area) contents]
344    foreach w [pack slaves $f] {
345        pack forget $w
346    }
347    pack $itk_component($pname) -expand yes -fill both
348
349    #
350    # HACK ALERT!  Force the scroller to check the size of the
351    # panel that we just slid in under the covers.  Make it
352    # think the panel and the scroller itself have changed size.
353    #
354    event generate [winfo parent $f] <Configure>
355    event generate $f <Configure>
356
357    set _selected $pname
358    return $pname
359}
360
361# ----------------------------------------------------------------------
362# USAGE: pop open|close|toggle
363#
364# Used to open/close the sidebar area.  When open, the selected panel
365# appears and the titlebar shows its name.
366# ----------------------------------------------------------------------
367itcl::body Rappture::SidebarFrame::pop {how} {
368    if {$how == "toggle"} {
369        if {$_state == "closed"} {
370            set how "open"
371        } else {
372            set how "close"
373        }
374    }
375
376    switch -- $how {
377        open {
378            $itk_component(popbutton) configure \
379                -image [Rappture::icon sbar-closed]
380            pack $itk_component(area) -side right -expand yes -fill both
381
382            set _state "open"
383            select $_selected
384            $_dispatcher event -idle !layout
385        }
386        close {
387            $itk_component(popbutton) configure \
388                -image [Rappture::icon sbar-open]
389            $itk_component(title) configure -text ""
390            pack forget $itk_component(area)
391
392            set _state "closed"
393            $_dispatcher event -idle !layout
394        }
395        default {
396            error "bad option \"$how\": should be open, close, toggle"
397        }
398    }
399}
400
401# ----------------------------------------------------------------------
402# USAGE: _toggleTab <which>
403#
404# Invoked automatically when the user clicks on a tab for the sidebar.
405# If the sidebar is closed, it is automatically opened and the tab is
406# selected.  If the sidebar is opened, then it's closed.
407# ----------------------------------------------------------------------
408itcl::body Rappture::SidebarFrame::_toggleTab {which} {
409    if {$_state == "closed"} {
410        pop open
411        select $which
412    } elseif {[$itk_component(tabs) index -name $_selected]
413          == [$itk_component(tabs) index -name $which]} {
414        pop close
415    } else {
416        select $which
417    }
418}
419
420# ----------------------------------------------------------------------
421# USAGE: _sash <op> <X>
422#
423# Invoked automatically when the user clicks/drags on a sash, to resize
424# the sidebar.
425# ----------------------------------------------------------------------
426itcl::body Rappture::SidebarFrame::_sash {op X} {
427    switch -- $op {
428        enter {
429            # mouse over sash -- make it active
430            if {$itk_option(-sashactiverelief) != ""} {
431                $itk_component(sash) configure -relief $itk_option(-sashactiverelief)
432            }
433        }
434        leave {
435            # mouse left sash -- back to normal
436            $itk_component(sash) configure -relief $itk_option(-sashrelief)
437        }
438        grab {
439            if {$_state == "closed"} { pop open }
440            _sash drag $X
441        }
442        drag {
443            set w [winfo width $itk_component(hull)]
444            set minw [winfo reqwidth $itk_component(controlbar)]
445            set dx [expr {$X - [winfo rootx $itk_component(hull)]}]
446            set sashw [winfo reqwidth $itk_component(sashbg)]
447            set _width [expr {$w - $dx - $sashw/2}]
448
449            if {$_width < $minw} { set _width $minw }
450            if {$_width > $w-50} { set _width [expr {$w-50}] }
451            _fixLayout
452        }
453        release {
454            set minw [winfo reqwidth $itk_component(controlbar)]
455            if {$_width-$minw < 40} {
456                set _width "auto"
457                pop close
458            }
459        }
460        default {
461            error "bad option \"$op\": should be enter, leave, grab, drag, release"
462        }
463    }
464}
465
466# ----------------------------------------------------------------------
467# USAGE: _fixLayout ?<eventArgs>...?
468#
469# Used internally to update the layout of panes whenever a new pane
470# is added or a sash is moved.
471# ----------------------------------------------------------------------
472itcl::body Rappture::SidebarFrame::_fixLayout {args} {
473    set w [winfo width $itk_component(hull)]
474    set h [winfo height $itk_component(hull)]
475
476    set sashw [winfo reqwidth $itk_component(sashbg)]
477
478    set tabw [winfo reqwidth $itk_component(tabs)]
479    set btnw [winfo reqwidth $itk_component(controls)]
480    set ctrlw [expr {($tabw > $btnw) ? $tabw : $btnw}]
481
482    if {$_state == "closed"} {
483        set sbarw $ctrlw
484    } else {
485        if {$_width == "auto"} {
486            # pop open to the size of the widest pane
487            set sbarw 0
488            foreach pname $_panels(all) {
489                set pw [winfo reqwidth $itk_component($pname)]
490                if {$pw > $sbarw} {
491                    set sbarw $pw
492                }
493            }
494            set sbarw [expr {$sbarw + $ctrlw + $sashw}]
495        } else {
496            set sbarw $_width
497        }
498    }
499
500    # don't let the sidebar take up too much of the window area
501    if {$sbarw > 0.75*$w} {
502        set sbarw [expr {int(0.75*$w)}]
503    }
504
505    set x1 [expr {$w - $sbarw - $sashw}]
506    set x2 [expr {$w - $sbarw}]
507    place $itk_component(frame) -x 0 -y 0 -anchor nw -width $x1 -height $h
508    place $itk_component(sashbg) -x $x1 -y 0 -anchor nw -width $sashw -height $h
509    place $itk_component(sidebar) -x $x2 -y 0 -anchor nw -width $sbarw -height $h
510}
511
512# ----------------------------------------------------------------------
513# CONFIGURATION OPTION: -sashpadding
514# ----------------------------------------------------------------------
515itcl::configbody Rappture::SidebarFrame::sashpadding {
516    pack $itk_component(sash) -padx $itk_option(-sashpadding)
517}
518
519# ----------------------------------------------------------------------
520# CONFIGURATION OPTION: -sashcolor
521# ----------------------------------------------------------------------
522itcl::configbody Rappture::SidebarFrame::sashcolor {
523    if {$itk_option(-sashcolor) != ""} {
524        $itk_component(sash) configure -background $itk_option(-sashcolor)
525    } else {
526        $itk_component(sash) configure -background $itk_option(-background)
527    }
528}
Note: See TracBrowser for help on using the repository browser.