After posting the first version of this module here, and getting some very useful ideas, code suggestions, and documentation help from hangon, (plus a nice synopsis of some of the bugs/issues from wol), I've updated the original code to fix many of the inherent issues.  This documents the updated module, and is also a request for comments, prior to posting this at CPAN.

The new version below addresses the following bugs in the original:

Update 2:  The module is now available on Cpan.  Feel free to continue to give me feedback (naturally!)

Update 1:  Here is the updated module (now containing pod, as well as an example):

package Tk::DoubleClick; our $VERSION = '1.000'; use strict; use warnings; require Exporter; our @ISA = qw(Exporter); our @EXPORT = qw(bind_clicks); # Track last-clicked mouse number, widget, "after" event id and callb +ack. my $h_pend = { 'mn' => 0, 'wi' => 0, 'id' => 0, 'cb' => 0 }; sub bind_clicks { my ($widget, $a_single, $a_double, %args) = @_; my $delay = delete $args{-delay} || 300; my $button = delete $args{-button} || 'left'; my $h_button = { left => 1, middle => 2, right => 3 }; my $mousenum = $h_button->{$button} || $button; ($mousenum =~ /^[123]$/) or $mousenum = 1; my $c_single = $a_single; if (ref $a_single eq 'ARRAY') { my $c_cmd = shift @$a_single; $c_single = sub { $c_cmd->(@$a_single) }; } my $c_double = $a_double; if (ref $a_double eq 'ARRAY') { my $c_cmd = shift @$a_double; $c_double = sub { $c_cmd->(@$a_double) }; } my $button_name = "<Button-$mousenum>"; my $c_pending = sub { my ($mousenum, $widget, $id) = @_; $h_pend->{'mn'} = $mousenum; $h_pend->{'wi'} = $widget; $h_pend->{'id'} = $id; $h_pend->{'cb'} = $c_single; }; my $c_cmd = sub { my $b_sched = 0; # Schedule new single-click? if (!$h_pend->{'id'}) { # No click is pending -- schedule a new one $b_sched = 1; } else { # Cancel pending single-click event $h_pend->{'wi'}->afterCancel($h_pend->{'id'}); $h_pend->{'id'} = 0; if ($h_pend->{'mn'} == $mousenum and $h_pend->{'wi'} eq $w +idget) { # Invoke double-click callback and reset pending event $c_double->(); $c_pending->(0, 0, 0); } else { # Invoke previous single-click, and schedule a new one $h_pend->{'cb'}->(); $b_sched = 1; } } # Schedule new single-click subroutine when $delay expires if ($b_sched) { my $c_after = sub { $c_pending->(0, 0, 0); $c_single->() } +; my $id = $widget->after($delay => $c_after); $c_pending->($mousenum, $widget, $id); } }; $widget->bind($button_name => $c_cmd); } 1; =head1 NAME Tk::Doubleclick - Correctly handle single click vs double click events +, calling only the appropriate callback for the given event. =head1 SYNOPSIS use Tk::Doubleclick; bind_clicks( $widget, [ \&single_callback, @args ], # Single callback with args \&double_callback, # Double callback without arg +s -delay => 500, -button => 'right', ); =head1 REQUIRED PARAMETERS =over 5 =item $widget Widget to bind to mousebuttons. Typically a Tk::Button object, but co +uld actually be almost any widget. =item [ \&single_click_callback, @single_click_args ], The callback subroutine to invoke when the event is a single-click, al +ong with the arguments to pass. When no arguments are passed, the bracket +s can be omitted. =item [ \&double_click_callback, @double_click_args ], The callback subroutine to invoke when the event is a double-click, al +ong with the arguments to pass. When no arguments are passed, the bracket +s can be omitted. =back =head1 OPTIONS =over 5 =item -delay Maximum delay time detween clicks in milliseconds. Default is 300. If the second click of a two proximate mouse clicks occurs within the +given delay time, the event is considered a double-click. If not, the two c +licks are considered two separate (albeit nearly simultaneous) single-clicks +. =item -button Mouse button to bind. Options are 1, 2, 3, or the corresponding synony +ms 'left', 'middle', or 'right'. The default is 1 ('left'). =back =head1 EXAMPLE # Libraries use strict; use warnings; use Tk; use Tk::DoubleClick; # User-defined my $a_colors = [ [ '#8800FF', '#88FF88', '#88FFFF' ], [ '#FF0000', '#FF0088', '#FF00FF' ], [ '#FF8800', '#FF8888', '#FF88FF' ], [ '#FFFF00', '#FFFF88', '#FFFFFF' ], ]; # Main program my $nsingle = my $ndouble = 0; my $mw = new MainWindow(-title => "Double-click example"); my $f1 = $mw->Frame->pack(-expand => 1, -fill => 'both'); my @args = qw( -width 12 -height 2 -relief groove -borderwidth +4 ); my @pack = qw( -side left -expand 1 -fill both ); # Display single/double click counts my $lb1 = $f1->Label(-text => "Single Clicks", @args); my $lb2 = $f1->Label(-textvar => \$nsingle, @args); my $lb3 = $f1->Label(-text => "Double Clicks", @args); my $lb4 = $f1->Label(-textvar => \$ndouble, @args); $lb1->pack($lb2, $lb3, $lb4, @pack); # Create button for each color, and bind single/double clicks to i +t foreach my $a_color (@$a_colors) { my $fr = $mw->Frame->pack(-expand => 1, -fill => 'both'); foreach my $bg (@$a_color) { my $b = $fr->Button(-bg => $bg, -text => $bg, @args); $b->pack(@pack); bind_clicks($b, [\&single, $lb2, $bg], [\&double, $lb4, $b +g]); } } # Make 'Escape' quit the program $mw->bind("<Escape>" => sub { exit }); MainLoop; # Callbacks sub single { my ($lbl, $color) = @_; $lbl->configure(-bg => $color); ++$nsingle; } sub double { my ($lbl, $color) = @_; $lbl->configure(-bg => $color); ++$ndouble; } =head1 ACKNOWLEDGEMENTS Thanks to Mark Freeman for numerous great suggestions and documentatio +n help. =head1 AUTHOR John C. Norton liverpole@perlmonk.org Copyright (c) 2009 John C. Norton. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 VERSION Version 1.000 (August 2009) =head1 REQUIREMENTS The Tk module is required. =head1 SEE ALSO perl(1) =cut

