The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
=for comment POD_DERIVED_INDEX_GENERATED
The following documentation is automatically generated.  Please do not edit
this file, but rather the original, inline with Tickit::Widget::Table
at lib/Tickit/Widget/Table.pm
(on the system that originally ran this).
If you do edit this file, and don't want your changes to be removed, make
sure you change the first line.

=encoding utf8

=cut

=head1 NAME

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

=head1 VERSION

version 0.214

=head1 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;

=head1 DESCRIPTION

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

=begin HTML

<p>Basic rendering:</p>
<p><img src="http://tickit.perlsite.co.uk/cpan-screenshot/tickit-widget-table-paged1.gif" alt="Paged table widget in action" width="430" height="306"></p>
<p>Adapter updating dynamically, styled columns, deferred loading:</p>
<p><img src="http://tickit.perlsite.co.uk/cpan-screenshot/tickit-widget-table-paged2.gif" alt="Paged table widget in action" width="539" height="315"></p>

=end HTML

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 C<count> and C<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 L<Adapter::Async::OrderedList::Array> if your data is stored in a Perl
array. Other subclasses may be available if you have a different source.

=head2 Transformations

Apply to:

=over 4

=item * Row

=item * Column

=item * Cell

=back

=head3 Item transformations

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

=over 4

=item * Future - when resolved, the items will be used as cells

=item * Arrayref - holds the cells directly

=back

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)
  )
 });

=head3 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)
  );
 });

=head3 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"
 });

=head3 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...")

=head1 METHODS

=head2 new

Instantiate. Will attempt to take focus.

Takes the following named parameters:

=over 4

=item * on_activate - coderef to call when the user hits the Enter key,
will be passed the highlighted row or selection when in C<multi_select> mode,
see L</on_activate> for more details.

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

=item * adapter - an L<Adapter::Async::OrderedList::Array> instance

=item * data - alternative to passing an adapter, if you want to wrap an existing
array without creating an L<Adapter::Async::OrderedList> subclass yourself

=back

Returns a new instance.

=head2 bus

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

=head1 METHODS - Table content

=head2 clear

Clear all data in the table.

=head2 expose_row

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

 $tbl->expose_row(14);

=head2 add_column

Add a new column. Takes the following named parameters:

=over 4

=item * width - (optional) number of columns

=item * type - (optional) data type, currently only supports 'text' (the default)

=item * align - (optional) align left, center or right

=item * transform - (optional) list of transformations to apply

=item * visible - (optional) true if this column should be shown

=back

Returns $self.

=head2 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).

=head1 METHODS - Callbacks

=head2 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 L</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)

=head2 multi_select

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

=head1 METHODS - Other

=head2 lines

Number of lines to request.

=head2 cols

Number of columns to request.

=head2 vscroll

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

=head2 hscroll

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

=head2 row_offset

Current row offset (vertical scroll position).

=head2 header_rect

Returns the L<Tickit::Rect> representing the header area.

=head2 body_rect

Returns the L<Tickit::Rect> representing the body area.

=head2 scrollbar_rect

Returns the L<Tickit::Rect> representing the scroll bar.

=head2 hide_header

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

sub hide_header {
	my ($self) = @_;
	$self->window->expose if $self->window;
	delete @{$self}{qw(body_rect header_rect scrollbar_rect)};
	$self->{header_visible} = 0;
	$self
}

=head2 show_header

Makes the header visible again. See L</hide_header>.

=head2 header_visible

Returns true if the header is visible, 0 otherwise.

=head2 header_lines

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

=head2 body_lines

Returns the number of lines in the body.

=head2 body_cols

Returns the number of columns in the body.

=head2 idx_from_row

Returns a storage index from a body row index.

=head2 row_from_idx

Returns a body row index from a storage index.

=head2 row_cache_idx

Returns a row cache offset from a storage index.

=head2 idx_from_row_cache

Returns a storage index from a row cache offset.

=head2 highlight_row

Returns the index of the currently-highlighted row.

=head2 highlight_visible_row

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

=head1 METHODS - Rendering

=head2 render_to_rb

Render the table. Called from expose events.

=head2 render_header

Render the header area.

=head2 render_header_cell

Render a specific header cell.

=head2 render_scrollbar

Render the scrollbar.

=head2 render_body

Render the table body.

=head2 render_row

