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

Last change on this file since 3177 was 3177, checked in by mmc, 12 years ago

Updated all of the copyright notices to reference the transfer to
the new HUBzero Foundation, LLC.

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