My, this one was a poser. Ven Tatsu's reply is basically correct: during a double-click, the first button-press event will trigger the single-press callback, always. So to flesh out his advice, the following will demonstrate the effect that you want.
#!/usr/bin/perl
use strict;
use Tk;
my $after_id;
my $mw = MainWindow->new();
$mw->Label(-text => "Type a phrase in the entry box below")->pack();
$mw->Label(-text => "Then click once to print all of it")->pack();
$mw->Label(-text => "Or double-click to print just one word")->pack();
my $ent = $mw->Entry(-width => 25)->pack();
$ent->bind( "<Double-Button-1>", [\&print_me, "word"] );
$ent->bind( "<Button-1>",
sub {
$after_id = $ent->after( 500, [\&print_me, $ent] )
}
);
MainLoop;
sub print_me
{
my ( $widget, $mode ) = @_;
my $content = $widget->get();
if ( $mode eq "word" ) {
$widget->afterCancel( $after_id );
my $start = $widget->index( 'sel.first' );
my $length = $widget->index( 'sel.last' ) - $start;
print "you double-clicked the word: " .
substr( $content, $start, $length), $/;
} else {
print $content,$/;
}
}
The way that works is that the initial "button-1" event will invoke "Tk::after" to schedule a call to "print_me" after half a second, but a double-button-1 event will call "print_me" immediately, with a parameter that makes it behave in a special way. The logic associated with that special parameter also has to "cancel" the Tk::after object that had been scheduled by the button-1 event that was the first half of the double-click.
I confess that it seems a bit unappealing. For one thing, I haven't figured out how to do this without, in effect, declaring a global $after_id variable (so that I can cancel the "after" object from a callback); for another, I dislike the idea of making the user wait at all for a response to a single-click, even just half a second. (Maybe the delay could be "optimized" if you can figure out or control the maximum interval that defines the double-click.)
The only other choice I can think of would be to abandon hope for the "intuitive" use of single- versus double-click, and adapt to using two truly distinct events, like "button-1" versus "shift-button-1" or "button-3" or something like that.
update: I fixed and rearranged the alternative event choices in the last paragraph, to make more sense. Also, in case the syntax of the two bind calls seems odd, I wanted to explain that, in the first case, bind will automatically provide the widget handle that receives the event as the first parameter to the referenced callback, so any other parameters provided in the bind call ("word", in this case) will be placed in "@_" after the widget handle. In the second "bind", I'm calling "print_me" from within an anonymous sub, so I have to pass the widget handle explicitly myself.
another update: (making multiple updates is one of my characteristic sins...) If you do decide to use "button-3" instead of double-click for your second event, you can have both events bind to the same callback sub, and include Ev('b') as one of the paramaters passed to it -- this will tell the callback which button was pressed. (I'm not sure how to tell it about a modified event like "shift-button-1" vs. an unmodified "button-1".) | [reply] [d/l] [select] |
... for another, I dislike the idea of making the user wait at all for a response to a single-click, even just half a second. (Maybe the delay could be "optimized" if you can figure out or control the maximum interval that defines the double-click.)
It seems that the interval is defined in pTk/tkBind.c as the NEARBY_MS macro. This macro is set to 500 milliseconds and not changeable in the API, so half a second is a good guess.
| [reply] |
If I understand what your asking there is no easy way to do it that I know of. When you double click Tk sees the first click and calles the code bound to the <Button-1> event, at this point from Tk's perspective there has been no double click. When the second click of the double click is recived the code for the <Double-Button-1> is called, but the code for <Button-1> will already have finished at this point.
The only hack I know to get to recognise that a click event is the first part of a double click is to scedual a sub to be called in about half a second on the first click event and then set a flag on double click (or unscedual the sub and call a different one) that lets the sub know that it was called in a double click. If the flag is not set it's a single click. | [reply] |
I'm not sure if there's an easy way to tell if a it's a double-click within the sub-routine (aside from forcing one sleep of a second and checking if the sub is called again at the end of that second).
However, according to the Perl Cookbook 1st edition, recipe 15.19, you can bind a subroutine to a <Double-Button> like this:
# permit viewing by binding double-click
$l->bind( '<Double-Button>' => \&view );
I haven't any experience with it though. Chapter 14 of Learning Perl/Tk mentions it as <Double-Button-1> though. Of course, Learning Perl/Tk is no longer in print, so you'd have to buy it second-hand. You might also wish to take a look at the second edition of the Perl Cookbook, as it might have more details. Give these a try and let us know what works/doesn't work. . .
- - arden. | [reply] [d/l] |
Since Perl is not (usualy) multi-threading (and when it is Tk is not thread safe), sleeping will delay TK's processing of events, meaning that you still can't see that there is a double click from inside a sub called from the single click event.
Now that I think of it though, $mw->update() should let Tk notice the double click, but I don't know how you would catch that from inside the sub, and I would think it would beg for odd timing issues to crop up.
| [reply] [d/l] [select] |
Heres another double click demo:
#!/usr/bin/perl
use strict;
use Tk;
my ( $clickedon, $released, $double );
my $mw = tkinit;
my $lb = $mw->Scrolled( 'Listbox', -scrollbars => 'osoe' )->pack;
$lb->insert( 0, 'A' .. 'Z' );
$lb->bind( '<ButtonPress-1>',
sub { $clickedon = $lb->get( $lb->curselection ); } );
$lb->bind( '<ButtonRelease-1>',
sub { $released = $lb->get( $lb->curselection ); } );
$lb->bind( '<Double-1>', sub { $double = $lb->get( $lb->curselection )
+; } );
my $f1 = $mw->Frame->pack( -anchor => 'w' );
$f1->Label( -text => 'Clicked on: ' )->pack( -side => 'left' )
+;
$f1->Label( -textvariable => \$clickedon )->pack( -side => 'left' )
+;
my $f2 = $mw->Frame->pack( -anchor => 'w' );
$f2->Label( -text => 'Released on: ' )->pack( -side => 'left'
+);
$f2->Label( -textvariable => \$released )->pack( -side => 'left'
+);
my $f3 = $mw->Frame->pack( -anchor => 'w' );
$f3->Label( -text => 'Double Clicked: ' )->pack( -side => 'lef
+t' );
$f3->Label( -textvariable => \$double )->pack( -side => 'lef
+t' );
MainLoop;
I'm not really a human, but I play one on earth.
flash japh
| [reply] [d/l] |