The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

use strict;
use warnings;

use Test::More;

use Tickit::Test 0.12;

use Tickit::Widget::Scroller;
use Tickit::Widget::Scroller::Item::Text;

# Tests are simpler if the terminal is much smaller
# TODO: mk_window once Tickit::Test can take a size there too
my ( $term, $win ) = mk_term_and_window lines => 5, cols => 15;

my $scrolled_delta = 0;
my $scroller = Tickit::Widget::Scroller->new(
   on_scrolled => sub { $scrolled_delta += $_[1] },
);

$scroller->push(
   map { Tickit::Widget::Scroller::Item::Text->new( "Item of text $_ which is long" ) } 1 .. 9
);

$scroller->set_window( $win );

flush_tickit;

is_termlog( [ GOTO(0,0),
              SETPEN, 
              PRINT("Item of text 1 "),
              GOTO(1,0),
              SETPEN,
              PRINT("which is long"),
              SETBG(undef),
              ERASECH(2),
              GOTO(2,0),
              SETPEN, 
              PRINT("Item of text 2 "),
              GOTO(3,0),
              SETPEN,
              PRINT("which is long"),
              SETBG(undef),
              ERASECH(2),
              GOTO(4,0),
              SETPEN,
              PRINT("Item of text 3 ") ],
            'Termlog initially' );

is_display( [ [TEXT("Item of text 1 ")],
              [TEXT("which is long")],
              [TEXT("Item of text 2 ")],
              [TEXT("which is long")],
              [TEXT("Item of text 3 ")] ],
            'Display initially' );

is( $scroller->item2line( 0,  0 ), 0, 'item2line 0, 0 initially' );
is( $scroller->item2line( 0, -1 ), 1, 'item2line 0, -1 initially' );
is( $scroller->item2line( 1,  0 ), 2, 'item2line 1, 0 initially' );
is( $scroller->item2line( 1, -1 ), 3, 'item2line 1, -1 initially' );
is( $scroller->item2line( 2,  0 ), 4, 'item2line 2, 0 initially' );
is( $scroller->item2line( 2, -1 ), undef, 'item2line 2, -1 initially offscreen' );

is_deeply( [ $scroller->item2line( 2, -1 )    ], [ undef, "below" ], 'list item2line 2, -1 initially below screen' );
is_deeply( [ $scroller->item2line( 2, -1, 1 ) ], [ 5,     "below" ], 'list item2line 2, -1 initially below screen with count_offscreen' );

is( $scroller->lines_above, 0, 'lines_above initially' );
is( $scroller->lines_below, 13, 'lines_below initially' );

$scroller->scroll( +10 );

is( $scrolled_delta, 10, '$scrolled_delta after ->scroll' );

flush_tickit;

is_termlog( [ GOTO(0,0),
              SETPEN, 
              PRINT("Item of text 6 "),
              GOTO(1,0),
              SETPEN,
              PRINT("which is long"),
              SETBG(undef),
              ERASECH(2),
              GOTO(2,0),
              SETPEN, 
              PRINT("Item of text 7 "),
              GOTO(3,0),
              SETPEN,
              PRINT("which is long"),
              SETBG(undef),
              ERASECH(2),
              GOTO(4,0),
              SETPEN,
              PRINT("Item of text 8 ") ],
            'Termlog after scroll +10' );

is_display( [ [TEXT("Item of text 6 ")],
              [TEXT("which is long")],
              [TEXT("Item of text 7 ")],
              [TEXT("which is long")],
              [TEXT("Item of text 8 ")] ],
            'Display after scroll +10' );

is( $scroller->item2line( 0,  0 ), undef, 'item2line 0, 0 offscreen after scroll +10' );
is( $scroller->item2line( 0, -1 ), undef, 'item2line 0, -1 offscreen after scroll +10' );
is( $scroller->item2line( 5,  0 ), 0, 'item2line 5, 0 after scroll +10' );
is( $scroller->item2line( 5, -1 ), 1, 'item2line 5, -1 after scroll +10' );
is( $scroller->item2line( 8,  0 ), undef, 'item2line 8, 0 offscreen after scroll +10' );

is_deeply( [ $scroller->item2line( 0, 0 )    ], [ undef, "above" ], 'list item2line 0, 0 above screen after scroll +10' );
is_deeply( [ $scroller->item2line( 0, 0, 1 ) ], [ -10,   "above" ], 'list item2line 0, 0 above screen after scroll +10 with count_offscreen' );
is_deeply( [ $scroller->item2line( 8, 0 )    ], [ undef, "below" ], 'list item2line 8, 0 below screen after scroll +10' );
is_deeply( [ $scroller->item2line( 8, 0, 1 ) ], [ 6,     "below" ], 'list item2line 8, 0 below screen after scroll +10 with count_offscreen' );

