source: trunk/gui/scripts/sidebarframe.tcl @ 2790

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