Index: scrollframe.tcl ================================================================== --- scrollframe.tcl +++ scrollframe.tcl @@ -13,10 +13,13 @@ # - ScrollableFrame::yview # - ScrollableFrame::_resize # ---------------------------------------------------------------------------- namespace eval ScrollableFrame { + # track scrollbar on hoover + array set mouseover {} + Widget::define ScrollableFrame scrollframe # If themed, there is no background and -bg option if {[Widget::theme]} { Widget::declare ScrollableFrame { @@ -24,10 +27,11 @@ {-height Int 0 0 {}} {-areawidth Int 0 0 {}} {-areaheight Int 0 0 {}} {-constrainedwidth Boolean 0 0} {-constrainedheight Boolean 0 0} + {-autohidescrollbar Boolean 0 0} {-xscrollcommand TkResource "" 0 canvas} {-yscrollcommand TkResource "" 0 canvas} {-xscrollincrement TkResource "" 0 canvas} {-yscrollincrement TkResource "" 0 canvas} } @@ -38,10 +42,11 @@ {-height Int 0 0 {}} {-areawidth Int 0 0 {}} {-areaheight Int 0 0 {}} {-constrainedwidth Boolean 0 0} {-constrainedheight Boolean 0 0} + {-autohidescrollbar Boolean 0 0} {-xscrollcommand TkResource "" 0 canvas} {-yscrollcommand TkResource "" 0 canvas} {-xscrollincrement TkResource "" 0 canvas} {-yscrollincrement TkResource "" 0 canvas} {-bg Synonym -background} @@ -66,15 +71,23 @@ # ---------------------------------------------------------------------------- # Command ScrollableFrame::create # ---------------------------------------------------------------------------- proc ScrollableFrame::create { path args } { + variable mouseover + Widget::init ScrollableFrame $path $args + # Actually $canvas is the same as $path set canvas [eval [list canvas $path] [Widget::subcget $path :cmd] \ -highlightthickness 0 -borderwidth 0 -relief flat] + # Initialization: mouse is within frame + set mouseover($canvas) 1 + + set autohidescrollbar [Widget::cget $path -autohidescrollbar] + if {[Widget::theme]} { set frame [eval [list ttk::frame $path.frame] \ [Widget::subcget $path .frame]] set bg [ttk::style lookup TFrame -background] } else { @@ -95,16 +108,44 @@ # add binding: is not called when frame # becomes so small that it suddenly falls outside of currently visible area. # but now we need to add a binding too bind $frame \ [list ScrollableFrame::_frameConfigure $canvas] + bind $frame \ + [list ScrollableFrame::_frameConfigure $canvas 1] bindtags $path [list $path BwScrollableFrame [winfo toplevel $path] all] + + if {$autohidescrollbar} { + # Show scrollbar if mouse within frame + bind [winfo parent $path] [list ScrollableFrame::enter $canvas] + # Hide scrollbar if mouse leaves frame + bind [winfo parent $path] [list ScrollableFrame::leave $canvas] + } return [Widget::create ScrollableFrame $path] } +# ---------------------------------------------------------------------------- +# Command ScrollableFrame::enter +# ---------------------------------------------------------------------------- +# Show scrollbars as mouse entered frame +proc ScrollableFrame::enter {canvas} { + variable mouseover + set mouseover($canvas) 1 + ScrollableFrame::_frameConfigure $canvas +} + +# ---------------------------------------------------------------------------- +# Command ScrollableFrame::leave +# ---------------------------------------------------------------------------- +# Hide scrollbars as mouse left frame +proc ScrollableFrame::leave {canvas} { + variable mouseover + set mouseover($canvas) 0 + ScrollableFrame::_frameConfigure $canvas 1 +} # ---------------------------------------------------------------------------- # Command ScrollableFrame::configure # ---------------------------------------------------------------------------- proc ScrollableFrame::configure { path args } { @@ -242,15 +283,32 @@ # ---------------------------------------------------------------------------- # Command ScrollableFrame::_frameConfigure # ---------------------------------------------------------------------------- proc ScrollableFrame::_max {a b} {return [expr {$a <= $b ? $b : $a}]} -proc ScrollableFrame::_frameConfigure {canvas} { +proc ::ScrollableFrame::_frameConfigure {canvas {unmap 0}} { + variable mouseover + # Allow to hide scrollbar + if {$mouseover($canvas)==0} { + set unmap 1 + } + + ## There is a bug in BWidget 1.9.0 related to the ScrollableFrame. + # Described in https://groups.google.com/forum/#!topic/comp.lang.tcl/Q5prg9lsOYc + # This added code solves the problem (see https://core.tcl.tk/bwidget/tktview/72a5727d1b7fb76b32cea032eb7d4bf7c6fa28bf) + if {![winfo ismapped $canvas.frame]} { + return + } + # This ensures that we don't get funny scrollability in the frame # when it is smaller than the canvas space # use [winfo] to get height & width of frame - if {![winfo ismapped $canvas.frame]} { return } - set height [_max [winfo height $canvas.frame] [winfo height $canvas]] - set width [_max [winfo width $canvas.frame] [winfo width $canvas]] + + # [winfo] doesn't work for unmapped frame + + set frameh [expr {$unmap ? 0 : [winfo height $canvas.frame]}] + set framew [expr {$unmap ? 0 : [winfo width $canvas.frame]}] + set height [_max $frameh [winfo height $canvas]] + set width [_max $framew [winfo width $canvas]] $canvas:cmd configure -scrollregion [list 0 0 $width $height] } Index: scrollw.tcl ================================================================== --- scrollw.tcl +++ scrollw.tcl @@ -15,20 +15,24 @@ # - ScrolledWindow::_setSBSize # - ScrolledWindow::_realize # ----------------------------------------------------------------------------- namespace eval ScrolledWindow { + # hide scrollbars if mouse not within frame + array set mouseover {} + Widget::define ScrolledWindow scrollw Widget::declare ScrolledWindow { {-background TkResource "" 0 button} {-scrollbar Enum both 0 {none both vertical horizontal}} {-auto Enum both 0 {none both vertical horizontal}} {-sides Enum se 0 {ne en nw wn se es sw ws}} {-size Int 0 1 "%d >= 0"} {-ipad Int 1 1 "%d >= 0"} - {-managed Boolean 1 1} + {-managed Boolean 1 1} + {-autohidescrollbar Boolean 0 0} {-relief TkResource flat 0 frame} {-borderwidth TkResource 0 0 frame} {-bg Synonym -background} {-bd Synonym -borderwidth} } @@ -39,16 +43,21 @@ # ----------------------------------------------------------------------------- # Command ScrolledWindow::create # ----------------------------------------------------------------------------- proc ScrolledWindow::create { path args } { + variable mouseover + # Initialization: mouse within frame + set mouseover($path) 1 + Widget::init ScrolledWindow $path $args Widget::getVariable $path data set bg [Widget::cget $path -background] set sbsize [Widget::cget $path -size] + set autohidescrollbar [Widget::cget $path -autohidescrollbar] if { $::Widget::_theme } { set sw [eval [list ttk::frame $path \ -relief flat -borderwidth 0 -takefocus 0] \ [Widget::subcget $path :cmd]] @@ -96,28 +105,75 @@ set sbsize [$path.vscroll cget -width] } } set data(ipad) [Widget::cget $path -ipad] - if {$data(hsb,packed)} { + if {$data(hsb,packed) && $mouseover($path)} { grid $path.hscroll -column 1 -row $data(hsb,row) \ -sticky ew -ipady $data(ipad) } - if {$data(vsb,packed)} { + if {$data(vsb,packed) && $mouseover($path)} { grid $path.vscroll -column $data(vsb,column) -row 1 \ -sticky ns -ipadx $data(ipad) } grid columnconfigure $path 1 -weight 1 grid rowconfigure $path 1 -weight 1 bind $path [list ScrolledWindow::_realize $path] bind $path [list ScrolledWindow::_destroy $path] + + if {$autohidescrollbar} { + # Show scrollbar if mouse within frame + bind [winfo parent $path] [list ScrolledWindow::enter $path] + # Hide scrollbar if mouse leaves frame + bind [winfo parent $path] [list ScrolledWindow::leave $path] + } return [Widget::create ScrolledWindow $path] } + +# ---------------------------------------------------------------------------- +# Command ScrolledWindow::enter +# ---------------------------------------------------------------------------- +# Show scrollbars as mouse entered frame +proc ScrolledWindow::enter {path} { + variable mouseover + set mouseover($path) 1 + + Widget::getVariable $path data + + foreach {vmin vmax} [$path.hscroll get] { break } + if {!$data(hsb,packed) && ($vmin != 0 || $vmax != 1)} { + grid $path.hscroll -column 1 -row $data(hsb,row) \ + -sticky ew -ipady $data(ipad) + } + foreach {vmin vmax} [$path.vscroll get] { break } + if {!$data(vsb,packed) && ($vmin != 0 || $vmax != 1)} { + grid $path.vscroll -column $data(vsb,column) -row 1 \ + -sticky ns -ipadx $data(ipad) + } + return -code continue +} + +# ---------------------------------------------------------------------------- +# Command ScrolledWindow::leave +# ---------------------------------------------------------------------------- +# Hide scrollbars as mouse left frame +proc ScrolledWindow::leave {path} { + variable mouseover + set mouseover($path) 0 + + Widget::getVariable $path data + + set data(hsb,packed) 0 + grid remove $path.hscroll + set data(vsb,packed) 0 + grid remove $path.vscroll + return -code continue +} # ----------------------------------------------------------------------------- # Command ScrolledWindow::getframe # ----------------------------------------------------------------------------- proc ScrolledWindow::getframe { path } { @@ -150,10 +206,13 @@ # ----------------------------------------------------------------------------- # Command ScrolledWindow::configure # ----------------------------------------------------------------------------- proc ScrolledWindow::configure { path args } { + variable mouseover + set mouseover($path) 0 + Widget::getVariable $path data set res [Widget::configure $path $args] if { ! $::Widget::_theme && [Widget::hasChanged $path -background bg] } { $path configure -background $bg @@ -172,20 +231,20 @@ set data(vsb,packed) [expr {$data(vsb,present) && \ (!$data(vsb,auto) || ($vmin != 0 || $vmax != 1))}] set data(ipad) [Widget::cget $path -ipad] - if {$data(hsb,packed)} { + if {$data(hsb,packed) && $mouseover($path)} { grid $path.hscroll -column 1 -row $data(hsb,row) \ -sticky ew -ipady $data(ipad) } else { if {![info exists data(hlock)]} { set data(hsb,packed) 0 grid remove $path.hscroll } } - if {$data(vsb,packed)} { + if {$data(vsb,packed) && $mouseover($path)} { grid $path.vscroll -column $data(vsb,column) -row 1 \ -sticky ns -ipadx $data(ipad) } else { if {![info exists data(hlock)]} { set data(vsb,packed) 0 @@ -207,10 +266,11 @@ # ----------------------------------------------------------------------------- # Command ScrolledWindow::_set_hscroll # ----------------------------------------------------------------------------- proc ScrolledWindow::_set_hscroll { path vmin vmax } { + variable mouseover Widget::getVariable $path data if {$data(realized) && $data(hsb,present)} { if {$data(hsb,auto) && ![info exists data(hlock)]} { if {$data(hsb,packed) && $vmin == 0 && $vmax == 1} { @@ -217,11 +277,11 @@ set data(hsb,packed) 0 grid remove $path.hscroll set data(hlock) 1 update idletasks unset data(hlock) - } elseif {!$data(hsb,packed) && ($vmin != 0 || $vmax != 1)} { + } elseif {!$data(hsb,packed) && ($vmin != 0 || $vmax != 1) && $mouseover($path)} { set data(hsb,packed) 1 grid $path.hscroll -column 1 -row $data(hsb,row) \ -sticky ew -ipady $data(ipad) set data(hlock) 1 update idletasks @@ -235,10 +295,12 @@ # ----------------------------------------------------------------------------- # Command ScrolledWindow::_set_vscroll # ----------------------------------------------------------------------------- proc ScrolledWindow::_set_vscroll { path vmin vmax } { + variable mouseover + Widget::getVariable $path data if {$data(realized) && $data(vsb,present)} { if {$data(vsb,auto) && ![info exists data(vlock)]} { if {$data(vsb,packed) && $vmin == 0 && $vmax == 1} { @@ -245,11 +307,11 @@ set data(vsb,packed) 0 grid remove $path.vscroll set data(vlock) 1 update idletasks unset data(vlock) - } elseif {!$data(vsb,packed) && ($vmin != 0 || $vmax != 1) } { + } elseif {!$data(vsb,packed) && ($vmin != 0 || $vmax != 1) && $mouseover($path)} { set data(vsb,packed) 1 grid $path.vscroll -column $data(vsb,column) -row 1 \ -sticky ns -ipadx $data(ipad) set data(vlock) 1 update idletasks