NAME

Tickit::Widget::Table - table widget with support for scrolling/paging

VERSION

version 0.216

SYNOPSIS

#!/usr/bin/env perl
use strict;
use warnings;

use Tickit;
use Tickit::Widget::Table;

my $tbl = Tickit::Widget::Table->new;
$tbl->add_column(
	label => 'Left',
	align => 'left',
	width => 8,
);
$tbl->add_column(
	label => 'Second column',
	align => 'centre'
);
$tbl->adapter->push([ map [qw(left middle)], 1..100 ]);
Tickit->new(root => $tbl)->run;

DESCRIPTION

WARNING: This is still a preview release. API might be subject to change in future, please get in contact if you're using this, or wait for version 1.000.

Basic rendering:

Paged table widget in action

Adapter updating dynamically, styled columns, deferred loading:

Paged table widget in action

This widget provides a scrollable table implementation for use on larger data sets. Rather than populating the table with values, you provide an adapter which implements the count and get methods, and the table widget will query the adapter for the current "page" of values.

This abstraction should allow access to larger datasets than would fit in available memory, such as a database table or procedurally-generated data.

See Adapter::Async::OrderedList::Array if your data is stored in a Perl array. Other subclasses may be available if you have a different source.

Transformations

Apply to:

  • Row

  • Column

  • Cell

Item transformations

This takes the original data item for the row, and returns one of the following:

  • Future - when resolved, the items will be used as cells

  • Arrayref - holds the cells directly

The data item can be anything - an array-backed adapter would return an arrayref, ORM will give you an object for basic collections.

Any number of cells may be returned from a row transformation, but you may get odd results if the cell count is not consistent.

An array adapter needs no row transformation, due to the arrayref behaviour. You could provide a Future alternative:

$row->apply_transformation(sub {
 my ($item) = @_;
 Future->wrap(
  @$item
 )
});

For the ORM example, something like this:

$row->apply_transformation(sub {
 my ($item) = @_;
 Future->wrap(
  map $item->$_, qw(id name created)
 )
});

Column transformations

Column transformations are used to apply styles and formats.

You get an input value, and return either a string or a Future.

Example date+colour transformation on column:

$col->apply_transformation(sub {
 my $v = shift;
 Future->wrap(
  String::Tagged->new(strftime '%Y-%m-%d', $v)
  ->apply_tag(0, 4, b => 1)
  ->apply_tag(5, 1, fg => 8)
  ->apply_tag(6, 2, fg => 4)
  ->apply_tag(9, 1, fg => 8)
 );
});

Cell transformations

Cell transformations are for cases where you need fine control over individual components. They operate similarly to column transformations, taking the input value and returning either a string or a Future.

Typical example would be a spreadsheet:

$cell->apply_transformation(sub {
 my $v = shift;
 return $v unless blessed $v;
 return eval $v if $v->is_formula;
 return $v->to_string if $v->is_formatted;
 return "$v"
});

View transformations

This happen every time the row is rendered. They provide the ability to do view-specific modification, such as replacing long strings with an elided version ("Some lengthy messa...")

METHODS

new

Instantiate. Will attempt to take focus.

Takes the following named parameters:

  • on_activate - coderef to call when the user hits the Enter key, will be passed the highlighted row or selection when in multi_select mode, see "on_activate" for more details.

  • multi_select - when set, the widget will allow selection of multiple rows (typically by pressing Space to toggle a given row)

  • adapter - an Adapter::Async::OrderedList::Array instance

  • data - alternative to passing an adapter, if you want to wrap an existing array without creating an Adapter::Async::OrderedList subclass yourself

Returns a new instance.

bus

Bus for event handling. Normally an Adapter::Async::Bus instance shared by the adapter.

METHODS - Table content

clear

Clear all data in the table.

expose_row

Expose the given row (provided as an index into the underlying storage).

$tbl->expose_row(14);

add_column

Add a new column. Takes the following named parameters:

  • width - (optional) number of columns

  • type - (optional) data type, currently only supports 'text' (the default)

  • align - (optional) align left, center or right

  • transform - (optional) list of transformations to apply

  • visible - (optional) true if this column should be shown

Returns $self.

selected_rows

Returns the selected row, or multiple rows as a list if multi_select is enabled. If multi_select is enabled it does not return the row currently highlighted (unless that row is also selected).

METHODS - Callbacks

on_activate

Accessor for the activation callback - if called without parameters, will return the current coderef (if any), otherwise, will set the new callback.

This callback will be triggered via "key_activate":

$code->($row_index, $row_data_as_arrayref)

If multiselect is enabled, the callback will have the following:

$code->(
  [$highlight_row_index, @selected_row_indices],
  $highlight_row_data_as_arrayref,
  @selected_rows_as_arrayrefs
)

(the selected row data + index list could be empty here)

multi_select

Accessor for multi_select mode - when set, this allows multiple rows to be selected.

METHODS - Other

lines

Number of lines to request.

cols

Number of columns to request.

vscroll

True if there's a vertical scrollbar (currently there is no way to disable this scrollbar).

hscroll

True if there's a horizontal scrollbar. There isn't one, this always returns false.