Renders a given row, using storage index.

=head2 on_scroll

Update row cache to reflect a scroll event.

=head2 fold_future

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

=head2 row_cache

Row cache accessor.

=head2 apply_view_transformations

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

=head2 reshape

Handle reshape requests.

=head2 distribute_columns

Distribute space between columns.

=head2 window_gained

Called when a window has been assigned to the widget.

=head2 expose_rows

Expose the given rows.

=head2 scroll_highlight

Update scroll information after changing highlight position.

=head2 move_highlight

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

=head2 scroll_position

Current vertical scrollbar position.

=head2 row_count

Total number of rows.

=head2 sb_height

Current scrollbar height.

=head2 scroll_rows

Positions of the scrollbar indicator.

=head2 active_scrollbar_rect

Rectangle representing the area covered by the current scrollbar.

=head2 scroll_dimension

Size of the vertical scrollbar.

=head2 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.

=head2 on_splice_event

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

=head2 on_clear_event

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

=head1 METHODS - Key bindings

=head2 key_previous_row

Go to the previous row.

=head2 key_next_row

Move to the next row.

=head2 key_first_row

Move to the first row.

=head2 key_last_row

Move to the last row.

=head2 key_previous_page

Go up a page.

=head2 key_next_page

Go down a page.

=head2 key_next_column

Move to the next column.

=head2 key_previous_column

Move to the previous column.

=head2 key_first_column

Move to the first column.

=head2 key_last_column

Move to the last column.

=head2 key_activate

Call the C< 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.

=head2 key_select_toggle

Toggle selected row.

=head1 METHODS - Filtering

Very broken. Ignore these for now. Sorry.

=head2 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)

=head2 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
L</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.

=head2 unselect_hidden_rows

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

=head1 TODO

Current list of pending features:

=over 4

=item * Column and cell highlighting modes

=item * Proper widget-in-cell support

=item * Better header support (more than one row, embedded widgets)

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

=back

=head1 SEE ALSO

Other tables and table-like things:

=over 4

=item * L<Tickit::Widget::Table::Paged> - earlier version of this module without adapter support

=item * L<Text::ANSITable> - not part of L<Tickit> but has some impressive styling capabilities.

=item * L<Term::TablePrint> - again, not part of L<Tickit> but provides an interactive table
via direct terminal access.

=back

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

=over 4

=item * L<String::Tagged> - supported for applying custom formatting (specifically, pen attributes)

=item * L<Adapter::Async> - API for dealing with abstract data sources

=item * L<Adapter::Async::OrderedList> - subclass of the above for our tabular layout API

=back

=head1 AUTHOR

Tom Molesworth <cpan@perlsite.co.uk>

=head1 INHERITED METHODS

=over 4

=item L<Tickit::Widget>

L<get_style_pen|Tickit::Widget/get_style_pen>, L<get_style_text|Tickit::Widget/get_style_text>, L<get_style_values|Tickit::Widget/get_style_values>, L<key_focus_next_after|Tickit::Widget/key_focus_next_after>, L<key_focus_next_before|Tickit::Widget/key_focus_next_before>, L<on_pen_changed|Tickit::Widget/on_pen_changed>, L<parent|Tickit::Widget/parent>, L<pen|Tickit::Widget/pen>, L<redraw|Tickit::Widget/redraw>, L<requested_cols|Tickit::Widget/requested_cols>, L<requested_lines|Tickit::Widget/requested_lines>, L<requested_size|Tickit::Widget/requested_size>, L<resized|Tickit::Widget/resized>, L<set_parent|Tickit::Widget/set_parent>, L<set_pen|Tickit::Widget/set_pen>, L<set_requested_size|Tickit::Widget/set_requested_size>, L<set_style|Tickit::Widget/set_style>, L<set_style_tag|Tickit::Widget/set_style_tag>, L<set_window|Tickit::Widget/set_window>, L<style_classes|Tickit::Widget/style_classes>, L<take_focus|Tickit::Widget/take_focus>, L<window|Tickit::Widget/window>, L<window_lost|Tickit::Widget/window_lost>

=back

=head1 CONTRIBUTORS

With thanks to the following for contribution:

=over 4

=item * Paul "LeoNerd" Evans for testing and suggestions on storage/abstraction handling

=item * buu, for testing and patches

=back

=head1 LICENSE

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