App-termpub
view release on metacpan or search on metacpan
lib/App/termpub/Pager.pm view on Meta::CPAN
package App::termpub::Pager;
use Mojo::Base -base;
use Curses;
has line => 0;
has positions => sub { {} };
has rows => sub {
my $self = shift;
my ( $rows, $columns );
getmaxyx( $rows, $columns );
$self->columns($columns);
$rows - 2;
};
has columns => sub {
my $self = shift;
my ( $rows, $columns );
getmaxyx( $rows, $columns );
$self->rows( $rows - 2 );
$columns;
};
has 'title';
has pad =>
sub { my $self = shift; newpad( $self->pad_rows, $self->pad_columns ) };
has pad_rows => sub { shift->rows };
has pad_columns => sub { shift->columns };
has key_bindings => sub {
return {
Curses::KEY_DOWN => 'next_line',
Curses::KEY_UP => 'prev_line',
'k' => 'prev_line',
'j' => 'next_line',
"\n" => 'next_line',
' ' => 'next_page',
Curses::KEY_NPAGE => 'next_page',
Curses::KEY_PPAGE => 'prev_page',
Curses::KEY_BACKSPACE => 'prev_page',
Curses::KEY_HOME => 'first_page',
Curses::KEY_END => 'last_page',
Curses::KEY_RESIZE => 'handle_resize',
'q' => 'quit',
'g' => 'goto_line',
'G' => 'goto_line_or_end',
'%' => 'goto_percent',
'm' => 'mark_position',
"'" => 'restore_position',
};
};
has 'prefix' => '';
sub goto_position {
my ( $self, $position ) = @_;
if ( $position->{line}
&& $position->{columns}
&& $position->{columns} == $self->pad_columns )
{
$self->goto_line( $position->{line} );
}
else {
$self->goto_percent( $position->{percent} );
}
}
sub get_position {
my $self = shift;
return {
percent => $self->get_percent,
line => $self->line,
columns => $self->pad_columns,
};
}
sub set_mark {
my ( $self, $position ) = @_;
$self->positions->{"'"} = $position || $self->get_position;
}
sub mark_position {
my $self = shift;
my $c = getch();
if ( $c =~ /[a-z]/ ) {
$self->positions->{$c} = $self->get_position;
}
return;
}
sub restore_position {
my $self = shift;
my $c = getch();
if ( $c =~ /[a-z']/ ) {
return if not exists $self->positions->{$c};
my $old = $self->get_position;
$self->goto_position( $self->positions->{$c} );
$self->set_mark($old);
}
return;
}
sub handle_resize {
my $self = shift;
my ( $rows, $columns );
getmaxyx( $rows, $columns );
$self->rows( $rows - 1 );
$self->columns($columns);
}
sub run {
my $self = shift;
keypad(1);
$self->update_screen;
while (1) {
my $c = getch;
## Clear message line
$self->display_msg;
if ( $c eq '' ) {
$self->prefix('');
next;
}
if ( $c =~ /^[0-9]$/ ) {
$self->prefix( $self->prefix . $c );
next;
}
my $method = $self->key_bindings->{$c};
if ( !$method ) {
$self->display_msg("Key is not bound. Press 'h' for help.");
next;
}
if ( !$self->can($method) ) {
$self->display_msg("Unknown function $method called.");
next;
}
if ( ( $self->$method || '' ) eq 'quit' ) {
last;
}
$self->prefix('');
}
return;
}
sub quit {
return 'quit';
}
sub display_msg {
my ( $self, $msg ) = @_;
move( $self->rows + 1, 0 );
clrtoeol;
addstr($msg) if defined $msg;
return;
}
sub goto_line {
my ( $self, $num ) = @_;
$num ||= ( $self->prefix || 1 ) - 1;
if ( $num <= $self->pad_rows ) {
$self->line($num);
$self->update_screen if $self->rows;
}
return;
}
sub goto_percent {
my ( $self, $num ) = @_;
$num ||= ( $self->prefix || 0 );
$self->set_mark;
$self->goto_line( int( $num * $self->pad_rows / 100 ) );
}
sub goto_line_or_end {
my $self = shift;
$self->set_mark;
if ( $self->prefix ) {
$self->goto_line;
}
else {
$self->last_page;
}
return;
}
sub next_line {
my $self = shift;
if ( $self->line + 1 <= $self->pad_rows
and $self->line + $self->rows <= $self->pad_rows )
{
$self->line( $self->line + 1 );
$self->update_screen;
}
}
sub prev_line {
my $self = shift;
if ( $self->line != 0 ) {
$self->line( $self->line - 1 );
$self->update_screen;
}
}
sub first_page {
my $self = shift;
$self->set_mark;
$self->line(0);
$self->update_screen;
}
sub last_page {
my $self = shift;
$self->set_mark;
my $line = $self->pad_rows - $self->rows + 1;
$self->line( $line >= 0 ? $line : 0 );
$self->update_screen;
}
sub next_page {
my $self = shift;
$self->set_mark;
if ( $self->line + $self->rows <= $self->pad_rows ) {
$self->line( $self->line + $self->rows );
$self->update_screen;
return 1;
}
return 0;
}
sub prev_page {
my $self = shift;
$self->set_mark;
if ( $self->line == 0 && $self->chapter - 1 >= 0 ) {
return 0;
}
if ( $self->line - $self->rows > 0 ) {
$self->line( $self->line - $self->rows );
}
else {
$self->line(0);
}
$self->update_screen;
return 1;
}
sub get_percent {
my $self = shift;
return 0 if !$self->pad_rows;
int( ( $self->line + 1 ) * 100 / $self->pad_rows );
}
sub update_screen {
my $self = shift;
clear;
refresh;
prefresh( $self->pad, $self->line, 0, 0, 0, $self->rows - 1,
$self->pad_columns );
my $status = '-' x $self->columns;
substr( $status, 0, length( $self->title ) ) = $self->title;
my $pos = $self->get_percent . '%';
if ( $self->line + $self->rows - 1 >= $self->pad_rows ) {
$pos = "end";
}
$pos = "($pos)";
substr( $status, -length($pos) - 2, length($pos) ) = $pos;
move( $self->rows, 0 );
addstring($status);
move( $self->rows, 0 );
chgat( -1, A_STANDOUT, 0, 0 );
}
1;
( run in 1.254 second using v1.01-cache-2.11-cpan-99c4e6809bf )