The Perl and Raku Conference 2025: Greenville, South Carolina - June 27-29 Learn more

package Table;
use QColor;
use QEvent;
use QPen;
use QWidget;
@ISA = qw(QTableView);
sub new ($$$;@) {
my $class = shift;
my $numRows = shift;
my $numCols = shift;
my $self = $class->SUPER::new(@_);
@$self{'curRow', 'curCol'} = (0, 0);
$self->setFocusPolicy($Focus{Strong});
$self->setBackgroundColor($white);
$self->setNumCols($numCols);
$self->setNumRows($numRows);
$self->setCellWidth(100);
$self->setCellHeight(30);
$self->setTableFlags($Tbl{vScrollBar} |
$Tbl{hScrollBar} |
$Tbl{clipCellPainting} |
$Tbl{smoothScrolling});
$self->resize(400, 200);
$$self{'contents'} = [];
return $self;
}
#
# Return content of cell
#
sub cellContent {
my $self = shift;
my $contents = $$self{'contents'};
my $row = shift;
my $col = shift;
return $$contents[$self->indexOf($row, $col)];
}
#
# Set content of cell
#
sub setCellContent {
my $self = shift;
my($contents, $curRow, $curCol) = @$self{'contents', 'curRow', 'curCol'};
my $row = shift;
my $col = shift;
my $c = shift;
$$contents[$self->indexOf($row, $col)] = $c;
$self->updateCell($row, $col);
}
#
# Handles cell painting for the Table widget.
#
sub paintCell {
my $self = shift;
my($contents, $curRow, $curCol) = @$self{'contents', 'curRow', 'curCol'};
my $p = shift;
my $row = shift;
my $col = shift;
my $w = $self->cellWidth($col);
my $h = $self->cellHeight($row);
my $x2 = $w - 1;
my $y2 = $h - 1;
#
# Draw our part of cell frame.
#
$p->drawLine($x2, 0, $x2, $y2);
$p->drawLine(0, $y2, $x2, $y2);
#
# Draw extra frame inside if this is the current cell.
#
if(($row == $curRow) && ($col == $curCol)) {
if($self->hasFocus()) {
$p->drawRect(0, 0, $x2, $y2);
} else {
$p->setPen($Pen{Dot});
$p->drawRect(0, 0, $x2, $y2);
$p->setPen($Pen{Solid});
}
}
#
# Draw cell content (text)
#
$p->drawText(0, 0, $w, $h, $Align{Center},
$$contents[$self->indexOf($row, $col)]);
}
#
# Handles mouse press events for the Table widget.
# The current cell marker is set to the cell the mouse is clicked in.
#
sub mousePressEvent {
my $self = shift;
my $e = shift;
my($oldRow, $oldCol) = @$self{'curRow', 'curCol'};
my $clickedPos = $e->pos();
my $curRow = $self->findRow($clickedPos->y());
my $curCol = $self->findCol($clickedPos->x());
if(($curRow != $oldRow) ||
($curCol != $oldCol)) {
@$self{'curRow', 'curCol'} = ($curRow, $curCol);
$self->updateCell($oldRow, $oldCol);
$self->updateCell($curRow, $curCol);
}
}
#
# Handles key press events for the Table widget.
# Allows moving the current cell marker around with the arrow keys
#
sub keyPressEvent {
my $self = shift;
my $e = shift;
my($curRow, $curCol) = @$self{'curRow', 'curCol'};
my($oldRow, $oldCol) = @$self{'curRow', 'curCol'};
my $edge = 0;
$_ = $e->key();
if($_ == $Key{Left}) {
if($curCol > 0) {
$curCol--;
$edge = $self->leftCell();
$self->setLeftCell($edge - 1)
if($curCol < $edge);
}
} elsif($_ == $Key{Right}) {
if($curCol < $self->numCols()-1) {
$curCol++;
$edge = $self->lastColVisible();
$self->setLeftCell($self->leftCell() + 1)
if($curCol >= $edge);
}
} elsif($_ == $Key{Up}) {
if($curRow > 0) {
$curRow--;
$edge = $self->topCell();
$self->setTopCell($edge - 1)
if($curRow < $edge);
}
} elsif($_ == $Key{Down}) {
if($curRow < $self->numRows()-1) {
$curRow++;
$edge = $self->lastRowVisible();
$self->setTopCell($self->topCell()+1)
if($curRow >= $edge);
}
} else {
$e->ignore();
return;
}
if(($curRow != $oldRow) ||
($curCol != $oldCol)) {
@$self{'curRow', 'curCol'} = ($curRow, $curCol);
$self->updateCell($oldRow, $oldCol);
$self->updateCell($curRow, $curCol);
}
}
#
# Handles focus reception events for the Table widget.
# Repaint only the current cell; to avoid flickering
#
sub focusInEvent {
my $self = shift;
my($curRow, $curCol) = @$self{'curRow', 'curCol'};
$self->updateCell($curRow, $curCol);
}
#
# Handles focus loss events for the Table widget.
# Repaint only the current cell; to avoid flickering
#
sub focusOutEvent {
my $self = shift;
my($curRow, $curCol) = @$self{'curRow', 'curCol'};
$self->updateCell($curRow, $curCol);
}
#
# Utility function for mapping from 2D table to 1D array
#
sub indexOf {
my $self = shift;
my $row = shift;
my $col = shift;
return ($row * $self->numCols()) + $col;
}