Index: library/button.tcl ================================================================== --- library/button.tcl +++ library/button.tcl @@ -595,16 +595,29 @@ # # Arguments: # w - The name of the widget. proc ::tk::ButtonInvoke w { - if {[$w cget -state] ne "disabled"} { + if {[winfo exists $w] && [$w cget -state] ne "disabled"} { set oldRelief [$w cget -relief] set oldState [$w cget -state] $w configure -state active -relief sunken - update idletasks - after 100 + after 100 [list ::tk::ButtonInvokeEnd $w $oldState $oldRelief] + } +} + +# ::tk::ButtonInvokeEnd -- +# The procedure below is called after a button is invoked through +# the keyboard. It simulate a release of the button via the mouse. +# +# Arguments: +# w - The name of the widget. +# oldState - Old state to be set back. +# oldRelief - Old relief to be set back. + +proc ::tk::ButtonInvokeEnd {w oldState oldRelief} { + if {[winfo exists $w]} { $w configure -state $oldState -relief $oldRelief uplevel #0 [list $w invoke] } } Index: library/scrlbar.tcl ================================================================== --- library/scrlbar.tcl +++ library/scrlbar.tcl @@ -428,10 +428,13 @@ # w - The scrollbar widget. # x, y - Mouse coordinates within the widget. proc ::tk::ScrollButton2Down {w x y} { variable ::tk::Priv + if {![winfo exists $w]} { + return + } set element [$w identify $x $y] if {[string match {arrow[12]} $element]} { ScrollButtonDown $w $x $y return } @@ -441,9 +444,11 @@ # Need the "update idletasks" below so that the widget calls us # back to reset the actual scrollbar position before we start the # slider drag. update idletasks - $w configure -activerelief sunken - $w activate slider - ScrollStartDrag $w $x $y + if {[winfo exists $w]} { + $w configure -activerelief sunken + $w activate slider + ScrollStartDrag $w $x $y + } } Index: tests/button.test ================================================================== --- tests/button.test +++ tests/button.test @@ -3905,10 +3905,28 @@ # A one line text should be as high as -height 1 lappend result [expr {[winfo reqheight .a] == [winfo reqheight .b]}] } -cleanup { destroy .a .b .c } -result {1 1 1} + +test button-14.1 {bug fix: [011706ec42] tk::ButtonInvoke unsafe wrt widget destruction} -body { + proc destroy_button {} { + if {[winfo exists .top.b]} { + destroy .top.b + } + } + toplevel .top + button .top.b -text Foo -command destroy_button + bind .top.b destroy_button + pack .top.b + focus -force .top.b + update + event generate .top.b + update ; # shall not trigger error invalid command name ".top.b" +} -cleanup { + destroy .top.b .top +} -result {} imageFinish cleanupTests return Index: tests/scrollbar.test ================================================================== --- tests/scrollbar.test +++ tests/scrollbar.test @@ -659,10 +659,47 @@ after 200 {set eventprocessed 1} ; vwait eventprocessed .t index @0,0 } -cleanup { destroy .t .s } -result {1.4} + +test scrollbar-11.1 {bug fix: [011706ec42] Scrollbar unsafe wrt widget destruction} -body { + proc destroy_scrollbar {} { + if {[winfo exists .top.s]} { + destroy .top.s + } + } + toplevel .top + scrollbar .top.s + bind .top.s <2> {destroy_scrollbar} + pack .top.s + focus -force .top.s + update + event generate .top.s <2> + update ; # shall not trigger error invalid command name ".top.s" +} -cleanup { + destroy .top.s .top +} -result {} +test scrollbar-11.2 {bug fix: [011706ec42] Scrollbar unsafe wrt widget destruction} -body { + proc destroy_scrollbar {{y 0}} { + if {[winfo exists .top.s]} { + destroy .top.s + } + } + toplevel .top + wm minsize .top 50 400 + update + scrollbar .top.s + bind .top.s <2> {after idle destroy_scrollbar} + pack .top.s -expand true -fill y + focus -force .top.s + update + event generate .top.s <2> -x 2 -y [expr {[winfo height .top.s] / 2}] + update ; # shall not trigger error invalid command name ".top.s" +} -cleanup { + destroy .top.s .top +} -result {} catch {destroy .s} catch {destroy .t} # cleanup