Version 6 of Binding to a single mouse click

Updated 2015-10-13 23:10:47 by RLE

Purpose: Explain how to establish a binding on a single mouse click, distinguished from a double click, and explain why not to do so.


Rick Hedin wrote:

There is a Double and a Triple event. When one wants just a plain old Single (or Click) event, that has only one click but applies the time interval and space interval contraints of Double and Triple, does one just do it oneself?

Kevin Kenny answered:

What <Double-Button1> actually means is "a click on button 1 within a certain number of milliseconds of the previous click", and <Triple-Button1> means "a click on button 1 within a certain number of milliseconds of a previous double-click". The unmodified <Button1> is always delivered first. So a doubleclick of button 1 delivers the four events:

     <1>
     <ButtonRelease-1>
     <Double-1>
     <Double-ButtonRelease-1>

(willdye: Note that the "certain number of milliseconds" interval can be defined in platform-specific user settings.)

Now, the trick here is that most apps don't care! For instance, in a typical application of a text widget, a single click sets the insertion cursor at the mouse, a double click expands the selection to the word around the cursor, and a triple click expands the selection to the line. Each builds on what has gone before.

Some other applications work by having the double click undo what the single click has done.

The hardest of all to get right is an application where the single click does something that's hard to undo, and the double click does something else entirely. The reason this is hard to get right is that it conflicts with users' unconscious expectations. The single click now cannot operate immediately. It has to "look ahead in time" to determine whether or not it's the first half of a double click, and act only if the second click fails to arrive. Tk doesn't build that functionality in, partly because it's not what people actually want in most cases.

If you must have it, though, it's doable. Try the following code. When you try it, though, note that the double click fires immediately, while the single click has a delay. Many users find that delay annoying, although they often can't tell you just why the application "doesn't feel right."

 grid [label .foo -text "Click here"] -sticky ew
 grid [label .bar -width 20 -textvariable clickType] -sticky ew
 
 bind .foo <Double-1> {doubleclick %W}
 bind .foo <1> {maybe_singleclick %W}
 
 proc maybe_singleclick { w } {
     variable singleclick_timer
     # If desired, replace the hard-coded '500' with a call that looks
     # up the platform-specific delay value for double-click detection.
     set singleclick_timer($w) [after 500 [list singleclick $w]]
     return
 }
 
 proc doubleclick { w } {
     variable singleclick_timer
     catch {
         after cancel $singleclick_timer($w)
         unset singleclick_timer($w)
     }
     set ::clickType {Double-Click}
     return
 }
 
 proc singleclick { w } {
     variable singleclick_timer
     catch {
         unset singleclick_timer($w)
     }
     set ::clickType {Single-Click}
     return
 }