s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/

Replies are listed 'Best First'.
Re: RFC: A Tk module for handling double clicks - Tk::DoubleClick
by hangon (Deacon) on Aug 06, 2009 at 15:30 UTC

    ++ Nice job to solve a nagging problem. Although Perl Tk can also bind triple or quadruple clicks, I don't recall ever seeing an actual program that uses them, so its probably not worth the extra delay to implement. One minor nit is that some parameters are positional, while the convention for Tk module api's seems to be for all parameters to be named values.

      Thanks, hangon.

      I thought about your suggestion to make all parameters non-positional, but it occurs to me that there are a significant number of Tk methods (most notably in Tk::Canvas) where mandatory parameters are positional, and are followed by the optional ones.  Here is just a partial list:

      $canvas->addtag(tag, searchSpec, ?arg, arg, ...?) $canvas->bbox(tagOrId, ?tagOrId, tagOrId, ...?) $canvas->create(type, x, y, ?x, y, ...?, ?option, value, ...?) $canvas->createArc(x1, y1, x2, y2, ?option, value, option, value, ...? +) $canvas->createBitmap(x, y, ?option, value, option, value, ...?) $canvas->createGrid(x1, y1, x2, y2, ?option, value, option, value, ... +?) $canvas->createImage(x, y, ?option, value, option, value, ...?) $canvas->createLine(x1, y1..., xn, yn, ?option, value, option, value, +...?) $canvas->createOval(x1, y1, x2, y2, ?option, value, option, value, ... +?) $canvas->createPolygon(x1, y1, ..., xn, yn, ?option, value, option, va +lue, ...?) $canvas->createRectangle(x1, y1, x2, y2, ?option, value, option, value +, ...?) $canvas->createText(x, y, ?option, value, option, value, ...?) $canvas->createWindow(x, y?, -option=>value, -option=>value, ...?)

      As the widget and the single-click and double-click callbacks are mandatory, it seems like they work more naturally as positional arguments.


      s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
Re: RFC: A Tk module for handling double clicks - Tk::DoubleClick
by Anonymous Monk on Jul 02, 2014 at 16:58 UTC

    This code worked like a dream...

    Many thanks for solving my problem - I've just spent several hours trying to figure out why double-clicks were totally random for the Tk::HList widget and this fixed it completely on the first pass !!

    Again, thanks to you and all the other contributors !!