is( $scroller->lines_above, 10, 'lines_above after scroll +10' );
is( $scroller->lines_below,  3, 'lines_below after scroll +10' );

$scroller->scroll( -1 );

is( $scrolled_delta, 9, '$scrolled_delta after ->scroll -1' );

flush_tickit;

is_termlog( [ SETBG(undef),
              SCROLLRECT(0,0,5,15, -1,0),
              GOTO(0,0),
              SETPEN,
              PRINT("which is long"),
              SETBG(undef),
              ERASECH(2) ],
            'Termlog after scroll -1' );

is_display( [ [TEXT("which is long")],
              [TEXT("Item of text 6 ")],
              [TEXT("which is long")],
              [TEXT("Item of text 7 ")],
              [TEXT("which is long")] ],
            'Display after scroll -1' );

is( $scroller->lines_above,  9, 'lines_above after scroll -1' );
is( $scroller->lines_below,  4, 'lines_below after scroll -1' );

$scroller->scroll( +1 );

is( $scrolled_delta, 10, '$scrolled_delta after ->scroll +1' );

flush_tickit;

is_termlog( [ SETBG(undef),
              SCROLLRECT(0,0,5,15, +1,0),
              GOTO(4,0),
              SETPEN,
              PRINT("Item of text 8 ") ],
            'Termlog after scroll +1' );

is_display( [ [TEXT("Item of text 6 ")],
              [TEXT("which is long")],
              [TEXT("Item of text 7 ")],
              [TEXT("which is long")],
              [TEXT("Item of text 8 ")] ],
            'Display after scroll +1' );

$scroller->scroll( -10 );

is( $scrolled_delta, 0, '$scrolled_delta after ->scroll -10' );

flush_tickit;

is_termlog( [ GOTO(0,0),
              SETPEN, 
              PRINT("Item of text 1 "),
              GOTO(1,0),
              SETPEN,
              PRINT("which is long"),
              SETBG(undef),
              ERASECH(2),
              GOTO(2,0),
              SETPEN, 
              PRINT("Item of text 2 "),
              GOTO(3,0),
              SETPEN,
              PRINT("which is long"),
              SETBG(undef),
              ERASECH(2),
              GOTO(4,0),
              SETPEN,
              PRINT("Item of text 3 ") ],
            'Termlog after scroll -10' );

is_display( [ [TEXT("Item of text 1 ")],
              [TEXT("which is long")],
              [TEXT("Item of text 2 ")],
              [TEXT("which is long")],
              [TEXT("Item of text 3 ")] ],
            'Display after scroll -10' );

$scroller->scroll_to_bottom;

is( $scrolled_delta, 13, '$scrolled_delta after ->scroll_to_bottom' );

flush_tickit;

is_termlog( [ GOTO(0,0),
              SETPEN, 
              PRINT("which is long"),
              SETBG(undef),
              ERASECH(2),
              GOTO(1,0),
              SETPEN, 
              PRINT("Item of text 8 "),
              GOTO(2,0),
              SETPEN,
              PRINT("which is long"),
              SETBG(undef),
              ERASECH(2),
              GOTO(3,0),
              SETPEN,
              PRINT("Item of text 9 "),
              GOTO(4,0),
              SETPEN,
              PRINT("which is long"),
              SETBG(undef),
              ERASECH(2) ],
            'Termlog after scroll_to_bottom' );

is_display( [ [TEXT("which is long")],
              [TEXT("Item of text 8 ")],
              [TEXT("which is long")],
              [TEXT("Item of text 9 ")],
              [TEXT("which is long")] ],
            'Display after scroll_to_bottom' );

$scroller->scroll_to_top;

is( $scrolled_delta, 0, '$scrolled_delta after ->scroll_to_top' );

flush_tickit;

is_termlog( [ GOTO(0,0),
              SETPEN, 
              PRINT("Item of text 1 "),
              GOTO(1,0),
              SETPEN,
              PRINT("which is long"),
              SETBG(undef),
              ERASECH(2),
              GOTO(2,0),
              SETPEN, 
              PRINT("Item of text 2 "),
              GOTO(3,0),
              SETPEN,
              PRINT("which is long"),
              SETBG(undef),
              ERASECH(2),
              GOTO(4,0),
              SETPEN,
              PRINT("Item of text 3 ") ],
            'Termlog after scroll_to_top' );