row_offset

Current row offset (vertical scroll position).

header_rect

Returns the Tickit::Rect representing the header area.

body_rect

Returns the Tickit::Rect representing the body area.

scrollbar_rect

Returns the Tickit::Rect representing the scroll bar.

hide_header

Removes the header - the body will expand upwards to compensate. .

show_header

Makes the header visible again. See "hide_header".

header_visible

Returns true if the header is visible, 0 otherwise.

header_lines

Returns the number of lines in the header. Hardcoded to 1.

body_lines

Returns the number of lines in the body.

body_cols

Returns the number of columns in the body.

idx_from_row

Returns a storage index from a body row index.

row_from_idx

Returns a body row index from a storage index.

row_cache_idx

Returns a row cache offset from a storage index.

idx_from_row_cache

Returns a storage index from a row cache offset.

highlight_row

Returns the index of the currently-highlighted row.

highlight_visible_row

Returns the position of the highlighted row taking scrollbar into account.

METHODS - Rendering

render_to_rb

Render the table. Called from expose events.

render_header

Render the header area.

render_header_cell

Render a specific header cell.

render_scrollbar

Render the scrollbar.

render_body

Render the table body.

render_row

Renders a given row, using storage index.

on_scroll

Update row cache to reflect a scroll event.

fold_future

Helper method to apply a series of coderefs to a value.

row_cache

Row cache accessor.

apply_view_transformations

Apply the transformations just before we render. Can return anything we know how to render.

reshape

Handle reshape requests.

distribute_columns

Distribute space between columns.

window_gained

Called when a window has been assigned to the widget.

expose_rows

Expose the given rows.

scroll_highlight

Update scroll information after changing highlight position.

move_highlight

Move the highlighted row by the given offset (can be negative to move up).

scroll_position

Current vertical scrollbar position.

row_count

Total number of rows.

sb_height

Current scrollbar height.

scroll_rows

Positions of the scrollbar indicator.

active_scrollbar_rect

Rectangle representing the area covered by the current scrollbar.

scroll_dimension

Size of the vertical scrollbar.

on_adapter_change

Applies a new adapter, taking care of any cleanup if there was an adapter previously active.

Can be passed undef, to remove the adapter completely.

on_splice_event

Invoked by the adapter when data is added to or removed from the data source.

on_clear_event

Called by the adapter when all data has been removed from the data source.

METHODS - Key bindings

key_previous_row

Go to the previous row.

key_next_row

Move to the next row.

key_first_row

Move to the first row.

key_last_row

Move to the last row.

key_previous_page

Go up a page.

key_next_page

Go down a page.

key_next_column

Move to the next column.

key_previous_column

Move to the previous column.

key_first_column

Move to the first column.

key_last_column

Move to the last column.

key_activate

Call the on_activate coderef with either the highlighted item, or the selected items if we're in multiselect mode.

$on_activate->([ row indices ], [ items... ])

The items will be as returned by the storage adapter, and will not have any of the data transformations applied.

key_select_toggle

Toggle selected row.

METHODS - Filtering

Very broken. Ignore these for now. Sorry.

row_visibility

Sets the visibility of the given row (by index).

Example:

# Make row 5 hidden
$tbl->row_visibility(5, 0)
# Show row 0
$tbl->row_visibility(0, 1)

filter

This will use the given coderef to set the visibility of each row in the table. The coderef will be called once for each row, and should return true for rows which should be visible, false for rows to be hidden.

The coderef currently takes a single parameter: an arrayref representing the columns of the row to be processed.

# Hide all rows where the second column contains the text 'OK'
$tbl->filter(sub { shift->[1] ne 'OK' });

Note that this does not affect row selection: if the multiselect flag is enabled, it is possible to filter out rows that are selected. This behaviour is by design (the idea was to allow union select via different filter criteria), call the "unselect_hidden_rows" method after filtering if you want to avoid this.

Also note that this is a one-shot operation. If you add or change data, you'll need to reapply the filter operation manually.

unselect_hidden_rows

Helper method to mark any hidden rows as unselected. Call this after "filter" if you want to avoid confusing users with invisible selected rows.

TODO

Current list of pending features:

  • Column and cell highlighting modes

  • Proper widget-in-cell support

  • Better header support (more than one row, embedded widgets)

  • More efficient redraw when showing/hiding header (scroll body and redraw just the header lines)

SEE ALSO

Other tables and table-like things:

And these are probably important background reading for formatting and data source support:

AUTHOR

Tom Molesworth <TEAM@cpan.org>

INHERITED METHODS

Tickit::Widget

get_style_pen, get_style_text, get_style_values, key_focus_next_after, key_focus_next_before, parent, pen, redraw, requested_cols, requested_lines, requested_size, resized, set_parent, set_requested_size, set_style, set_style_tag, set_window, style_classes, take_focus, window, window_lost

CONTRIBUTORS

With thanks to the following for contribution:

  • Paul "LeoNerd" Evans for testing and suggestions on storage/abstraction handling

  • buu, for testing and patches

LICENSE

Copyright Tom Molesworth 2012-2015. Licensed under the same terms as Perl itself.