| Rob Seegel 2004-11-27, 9:04 pm |
| I noticed in my revised example that I accidentally left in this binding:
$list->Subwidget('scrolled')->bind('<ButtonPress-3>', \&getPosition);
It was from something I was playing with. One thing that's very annoying
with embedded widgets is that they muck with the event handling for the
parent handling. HList has it's own event Handlers (still somewhat
broken, I see...) that handle selection/deselection of rows. When you
embed widgets within the HList, these widgets obscure the rows -- they
"float" on top of them. Usually this blocking is incomplete, based on
borderwidths, padding, and widget size vs column size. So you end up
with small areas where the HList event handlers are used, and other
areas where the Widget is responsible for handling the events. Depending
on your application and point-of-view this can be a good or a bad thing.
There are a few different ways this can be dealt with, you might
generate events for the HList, from within the handlers you create for
the Entry widgets.
Another thing you might do is assume responsibility for handling certain
types of events. The following extends my previous example a bit more.
In the example I have a method called selectDeselect that I want to bind
to each Entry and to the HList widget so that selection is all happening
in the same place. Also notice that I'm setting focus to the
entry closest to the mouse pointer whenever I press the mouse button.
Note that the getColumn method is probably suspect, because it doesn't
take into account things suchs as borderwidths or padding, but seems to
work ok for this quick and dirty example. The example is also not as
pretty as I would like because it expects certain conventions. A cleaner
way, IMO would be to subclass HList and internally map the
rows/columns to numeric indices. This way you could more readily reuse
the functionality if you needed to do something like this regularly.
use Tk;
## Use Tk::HList, but prevent a few event handlers
## from getting in the way.
use Tk::HList;
sub Tk::HList::ButtonRelease1 {}
sub Tk::HList::Button1 {}
my $mw = MainWindow->new;
my %entryMap;
## Create the Scrolled HList
my $list = $mw->Scrolled("HList",
-selectmode => 'single',
-width => 80,
-columns => 3,
-scrollbars => "osoe",
-background => "white",
-itemtype => 'window',
)->pack(qw/-expand 1 -fill both/);
$list->Subwidget('scrolled')->bind('<ButtonPress-1>', \&selectDeselect);
foreach my $row (0 .. 9) {
my $e = createEntry($list, $row, 0);
my $path = $list->add($row, -itemtype => 'window', -widget => $e);
my $last = $list->cget('-columns') - 1;
foreach my $col (1 .. $last) {
my $item = createEntry($list, $row, $col);
my $itemPath = $list->itemCreate($row, $col,
-itemtype => 'window',
-widget => $item);
}
}
MainLoop;
sub createEntry {
my ($list, $row, $col) = @_;
my $key = "$row,$col";
my $entry = $list->Entry;
$entryMap{$key} = $entry;
$entry->insert(0, "Entry $key");
$entry->bind('<ButtonPress-1>',
[\&selectDeselect, $list, $row, $col]);
return $entry;
}
sub getColumn {
my ($list) = @_;
my $x = $list->XEvent->x;
my $last = $list->cget('-columns');
my $limit = 0;
my $column = $last-1;
for (my $c = 0; $c < $last; $c++) {
$limit += $list->columnWidth($c);
if ($x < $limit) { $column = $c; last; }
}
return $column;
}
sub getRow {
my ($list, $event) = @_;
my $y = $list->XEvent->y;
return $list->nearest($y);
}
sub selectDeselect {
my ($e, $list, $row, $col) = @_;
## If list is undefined then the call must be coming
## from our HList binding.
if (!defined($list)) {
print "SelectDeselect calledon HList\n";
$list = $e;
$row = getRow($list);
$col = getColumn($list);
my $entry = $entryMap{"$row,$col"};
$entry->focus;
}
print "entry $row,$col pressed\n";
my @curSelect = $list->infoSelection();
if (! @curSelect) {
$list->selectionSet($row);
print "Selected row: $row\n";
}
else {
print @curSelect . $curSelect[0] . "\n";
if ($curSelect[0] eq $row) {
$list->selectionClear();
print "Deselected row: $row\n";
}
else {
$list->selectionClear();
$list->selectionSet($row);
print "selected row: $row\n";
}
}
}
|