is_display( [ [TEXT("Item of text 1 ")],
              [TEXT("which is long")],
              [TEXT("Item of text 2 ")],
              [TEXT("which is long")],
              [TEXT("Item of text 3 ")] ],
            'Display after scroll_to_top' );

$scroller->scroll_to( 2, 4, 0 ); # About halfway

is( $scrolled_delta, 6, '$scrolled_delta after ->scroll_to halfway' );

flush_tickit;

is_termlog( [ GOTO(0,0),
              SETPEN, 
              PRINT("Item of text 4 "),
              GOTO(1,0),
              SETPEN,
              PRINT("which is long"),
              SETBG(undef),
              ERASECH(2),
              GOTO(2,0),
              SETPEN, 
              PRINT("Item of text 5 "),
              GOTO(3,0),
              SETPEN,
              PRINT("which is long"),
              SETBG(undef),
              ERASECH(2),
              GOTO(4,0),
              SETPEN,
              PRINT("Item of text 6 ") ],
            'Termlog after scroll_to middle' );

is_display( [ [TEXT("Item of text 4 ")],
              [TEXT("which is long")],
              [TEXT("Item of text 5 ")],
              [TEXT("which is long")],
              [TEXT("Item of text 6 ")] ],
            'Display after scroll_to middle' );

$scroller->scroll( +5 );
flush_tickit;
drain_termlog;

{
   my $pre_scroll_delta = $scrolled_delta;

   $scroller->scroll( +5 ); # over the end

   is( $scrolled_delta - $pre_scroll_delta, 2, 'on_scroll given actual delta, not requested' );
   is( $scrolled_delta, 13, '$scrolled_delta after ->scroll +5 over the end' );
}

flush_tickit;

is_termlog( [ SETBG(undef),
              SCROLLRECT(0,0,5,15, +2,0),
              GOTO(3,0),
              SETPEN, 
              PRINT("Item of text 9 "),
              GOTO(4,0),
              SETPEN,
              PRINT("which is long"),
              SETBG(undef),
              ERASECH(2) ],
            'Termlog down past the end' );

is_display( [ [TEXT("which is long")],
              [TEXT("Item of text 8 ")],
              [TEXT("which is long")],
              [TEXT("Item of text 9 ")],
              [TEXT("which is long")] ],
            'Display after scroll down past the end' );

$scroller->scroll( -2 );
$scroller->scroll( -2 );
flush_tickit;

is_termlog( [ SETBG(undef),
              SCROLLRECT(0,0,5,15, -2,0),
              SETBG(undef),
              SCROLLRECT(0,0,5,15, -2,0),
              GOTO(0,0), SETPEN, PRINT("which is long"), SETBG(undef), ERASECH(2),
              GOTO(1,0), SETPEN, PRINT("Item of text 6 "),
              GOTO(2,0), SETPEN, PRINT("which is long"), SETBG(undef), ERASECH(2),
              GOTO(3,0), SETPEN, PRINT("Item of text 7 "), ],
            'Termlog after ->scroll(-2) x 2' );

is_display( [ [TEXT("which is long")],
              [TEXT("Item of text 6 ")],
              [TEXT("which is long")],
              [TEXT("Item of text 7 ")],
              [TEXT("which is long")] ],
            'Display after ->scroll(-2) x 2' );

$scroller->scroll( +2 );
$scroller->scroll( +2 );
flush_tickit;

is_termlog( [ SETBG(undef),
              SCROLLRECT(0,0,5,15, +2,0),
              SETBG(undef),
              SCROLLRECT(0,0,5,15, +2,0),
              GOTO(1,0), SETPEN, PRINT("Item of text 8 "),
              GOTO(2,0), SETPEN, PRINT("which is long"), SETBG(undef), ERASECH(2),
              GOTO(3,0), SETPEN, PRINT("Item of text 9 "),
              GOTO(4,0), SETPEN, PRINT("which is long"), SETBG(undef), ERASECH(2), ],
            'Termlog after ->scroll(+2) x 2' );

is_display( [ [TEXT("which is long")],
              [TEXT("Item of text 8 ")],
              [TEXT("which is long")],
              [TEXT("Item of text 9 ")],
              [TEXT("which is long")] ],
            'Display after ->scroll(+2) x 2' );

is( $scrolled_delta, 13, '$scrolled_delta before EOF' );

done_testing;