widget:repeaterButton

GPS - Sun Mar 10, 2002: I looked at some code to a button that repeated an action and found it to be overly complex, so that inspired me to whip up this concise little tool.


  #!/bin/wish8.4
  #updated Tue Nov 12, 2002
   proc bind:copyClass {class newClass} {
    foreach binding [bind $class] {
      bind $newClass $binding [bind $class $binding]
    }
  }
  
  proc widget:repeaterButton:ButtonPress-1 {win} {
    eval [$win cget -command]
    if {$::buttonPressed == 1} {
      $win configure -relief sunken
      after 100 [list widget:repeaterButton:ButtonPress-1 $win]
    }
  }
  
  proc widget:repeaterButton:ButtonRelease-1 {win} {
    $win configure -relief raised
  }
  
  proc widget:repeaterButton {win args} {
    bind:copyClass Button RepeaterButton
  
    bind RepeaterButton <ButtonPress-1> {set ::buttonPressed 1; widget:repeaterButton:ButtonPress-1 %W}
    bind RepeaterButton <ButtonRelease-1> {set ::buttonPressed 0; widget:repeaterButton:ButtonRelease-1 %W}
  
    eval button [concat $win $args]
  
    bindtags $win [list RepeaterButton all]
    return $win
  }
  
  
  catch {console show}
  pack [widget:repeaterButton .b -text Hello -command {puts Hello}]

It's nice, but it repeats too fast, I think. Is there a way to slow it down?

AK - What happens when you increase the timeout of the after command ?

GPS: I'm glad you like it. Increasing the after timeout as AK suggested will fix the issue. Also, note the update to it above which uses concat to fix an issue with passing some arguments.


JPT : I've found your code very interesting, and decided to propose a version that allows the creation of multiple repeaterButtons by the use of individual proc and variables for each new instance (I left aside the "bindtags" command, not knowing exactly what's its purpose).

  proc RepeaterButton {w args} {
    proc ${w}_command {w} {
     if {![set ::${w}_ok]} {
       $w config -relief raised
       return
     }
     $w config -relief sunken
     uplevel eval [$w cget -command]
     after 50 "${w}_command $w"
    }
    catch {destroy $w}
    eval button [concat $w $args]
    bind $w <ButtonPress-1> {set ::%W_ok 1; %W_command %W}
    bind $w <ButtonRelease-1> {set ::%W_ok 0}
    return $w
  }

  pack [RepeaterButton  .rb -text incr -command {incr i}]
  pack [RepeaterButton  .rb2 -text decr -command {incr i -1}]
  pack [label .l -textvariable i]
  set i 0

GPS I used the bindtags command to remove the default bindings for a button, and replace them with my modified bindings. Using uplevel to eval the -command is a good idea. I'm glad you were able to adapt the code for your own use. :)


JES Here it is with mods to have a 1 second pause before starting repeating behavior. I also modified what the command calls itself... for some reason winfo children thought ${w}_command was a widget, not a regular proc.

 proc RepeaterButton {w args} {
     proc command${w} {w pause} {
     if {![set ::${w}_ok]} {
       return
     }
     $w config -relief sunken
     uplevel eval [$w cget -command]
     after $pause "command${w} $w 100"
    }
    catch {destroy $w}
    eval button [concat $w $args]
    bind $w <ButtonPress-1> {set ::%W_ok 1; command%W %W 1000}
    bind $w <ButtonRelease-1> "set ::%W_ok 0; $w config -relief raised"
    bind $w <Leave> [ bind Button <Leave> ]
    bind $w <Enter> [ bind Button <Enter> ]
    bindtags $w [lreplace [bindtags $w] 1 1 ]
    return $w
 }