summaryrefslogtreecommitdiff
path: root/t
diff options
context:
space:
mode:
authorAndrej Shadura <andrewsh@debian.org>2018-07-28 16:54:34 +0100
committerAndrej Shadura <andrewsh@debian.org>2018-07-28 16:54:34 +0100
commitd38ea3c6312e8075383c3e53004d53db8198446f (patch)
tree1256fab2f0ad29e0bee80ab3441a9934927c34ca /t
Import original source of Tickit-Widget-Scroller 0.23
Diffstat (limited to 't')
-rw-r--r--t/00use.t10
-rw-r--r--t/01item-text.t205
-rw-r--r--t/02item-richtext.t123
-rw-r--r--t/10initial.t117
-rw-r--r--t/11scroll.t380
-rw-r--r--t/12resize-bottom.t102
-rw-r--r--t/12resize-top.t100
-rw-r--r--t/20push-bottom.t188
-rw-r--r--t/20push-top.t181
-rw-r--r--t/21shift-bottom.t177
-rw-r--r--t/21shift-top.t168
-rw-r--r--t/22unshift-bottom.t187
-rw-r--r--t/22unshift-top.t190
-rw-r--r--t/23pop-bottom.t170
-rw-r--r--t/23pop-top.t182
-rw-r--r--t/30indicator.t143
-rw-r--r--t/99pod.t11
17 files changed, 2634 insertions, 0 deletions
diff --git a/t/00use.t b/t/00use.t
new file mode 100644
index 0000000..cce1829
--- /dev/null
+++ b/t/00use.t
@@ -0,0 +1,10 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+
+use_ok( 'Tickit::Widget::Scroller' );
+
+done_testing;
diff --git a/t/01item-text.t b/t/01item-text.t
new file mode 100644
index 0000000..ca5a5fe
--- /dev/null
+++ b/t/01item-text.t
@@ -0,0 +1,205 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+
+use Tickit::Test 0.12;
+use Tickit::RenderBuffer;
+
+use Tickit::Widget::Scroller::Item::Text;
+
+my $term = mk_term;
+
+my $item = Tickit::Widget::Scroller::Item::Text->new( "My message here" );
+
+isa_ok( $item, "Tickit::Widget::Scroller::Item::Text", '$item' );
+
+is_deeply( [ $item->chunks ],
+ [ [ "My message here", 15 ] ],
+ '$item->chunks' );
+
+is( $item->height_for_width( 80 ), 1, 'height_for_width 80' );
+
+my $rb = Tickit::RenderBuffer->new( lines => $term->lines, cols => $term->cols );
+
+$item->render( $rb, top => 0, firstline => 0, lastline => 0, width => 80, height => 25 );
+$rb->flush_to_term( $term );
+
+flush_tickit;
+
+is_termlog( [ GOTO(0,0),
+ SETPEN,
+ PRINT("My message here"),
+ SETBG(undef),
+ ERASECH(65) ],
+ 'Termlog for render fullwidth' );
+
+is_display( [ [TEXT("My message here")] ],
+ 'Display for render fullwidth' );
+
+$term->clear;
+drain_termlog;
+
+{
+ {
+ $rb->save;
+
+ $rb->clip( Tickit::Rect->new(
+ top => 0,
+ left => 0,
+ lines => 10,
+ cols => 12,
+ ) );
+
+ is( $item->height_for_width( 12 ), 2, 'height_for_width 12' );
+
+ $item->render( $rb, top => 0, firstline => 0, lastline => 1, width => 12, height => 10 );
+
+ $rb->restore;
+ }
+
+ $rb->flush_to_term( $term );
+
+ flush_tickit;
+
+ is_termlog( [ GOTO(0,0),
+ SETPEN,
+ PRINT("My message "),
+ SETBG(undef),
+ ERASECH(1),
+ GOTO(1,0),
+ SETPEN,
+ PRINT("here"),
+ SETBG(undef),
+ ERASECH(8) ],
+ 'Termlog for render width 12' );
+
+ is_display( [ [TEXT("My message")],
+ [TEXT("here")] ],
+ 'Display for render width 12' );
+
+ my $indenteditem = Tickit::Widget::Scroller::Item::Text->new( "My message here", indent => 4 );
+
+ is( $indenteditem->height_for_width( 12 ), 2, 'height_for_width 12 with indent' );
+
+ $indenteditem->render( $rb, top => 0, firstline => 0, lastline => 1, width => 12, height => 10 );
+ $rb->flush_to_term( $term );
+
+ flush_tickit;
+
+ is_termlog( [ GOTO(0,0),
+ SETPEN,
+ PRINT("My message "),
+ SETBG(undef),
+ ERASECH(1),
+ GOTO(1,0),
+ SETBG(undef),
+ ERASECH(4,1),
+ SETPEN,
+ PRINT("here"),
+ SETBG(undef),
+ ERASECH(4) ],
+ 'Termlog for render width 12 with indent' );
+
+ is_display( [ [TEXT("My message")],
+ [TEXT(" here")] ],
+ 'Display for render width 12 with indent' );
+}
+
+# Boundary condition in whitespace splitting
+{
+ $term->clear;
+ drain_termlog;
+
+ my $item = Tickit::Widget::Scroller::Item::Text->new( "AAAA BBBB CCCC DDDD" );
+
+ is( $item->height_for_width( 9 ), 2, 'height_for_width 2 for splitting boundary' );
+
+ $item->render( $rb, top => 0, firstline => 0, lastline => 1, width => 9, height => 2 );
+ $rb->flush_to_term( $term );
+
+ flush_tickit;
+
+ is_termlog( [ GOTO(0,0),
+ SETPEN,
+ PRINT("AAAA BBBB"),
+ GOTO(1,0),
+ SETPEN,
+ PRINT("CCCC DDDD") ],
+ 'Termlog for render splitting boundary' );
+
+ is_display( [ [TEXT("AAAA BBBB")],
+ [TEXT("CCCC DDDD")] ],
+ 'Display for render splitting boundary' );
+}
+
+# Linefeeds
+{
+ $term->clear;
+ drain_termlog;
+
+ my $item = Tickit::Widget::Scroller::Item::Text->new( "Some more text\nwith linefeeds" );
+
+ is_deeply( [ $item->chunks ],
+ [ [ "Some more text", 14, linebreak => 1 ],
+ [ "with linefeeds", 14 ] ],
+ '$item->chunks with linefeeds' );
+
+ is( $item->height_for_width( 80 ), 2, 'height_for_width 2 with linefeeds' );
+
+ $item->render( $rb, top => 0, firstline => 0, lastline => 1, width => 80, height => 2 );
+ $rb->flush_to_term( $term );
+
+ flush_tickit;
+
+ is_termlog( [ GOTO(0,0),
+ SETPEN,
+ PRINT("Some more text"),
+ SETPEN,
+ ERASECH(66),
+ GOTO(1,0),
+ SETPEN,
+ PRINT("with linefeeds"),
+ SETPEN,
+ ERASECH(66) ],
+ 'Termlog for render with linefeeds' );
+
+ is_display( [ [TEXT("Some more text")],
+ [TEXT("with linefeeds")] ],
+ 'Display for render with linefeeds' );
+}
+
+# Odd Unicode
+{
+ use utf8;
+
+ $term->clear;
+ drain_termlog;
+
+ my $item = Tickit::Widget::Scroller::Item::Text->new( "(ノಠ益ಠ)ノ彡┻━┻" );
+
+ is_deeply( [ $item->chunks ],
+ [ [ "(ノಠ益ಠ)ノ彡┻━┻", 15 ] ],
+ '$item->chunks with Unicode' );
+
+ is( $item->height_for_width( 80 ), 1, 'height_for_width 2 with Unicode' );
+
+ $item->render( $rb, top => 0, firstline => 0, lastline => 0, width => 80, height => 1 );
+ $rb->flush_to_term( $term );
+
+ flush_tickit;
+
+ is_termlog( [ GOTO(0,0),
+ SETPEN,
+ PRINT("(ノಠ益ಠ)ノ彡┻━┻"),
+ SETPEN,
+ ERASECH(65) ],
+ 'Termlog for render with Unicode' );
+
+ is_display( [ [TEXT("(ノಠ益ಠ)ノ彡┻━┻")] ],
+ 'Display for render with Unicode' );
+}
+
+done_testing;
diff --git a/t/02item-richtext.t b/t/02item-richtext.t
new file mode 100644
index 0000000..a081465
--- /dev/null
+++ b/t/02item-richtext.t
@@ -0,0 +1,123 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+
+use Tickit::Test;
+use Tickit::RenderBuffer;
+
+use String::Tagged;
+use Tickit::Widget::Scroller::Item::RichText;
+
+my $term = mk_term;
+
+my $rb = Tickit::RenderBuffer->new( lines => $term->lines, cols => $term->cols );
+
+my $str = String::Tagged->new( "My message here" );
+$str->apply_tag( 3, 7, b => 1 );
+$str->apply_tag( 11, 4, u => 1 );
+
+my $item = Tickit::Widget::Scroller::Item::RichText->new( $str );
+
+isa_ok( $item, "Tickit::Widget::Scroller::Item::Text", '$item' );
+
+is_deeply( [ $item->chunks ],
+ [ [ "My ", 3, pen => Tickit::Pen->new() ],
+ [ "message", 7, pen => Tickit::Pen->new( b => 1 ) ],
+ [ " ", 1, pen => Tickit::Pen->new() ],
+ [ "here", 4, pen => Tickit::Pen->new( u => 1 ) ] ],
+ '$item->chunks' );
+
+is( $item->height_for_width( 80 ), 1, 'height_for_width 80' );
+
+$item->render( $rb, top => 0, firstline => 0, lastline => 0, width => 80, height => 25 );
+$rb->flush_to_term( $term );
+
+flush_tickit;
+
+is_termlog( [ GOTO(0,0),
+ SETPEN,
+ PRINT("My "),
+ SETPEN(b => 1),
+ PRINT("message"),
+ SETPEN,
+ PRINT(" "),
+ SETPEN(u => 1),
+ PRINT("here"),
+ SETBG(undef),
+ ERASECH(65) ],
+ 'Termlog for render fullwidth' );
+
+is_display( [ [TEXT("My "), TEXT("message",b=>1), BLANK(1), TEXT("here",u=>1)] ],
+ 'Display for render fullwidth' );
+
+# Linefeeds
+{
+ $term->clear;
+ drain_termlog;
+
+ my $str = String::Tagged->new( "Another message\nwith linefeeds" );
+ $str->apply_tag( 8, 12, b => 1 );
+
+ my $item = Tickit::Widget::Scroller::Item::RichText->new( $str );
+
+ is_deeply( [ $item->chunks ],
+ [ [ "Another ", 8, pen => Tickit::Pen->new() ],
+ [ "message", 7, pen => Tickit::Pen->new( b => 1 ), linebreak => 1 ],
+ [ "with", 4, pen => Tickit::Pen->new( b => 1 ) ],
+ [ " linefeeds", 10, pen => Tickit::Pen->new() ] ],
+ '$item->chunks with linefeeds' );
+}
+
+# Word wrapping on pen changes
+{
+ $term->clear;
+ drain_termlog;
+
+ my $str = String::Tagged->new;
+ foreach my $colour (qw( red blue green yellow )) {
+ $str->append_tagged( $colour, fg => $colour );
+ $str->append( " " );
+ }
+
+ my $item = Tickit::Widget::Scroller::Item::RichText->new( $str );
+
+ is( $item->height_for_width( 18 ), 2, 'height_for_width 18 for wrapping pen change' );
+
+ $item->render( $rb, top => 0, firstline => 0, lastline => 1, width => 18, height => 2 );
+ $rb->flush_to_term( $term );
+
+ flush_tickit;
+
+ is_termlog( [ GOTO(0,0),
+ SETPEN(fg=>1),
+ PRINT("red"),
+ SETPEN,
+ PRINT(" "),
+ SETPEN(fg=>4),
+ PRINT("blue"),
+ SETPEN,
+ PRINT(" "),
+ SETPEN(fg=>2),
+ PRINT("green"),
+ SETPEN,
+ PRINT(" "),
+ SETPEN,
+ ERASECH(3),
+ GOTO(1,0),
+ SETPEN(fg=>3),
+ PRINT("yellow"),
+ SETPEN,
+ PRINT(" "),
+ SETPEN,
+ ERASECH(11) ],
+ 'Termlog for render wrapping pen change' );
+
+ is_display( [ [TEXT("red",fg=>1), BLANK(1), TEXT("blue",fg=>4), BLANK(1), TEXT("green",fg=>2)],
+ [TEXT("yellow",fg=>3)] ],
+ 'Display for render wrapping pen change' );
+}
+
+done_testing;
diff --git a/t/10initial.t b/t/10initial.t
new file mode 100644
index 0000000..e42d82f
--- /dev/null
+++ b/t/10initial.t
@@ -0,0 +1,117 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+
+use Tickit::Test;
+
+use Tickit::Widget::Scroller;
+use Tickit::Widget::Scroller::Item::Text;
+
+my $win = mk_window;
+
+my $scroller = Tickit::Widget::Scroller->new;
+
+ok( defined $scroller, 'defined $scroller' );
+
+$scroller->push(
+ map { Tickit::Widget::Scroller::Item::Text->new( $_ ) }
+ "The first line",
+ "Another line in the middle",
+ "The third line",
+);
+
+$scroller->set_window( $win );
+
+flush_tickit;
+
+is_termlog( [ GOTO(0,0),
+ SETPEN,
+ PRINT("The first line"),
+ SETBG(undef),
+ ERASECH(66),
+ GOTO(1,0),
+ SETPEN,
+ PRINT("Another line in the middle"),
+ SETBG(undef),
+ ERASECH(54),
+ GOTO(2,0),
+ SETPEN,
+ PRINT("The third line"),
+ SETBG(undef),
+ ERASECH(66),
+ map { GOTO($_,0), SETBG(undef), ERASECH(80) } 3 .. 24 ],
+ 'Termlog initially' );
+
+is_display( [ "The first line",
+ "Another line in the middle",
+ "The third line" ],
+ 'Display initially' );
+
+is( scalar $scroller->line2item( 0 ), 0, 'scalar line2item 0' );
+is_deeply( [ $scroller->line2item( 0 ) ], [ 0, 0 ], 'line2item 0' );
+is_deeply( [ $scroller->line2item( 1 ) ], [ 1, 0 ], 'line2item 1' );
+is_deeply( [ $scroller->line2item( 2 ) ], [ 2, 0 ], 'line2item 2' );
+is_deeply( [ $scroller->line2item( 3 ) ], [ ], 'line2item 3' );
+
+is_deeply( [ $scroller->line2item( -1 ) ], [ ], 'line2item -1' );
+is_deeply( [ $scroller->line2item( -23 ) ], [ 2, 0 ], 'line2item -23' );
+
+is( $scroller->item2line( 0 ), 0, 'item2line 0' );
+is( $scroller->item2line( 0, -1 ), 0, 'item2line 0, -1' );
+is( $scroller->item2line( 1 ), 1, 'item2line 1' );
+is( $scroller->item2line( 2 ), 2, 'item2line 2' );
+
+is( $scroller->item2line( -1 ), 2, 'item2line -1' );
+
+resize_term( 25, 20 );
+
+flush_tickit;
+
+is_termlog( [ GOTO(0,0),
+ SETPEN,
+ PRINT("The first line"),
+ SETBG(undef),
+ ERASECH(6),
+ GOTO(1,0),
+ SETPEN,
+ PRINT("Another line in the "),
+ GOTO(2,0),
+ SETPEN,
+ PRINT("middle"),
+ SETBG(undef),
+ ERASECH(14),
+ GOTO(3,0),
+ SETPEN,
+ PRINT("The third line"),
+ SETBG(undef),
+ ERASECH(6),
+ map { GOTO($_,0), SETBG(undef), ERASECH(20) } 4 .. 24 ],
+ 'Termlog after narrowing' );
+
+is_display( [ "The first line",
+ "Another line in the ",
+ "middle",
+ "The third line" ],
+ 'Display after narrowing' );
+
+is_deeply( [ $scroller->line2item( 0 ) ], [ 0, 0 ], 'line2item 0' );
+is_deeply( [ $scroller->line2item( 1 ) ], [ 1, 0 ], 'line2item 1' );
+is_deeply( [ $scroller->line2item( 2 ) ], [ 1, 1 ], 'line2item 2' );
+is_deeply( [ $scroller->line2item( 3 ) ], [ 2, 0 ], 'line2item 3' );
+is_deeply( [ $scroller->line2item( 4 ) ], [ ], 'line2item 4' );
+
+is_deeply( [ $scroller->line2item( -1 ) ], [ ], 'line2item -1' );
+is_deeply( [ $scroller->line2item( -22 ) ], [ 2, 0 ], 'line2item -22' );
+
+is( $scroller->item2line( 0 ), 0, 'item2line 0' );
+is( $scroller->item2line( 0, -1 ), 0, 'item2line 0, -1' );
+is( $scroller->item2line( 1 ), 1, 'item2line 1' );
+is( $scroller->item2line( 1, -1 ), 2, 'item2line 1, -1' );
+is( $scroller->item2line( 2 ), 3, 'item2line 2' );
+
+is( $scroller->item2line( -1 ), 3, 'item2line -1' );
+
+done_testing;
diff --git a/t/11scroll.t b/t/11scroll.t
new file mode 100644
index 0000000..0d97fb2
--- /dev/null
+++ b/t/11scroll.t
@@ -0,0 +1,380 @@
+#!/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;
diff --git a/t/12resize-bottom.t b/t/12resize-bottom.t
new file mode 100644
index 0000000..28b2634
--- /dev/null
+++ b/t/12resize-bottom.t
@@ -0,0 +1,102 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+
+use Tickit::Test;
+
+use Tickit::Widget::Scroller;
+use Tickit::Widget::Scroller::Item::Text;
+
+my ( $term, $rootwin ) = mk_term_and_window;
+my $win = $rootwin->make_sub( 0, 0, 5, 40 );
+
+my $scroller = Tickit::Widget::Scroller->new(
+ gravity => "bottom",
+);
+
+$scroller->push( Tickit::Widget::Scroller::Item::Text->new( "A line of content at line $_" ) ) for 1 .. 10;
+
+$scroller->set_window( $win );
+
+$scroller->scroll( +3 );
+
+flush_tickit;
+
+is_display( [ "A line of content at line 4",
+ "A line of content at line 5",
+ "A line of content at line 6",
+ "A line of content at line 7",
+ "A line of content at line 8", ],
+ 'Display initially' );
+
+$term->clear;
+$win->resize( 7, 40 );
+
+flush_tickit;
+
+is_display( [ "A line of content at line 2",
+ "A line of content at line 3",
+ "A line of content at line 4",
+ "A line of content at line 5",
+ "A line of content at line 6",
+ "A line of content at line 7",
+ "A line of content at line 8", ],
+ 'Display after resize more lines' );
+
+$term->clear;
+$win->resize( 5, 40 );
+
+flush_tickit;
+
+is_display( [ "A line of content at line 4",
+ "A line of content at line 5",
+ "A line of content at line 6",
+ "A line of content at line 7",
+ "A line of content at line 8", ],
+ 'Display after resize fewer lines' );
+
+$term->clear;
+$win->resize( 5, 20 );
+
+flush_tickit;
+
+is_display( [ "line 6",
+ "A line of content at",
+ "line 7",
+ "A line of content at",
+ "line 8", ],
+ 'Display after resize fewer columns' );
+
+$term->clear;
+$win->resize( 15, 40 );
+
+flush_tickit;
+
+is_display( [ "A line of content at line 1",
+ "A line of content at line 2",
+ "A line of content at line 3",
+ "A line of content at line 4",
+ "A line of content at line 5",
+ "A line of content at line 6",
+ "A line of content at line 7",
+ "A line of content at line 8",
+ "A line of content at line 9",
+ "A line of content at line 10" ],
+ 'Display after resize too big' );
+
+$term->clear;
+$win->resize( 5, 40 );
+
+flush_tickit;
+
+is_display( [ "A line of content at line 6",
+ "A line of content at line 7",
+ "A line of content at line 8",
+ "A line of content at line 9",
+ "A line of content at line 10" ],
+ 'Display after shrink from resize too big' );
+
+done_testing;
diff --git a/t/12resize-top.t b/t/12resize-top.t
new file mode 100644
index 0000000..2f32438
--- /dev/null
+++ b/t/12resize-top.t
@@ -0,0 +1,100 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+
+use Tickit::Test;
+
+use Tickit::Widget::Scroller;
+use Tickit::Widget::Scroller::Item::Text;
+
+my ( $term, $rootwin ) = mk_term_and_window;
+my $win = $rootwin->make_sub( 0, 0, 5, 40 );
+
+my $scroller = Tickit::Widget::Scroller->new(
+ gravity => "top",
+);
+
+$scroller->push( Tickit::Widget::Scroller::Item::Text->new( "A line of content at line $_" ) ) for 1 .. 10;
+
+$scroller->set_window( $win );
+
+flush_tickit;
+
+is_display( [ "A line of content at line 1",
+ "A line of content at line 2",
+ "A line of content at line 3",
+ "A line of content at line 4",
+ "A line of content at line 5", ],
+ 'Display initially' );
+
+$term->clear;
+$win->resize( 7, 40 );
+
+flush_tickit;
+
+is_display( [ "A line of content at line 1",
+ "A line of content at line 2",
+ "A line of content at line 3",
+ "A line of content at line 4",
+ "A line of content at line 5",
+ "A line of content at line 6",
+ "A line of content at line 7", ],
+ 'Display after resize more lines' );
+
+$term->clear;
+$win->resize( 5, 40 );
+
+flush_tickit;
+
+is_display( [ "A line of content at line 1",
+ "A line of content at line 2",
+ "A line of content at line 3",
+ "A line of content at line 4",
+ "A line of content at line 5", ],
+ 'Display after resize fewer lines' );
+
+$term->clear;
+$win->resize( 5, 20 );
+
+flush_tickit;
+
+is_display( [ "A line of content at",
+ "line 1",
+ "A line of content at",
+ "line 2",
+ "A line of content at", ],
+ 'Display after resize fewer columns' );
+
+$term->clear;
+$win->resize( 15, 40 );
+
+flush_tickit;
+
+is_display( [ "A line of content at line 1",
+ "A line of content at line 2",
+ "A line of content at line 3",
+ "A line of content at line 4",
+ "A line of content at line 5",
+ "A line of content at line 6",
+ "A line of content at line 7",
+ "A line of content at line 8",
+ "A line of content at line 9",
+ "A line of content at line 10" ],
+ 'Display after resize too big' );
+
+$term->clear;
+$win->resize( 5, 40 );
+
+flush_tickit;
+
+is_display( [ "A line of content at line 1",
+ "A line of content at line 2",
+ "A line of content at line 3",
+ "A line of content at line 4",
+ "A line of content at line 5" ],
+ 'Display after shrink from resize too big' );
+
+done_testing;
diff --git a/t/20push-bottom.t b/t/20push-bottom.t
new file mode 100644
index 0000000..4185ac3
--- /dev/null
+++ b/t/20push-bottom.t
@@ -0,0 +1,188 @@
+#!/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;
+
+# TODO: mk_window once Tickit::Test can take a size there too
+my ( $term, $rootwin ) = mk_term_and_window cols => 20, lines => 8;
+my $win = $rootwin->make_sub( 0, 0, 6, 20 );
+
+$rootwin->focus( 7, 0 );
+
+my $scroller = Tickit::Widget::Scroller->new(
+ gravity => "bottom",
+);
+
+$scroller->set_window( $win );
+
+flush_tickit;
+
+is_termlog( [ ( map { GOTO($_,0), SETBG(undef), ERASECH(20) } 0 .. 5 ),
+ GOTO(7,0) ],
+ 'Termlog initially' );
+
+is_display( [ ],
+ 'Display initially' );
+
+is_cursorpos( 7, 0, 'Cursor position intially' );
+
+$scroller->push(
+ Tickit::Widget::Scroller::Item::Text->new( "A line of text" ),
+);
+
+flush_tickit;
+
+is_termlog( [ GOTO(0,0),
+ SETPEN,
+ PRINT("A line of text"),
+ SETBG(undef),
+ ERASECH(6),
+ GOTO(7,0) ],
+ 'Termlog after push' );
+
+is_display( [ [TEXT("A line of text")] ],
+ 'Display after push' );
+
+is_cursorpos( 7, 0, 'Cursor position after push' );
+
+$scroller->push(
+ map { Tickit::Widget::Scroller::Item::Text->new( "Another line $_" ) } 1 .. 4,
+);
+
+flush_tickit;
+
+is_termlog( [ GOTO(1,0),
+ SETPEN,
+ PRINT("Another line 1"),
+ SETBG(undef),
+ ERASECH(6),
+ GOTO(2,0),
+ SETPEN,
+ PRINT("Another line 2"),
+ SETBG(undef),
+ ERASECH(6),
+ GOTO(3,0),
+ SETPEN,
+ PRINT("Another line 3"),
+ SETBG(undef),
+ ERASECH(6),
+ GOTO(4,0),
+ SETPEN,
+ PRINT("Another line 4"),
+ SETBG(undef),
+ ERASECH(6),
+ GOTO(7,0) ],
+ 'Termlog after push 4' );
+
+is_display( [ [TEXT("A line of text")],
+ [TEXT("Another line 1")],
+ [TEXT("Another line 2")],
+ [TEXT("Another line 3")],
+ [TEXT("Another line 4")] ],
+ 'Display after push 4' );
+
+is_cursorpos( 7, 0, 'Cursor position after push 4' );
+
+$scroller->push( Tickit::Widget::Scroller::Item::Text->new( "An item of text that wraps" ) );
+
+flush_tickit;
+
+is_termlog( [ SETBG(undef),
+ SCROLLRECT(0,0,6,20, 1,0),
+ GOTO(4,0),
+ SETPEN,
+ PRINT("An item of text that"),
+ GOTO(5,0),
+ SETPEN,
+ PRINT("wraps"),
+ SETBG(undef),
+ ERASECH(15),
+ GOTO(7,0) ],
+ 'Termlog after push wrapping' );
+
+is_display( [ [TEXT("Another line 1")],
+ [TEXT("Another line 2")],
+ [TEXT("Another line 3")],
+ [TEXT("Another line 4")],
+ [TEXT("An item of text that")],
+ [TEXT("wraps")] ],
+ 'Display after push wrapping' );
+
+is_cursorpos( 7, 0, 'Cursor position after push wrapping' );
+
+$scroller->push(
+ map { Tickit::Widget::Scroller::Item::Text->new( "Another line $_" ) } 5 .. 10,
+);
+
+flush_tickit;
+drain_termlog;
+
+is_display( [ [TEXT("Another line 5")],
+ [TEXT("Another line 6")],
+ [TEXT("Another line 7")],
+ [TEXT("Another line 8")],
+ [TEXT("Another line 9")],
+ [TEXT("Another line 10")] ],
+ 'Display after push 6' );
+
+is_cursorpos( 7, 0, 'Cursor position after push 6' );
+
+$scroller->set_window( undef );
+
+$scroller->push( Tickit::Widget::Scroller::Item::Text->new( "A line while offscreen" ) );
+
+$scroller->set_window( $win );
+
+flush_tickit;
+drain_termlog;
+
+is_display( [ [TEXT("Another line 7")],
+ [TEXT("Another line 8")],
+ [TEXT("Another line 9")],
+ [TEXT("Another line 10")],
+ [TEXT("A line while ")],
+ [TEXT("offscreen")] ],
+ 'Display after push while offscreen' );
+
+is_cursorpos( 7, 0, 'Cursor position after push while offscreen' );
+
+$scroller->scroll_to_top;
+
+flush_tickit;
+drain_termlog;
+
+is_display( [ [TEXT("A line of text")],
+ [TEXT("Another line 1")],
+ [TEXT("Another line 2")],
+ [TEXT("Another line 3")],
+ [TEXT("Another line 4")],
+ [TEXT("An item of text that")] ],
+ 'Display after scroll_to_top' );
+
+is_cursorpos( 7, 0, 'Cursor position after push scroll_to_top' );
+
+$scroller->push(
+ Tickit::Widget::Scroller::Item::Text->new( "Unseen line" ),
+);
+
+is_termlog( [],
+ 'Termlog empty after push at head' );
+
+is_display( [ [TEXT("A line of text")],
+ [TEXT("Another line 1")],
+ [TEXT("Another line 2")],
+ [TEXT("Another line 3")],
+ [TEXT("Another line 4")],
+ [TEXT("An item of text that")] ],
+ 'Display after push at head' );
+
+is_cursorpos( 7, 0, 'Cursor position after push at head' );
+
+done_testing;
diff --git a/t/20push-top.t b/t/20push-top.t
new file mode 100644
index 0000000..b900c6a
--- /dev/null
+++ b/t/20push-top.t
@@ -0,0 +1,181 @@
+#!/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;
+
+# TODO: mk_window once Tickit::Test can take a size there too
+my ( $term, $rootwin ) = mk_term_and_window cols => 20, lines => 8;
+my $win = $rootwin->make_sub( 0, 0, 6, 20 );
+
+$rootwin->focus( 7, 0 );
+
+my $scroller = Tickit::Widget::Scroller->new(
+ gravity => "top",
+);
+
+$scroller->set_window( $win );
+
+flush_tickit;
+
+is_termlog( [ ( map { GOTO($_,0), SETBG(undef), ERASECH(20) } 0 .. 5 ),
+ GOTO(7,0) ],
+ 'Termlog initially' );
+
+is_display( [ ],
+ 'Display initially' );
+
+is_cursorpos( 7, 0, 'Cursor position intially' );
+
+$scroller->push(
+ Tickit::Widget::Scroller::Item::Text->new( "A line of text" ),
+);
+
+flush_tickit;
+
+is_termlog( [ GOTO(0,0),
+ SETPEN,
+ PRINT("A line of text"),
+ SETBG(undef),
+ ERASECH(6),
+ GOTO(7,0) ],
+ 'Termlog after push' );
+
+is_display( [ [TEXT("A line of text")] ],
+ 'Display after push' );
+
+is_cursorpos( 7, 0, 'Cursor position after push' );
+
+$scroller->push(
+ map { Tickit::Widget::Scroller::Item::Text->new( "Another line $_" ) } 1 .. 4,
+);
+
+flush_tickit;
+
+is_termlog( [ GOTO(1,0),
+ SETPEN,
+ PRINT("Another line 1"),
+ SETBG(undef),
+ ERASECH(6),
+ GOTO(2,0),
+ SETPEN,
+ PRINT("Another line 2"),
+ SETBG(undef),
+ ERASECH(6),
+ GOTO(3,0),
+ SETPEN,
+ PRINT("Another line 3"),
+ SETBG(undef),
+ ERASECH(6),
+ GOTO(4,0),
+ SETPEN,
+ PRINT("Another line 4"),
+ SETBG(undef),
+ ERASECH(6),
+ GOTO(7,0) ],
+ 'Termlog after push 4' );
+
+is_display( [ [TEXT("A line of text")],
+ [TEXT("Another line 1")],
+ [TEXT("Another line 2")],
+ [TEXT("Another line 3")],
+ [TEXT("Another line 4")] ],
+ 'Display after push 4' );
+
+is_cursorpos( 7, 0, 'Cursor position after push 4' );
+
+$scroller->push( Tickit::Widget::Scroller::Item::Text->new( "An item of text that wraps" ) );
+
+flush_tickit;
+
+is_termlog( [ GOTO(5,0),
+ SETPEN,
+ PRINT("An item of text that"),
+ GOTO(7,0) ],
+ 'Termlog after push scroll' );
+
+is_display( [ [TEXT("A line of text")],
+ [TEXT("Another line 1")],
+ [TEXT("Another line 2")],
+ [TEXT("Another line 3")],
+ [TEXT("Another line 4")],
+ [TEXT("An item of text that")] ],
+ 'Display after push scroll' );
+
+is_cursorpos( 7, 0, 'Cursor position after push scroll' );
+
+$scroller->push(
+ map { Tickit::Widget::Scroller::Item::Text->new( "Another line $_" ) } 5 .. 10,
+);
+
+flush_tickit;
+
+is_termlog( [],
+ 'Termlog after push 6' );
+
+is_display( [ [TEXT("A line of text")],
+ [TEXT("Another line 1")],
+ [TEXT("Another line 2")],
+ [TEXT("Another line 3")],
+ [TEXT("Another line 4")],
+ [TEXT("An item of text that")] ],
+ 'Display after push 6' );
+
+is_cursorpos( 7, 0, 'Cursor position after push 6' );
+
+$scroller->set_window( undef );
+
+$scroller->push( Tickit::Widget::Scroller::Item::Text->new( "A line while offscreen" ) );
+
+$scroller->set_window( $win );
+
+flush_tickit;
+drain_termlog;
+
+is_display( [ [TEXT("A line of text")],
+ [TEXT("Another line 1")],
+ [TEXT("Another line 2")],
+ [TEXT("Another line 3")],
+ [TEXT("Another line 4")],
+ [TEXT("An item of text that")] ],
+ 'Display after push while offscreen' );
+
+$scroller->scroll_to_top;
+
+flush_tickit;
+drain_termlog;
+
+is_display( [ [TEXT("A line of text")],
+ [TEXT("Another line 1")],
+ [TEXT("Another line 2")],
+ [TEXT("Another line 3")],
+ [TEXT("Another line 4")],
+ [TEXT("An item of text that")] ],
+ 'Display after scroll_to_top' );
+
+is_cursorpos( 7, 0, 'Cursor position after push scroll_to_top' );
+
+$scroller->push(
+ Tickit::Widget::Scroller::Item::Text->new( "Unseen line" ),
+);
+
+is_termlog( [],
+ 'Termlog empty after push at head' );
+
+is_display( [ [TEXT("A line of text")],
+ [TEXT("Another line 1")],
+ [TEXT("Another line 2")],
+ [TEXT("Another line 3")],
+ [TEXT("Another line 4")],
+ [TEXT("An item of text that")] ],
+ 'Display after push at head' );
+
+is_cursorpos( 7, 0, 'Cursor position after push at head' );
+
+done_testing;
diff --git a/t/21shift-bottom.t b/t/21shift-bottom.t
new file mode 100644
index 0000000..22ef5a7
--- /dev/null
+++ b/t/21shift-bottom.t
@@ -0,0 +1,177 @@
+#!/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;
+
+# TODO: mk_window once Tickit::Test can take a size there too
+my ( $term, $rootwin ) = mk_term_and_window cols => 20, lines => 8;
+my $win = $rootwin->make_sub( 0, 0, 6, 20 );
+
+$rootwin->focus( 7, 0 );
+
+my $scroller = Tickit::Widget::Scroller->new(
+ gravity => "bottom",
+);
+
+$scroller->push(
+ Tickit::Widget::Scroller::Item::Text->new( "Existing line $_" ),
+) for 1 .. 20;
+
+$scroller->set_window( $win );
+
+flush_tickit;
+
+is_termlog( [ ( map { GOTO($_-1,0),
+ SETPEN,
+ PRINT("Existing line $_"),
+ SETBG(undef),
+ ERASECH(5) } 1 .. 6 ),
+ GOTO(7,0) ],
+ 'Termlog initially' );
+
+is_display( [ [TEXT("Existing line 1")],
+ [TEXT("Existing line 2")],
+ [TEXT("Existing line 3")],
+ [TEXT("Existing line 4")],
+ [TEXT("Existing line 5")],
+ [TEXT("Existing line 6")] ],
+ 'Display initially' );
+
+is_cursorpos( 7, 0, 'Cursor position intially' );
+
+$scroller->shift;
+
+flush_tickit;
+
+is_termlog( [ SETBG(undef),
+ SCROLLRECT(0,0,6,20, 1,0),
+ GOTO(5,0),
+ SETPEN,
+ PRINT("Existing line 7"),
+ SETBG(undef),
+ ERASECH(5),
+ GOTO(7,0) ],
+ 'Termlog after shift' );
+
+is_display( [ [TEXT("Existing line 2")],
+ [TEXT("Existing line 3")],
+ [TEXT("Existing line 4")],
+ [TEXT("Existing line 5")],
+ [TEXT("Existing line 6")],
+ [TEXT("Existing line 7")] ],
+ 'Display after shift' );
+
+is_cursorpos( 7, 0, 'Cursor position after shift' );
+
+$scroller->shift( 3 );
+
+flush_tickit;
+
+is_termlog( [ SETBG(undef),
+ SCROLLRECT(0,0,6,20, 3,0),
+ GOTO(3,0),
+ SETPEN,
+ PRINT("Existing line 8"),
+ SETBG(undef),
+ ERASECH(5),
+ GOTO(4,0),
+ SETPEN,
+ PRINT("Existing line 9"),
+ SETBG(undef),
+ ERASECH(5),
+ GOTO(5,0),
+ SETPEN,
+ PRINT("Existing line 10"),
+ SETBG(undef),
+ ERASECH(4),
+ GOTO(7,0) ],
+ 'Termlog after shift 3' );
+
+is_display( [ [TEXT("Existing line 5")],
+ [TEXT("Existing line 6")],
+ [TEXT("Existing line 7")],
+ [TEXT("Existing line 8")],
+ [TEXT("Existing line 9")],
+ [TEXT("Existing line 10")] ],
+ 'Display after shift 3' );
+
+is_cursorpos( 7, 0, 'Cursor position after shift 3' );
+
+$scroller->scroll_to_bottom;
+flush_tickit;
+drain_termlog;
+
+is_display( [ [TEXT("Existing line 15")],
+ [TEXT("Existing line 16")],
+ [TEXT("Existing line 17")],
+ [TEXT("Existing line 18")],
+ [TEXT("Existing line 19")],
+ [TEXT("Existing line 20")] ],
+ 'Display after scroll_to_bottom' );
+
+$scroller->shift;
+
+flush_tickit;
+
+is_termlog( [],
+ 'Termlog empty after shift at bottom' );
+
+is_display( [ [TEXT("Existing line 15")],
+ [TEXT("Existing line 16")],
+ [TEXT("Existing line 17")],
+ [TEXT("Existing line 18")],
+ [TEXT("Existing line 19")],
+ [TEXT("Existing line 20")] ],
+ 'Display unchanged after shift at bottom' );
+
+$scroller->scroll_to_top;
+flush_tickit;
+drain_termlog;
+
+is_display( [ [TEXT("Existing line 6")],
+ [TEXT("Existing line 7")],
+ [TEXT("Existing line 8")],
+ [TEXT("Existing line 9")],
+ [TEXT("Existing line 10")],
+ [TEXT("Existing line 11")] ],
+ 'Display after scroll_to_top' );
+
+$scroller->shift( 6 );
+
+flush_tickit;
+
+is_termlog( [ ( map { GOTO($_-12,0),
+ SETPEN,
+ PRINT("Existing line $_"),
+ SETBG(undef),
+ ERASECH(4) } 12 .. 17 ),
+ GOTO(7,0) ],
+ 'Termlog after shift 6 at top' );
+
+is_display( [ [TEXT("Existing line 12")],
+ [TEXT("Existing line 13")],
+ [TEXT("Existing line 14")],
+ [TEXT("Existing line 15")],
+ [TEXT("Existing line 16")],
+ [TEXT("Existing line 17")] ],
+ 'Display after shift 6 at top' );
+
+$scroller->shift( 4 );
+
+flush_tickit;
+
+is_display( [ [TEXT("Existing line 16")],
+ [TEXT("Existing line 17")],
+ [TEXT("Existing line 18")],
+ [TEXT("Existing line 19")],
+ [TEXT("Existing line 20")] ],
+ 'Display after shift to expose bottom' );
+
+done_testing;
diff --git a/t/21shift-top.t b/t/21shift-top.t
new file mode 100644
index 0000000..f40ebe6
--- /dev/null
+++ b/t/21shift-top.t
@@ -0,0 +1,168 @@
+#!/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;
+
+# TODO: mk_window once Tickit::Test can take a size there too
+my ( $term, $rootwin ) = mk_term_and_window cols => 20, lines => 8;
+my $win = $rootwin->make_sub( 0, 0, 6, 20 );
+
+$rootwin->focus( 7, 0 );
+
+my $scroller = Tickit::Widget::Scroller->new(
+ gravity => "top",
+);
+
+$scroller->push(
+ Tickit::Widget::Scroller::Item::Text->new( "Existing line $_" ),
+) for 1 .. 20;
+
+$scroller->set_window( $win );
+
+flush_tickit;
+
+is_termlog( [ ( map { GOTO($_-1,0),
+ SETPEN,
+ PRINT("Existing line $_"),
+ SETBG(undef),
+ ERASECH(5) } 1 .. 6 ),
+ GOTO(7,0) ],
+ 'Termlog initially' );
+
+is_display( [ [TEXT("Existing line 1")],
+ [TEXT("Existing line 2")],
+ [TEXT("Existing line 3")],
+ [TEXT("Existing line 4")],
+ [TEXT("Existing line 5")],
+ [TEXT("Existing line 6")] ],
+ 'Display initially' );
+
+is_cursorpos( 7, 0, 'Cursor position intially' );
+
+my ( $item ) = $scroller->shift;
+
+isa_ok( $item, "Tickit::Widget::Scroller::Item::Text" );
+
+flush_tickit;
+
+is_termlog( [ SETBG(undef),
+ SCROLLRECT(0,0,6,20, 1,0),
+ GOTO(5,0),
+ SETPEN,
+ PRINT("Existing line 7"),
+ SETBG(undef),
+ ERASECH(5),
+ GOTO(7,0) ],
+ 'Termlog after shift' );
+
+is_display( [ [TEXT("Existing line 2")],
+ [TEXT("Existing line 3")],
+ [TEXT("Existing line 4")],
+ [TEXT("Existing line 5")],
+ [TEXT("Existing line 6")],
+ [TEXT("Existing line 7")] ],
+ 'Display after shift' );
+
+is_cursorpos( 7, 0, 'Cursor position after shift' );
+
+$scroller->shift( 3 );
+
+flush_tickit;
+
+is_termlog( [ SETBG(undef),
+ SCROLLRECT(0,0,6,20, 3,0),
+ GOTO(3,0),
+ SETPEN,
+ PRINT("Existing line 8"),
+ SETBG(undef),
+ ERASECH(5),
+ GOTO(4,0),
+ SETPEN,
+ PRINT("Existing line 9"),
+ SETBG(undef),
+ ERASECH(5),
+ GOTO(5,0),
+ SETPEN,
+ PRINT("Existing line 10"),
+ SETBG(undef),
+ ERASECH(4),
+ GOTO(7,0) ],
+ 'Termlog after shift 3' );
+
+is_display( [ [TEXT("Existing line 5")],
+ [TEXT("Existing line 6")],
+ [TEXT("Existing line 7")],
+ [TEXT("Existing line 8")],
+ [TEXT("Existing line 9")],
+ [TEXT("Existing line 10")] ],
+ 'Display after shift 3' );
+
+is_cursorpos( 7, 0, 'Cursor position after shift 3' );
+
+$scroller->scroll_to_bottom;
+flush_tickit;
+drain_termlog;
+
+is_display( [ [TEXT("Existing line 15")],
+ [TEXT("Existing line 16")],
+ [TEXT("Existing line 17")],
+ [TEXT("Existing line 18")],
+ [TEXT("Existing line 19")],
+ [TEXT("Existing line 20")] ],
+ 'Display after scroll_to_bottom' );
+
+$scroller->shift;
+
+flush_tickit;
+
+is_termlog( [],
+ 'Termlog empty after shift at bottom' );
+
+is_display( [ [TEXT("Existing line 15")],
+ [TEXT("Existing line 16")],
+ [TEXT("Existing line 17")],
+ [TEXT("Existing line 18")],
+ [TEXT("Existing line 19")],
+ [TEXT("Existing line 20")] ],
+ 'Display unchanged after shift at bottom' );
+
+$scroller->scroll_to_top;
+flush_tickit;
+drain_termlog;
+
+is_display( [ [TEXT("Existing line 6")],
+ [TEXT("Existing line 7")],
+ [TEXT("Existing line 8")],
+ [TEXT("Existing line 9")],
+ [TEXT("Existing line 10")],
+ [TEXT("Existing line 11")] ],
+ 'Display after scroll_to_top' );
+
+$scroller->shift( 6 );
+
+flush_tickit;
+
+is_termlog( [ ( map { GOTO($_-12,0),
+ SETPEN,
+ PRINT("Existing line $_"),
+ SETBG(undef),
+ ERASECH(4) } 12 .. 17 ),
+ GOTO(7,0) ],
+ 'Termlog after shift 6 at top' );
+
+is_display( [ [TEXT("Existing line 12")],
+ [TEXT("Existing line 13")],
+ [TEXT("Existing line 14")],
+ [TEXT("Existing line 15")],
+ [TEXT("Existing line 16")],
+ [TEXT("Existing line 17")] ],
+ 'Display after shift 6 at top' );
+
+done_testing;
diff --git a/t/22unshift-bottom.t b/t/22unshift-bottom.t
new file mode 100644
index 0000000..4e56a46
--- /dev/null
+++ b/t/22unshift-bottom.t
@@ -0,0 +1,187 @@
+#!/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;
+
+# TODO: mk_window once Tickit::Test can take a size there too
+my ( $term, $rootwin ) = mk_term_and_window cols => 20, lines => 8;
+my $win = $rootwin->make_sub( 0, 0, 6, 20 );
+
+$rootwin->focus( 7, 0 );
+
+my $scroller = Tickit::Widget::Scroller->new(
+ gravity => "bottom",
+);
+
+$scroller->set_window( $win );
+
+flush_tickit;
+
+is_termlog( [ ( map { GOTO($_,0), SETBG(undef), ERASECH(20) } 0 .. 5 ),
+ GOTO(7,0) ],
+ 'Termlog initially' );
+
+is_display( [ ],
+ 'Display initially' );
+
+is_cursorpos( 7, 0, 'Cursor position intially' );
+
+$scroller->unshift(
+ Tickit::Widget::Scroller::Item::Text->new( "A line of text" ),
+);
+
+flush_tickit;
+
+is_termlog( [ GOTO(0,0),
+ SETPEN,
+ PRINT("A line of text"),
+ SETBG(undef),
+ ERASECH(6),
+ GOTO(7,0) ],
+ 'Termlog after unshift' );
+
+is_display( [ [TEXT("A line of text")] ],
+ 'Display after unshift' );
+
+is_cursorpos( 7, 0, 'Cursor position after unshift' );
+
+$scroller->unshift( reverse
+ map { Tickit::Widget::Scroller::Item::Text->new( "Another line $_" ) } 1 .. 4,
+);
+
+flush_tickit;
+
+is_termlog( [ SETBG(undef),
+ SCROLLRECT(0,0,6,20, -4,0),
+ GOTO(0,0),
+ SETPEN,
+ PRINT("Another line 4"),
+ SETBG(undef),
+ ERASECH(6),
+ GOTO(1,0),
+ SETPEN,
+ PRINT("Another line 3"),
+ SETBG(undef),
+ ERASECH(6),
+ GOTO(2,0),
+ SETPEN,
+ PRINT("Another line 2"),
+ SETBG(undef),
+ ERASECH(6),
+ GOTO(3,0),
+ SETPEN,
+ PRINT("Another line 1"),
+ SETBG(undef),
+ ERASECH(6),
+ GOTO(7,0) ],
+ 'Termlog after unshift 4' );
+
+is_display( [ [TEXT("Another line 4")],
+ [TEXT("Another line 3")],
+ [TEXT("Another line 2")],
+ [TEXT("Another line 1")],
+ [TEXT("A line of text")] ],
+ 'Display after unshift 4' );
+
+is_cursorpos( 7, 0, 'Cursor position after unshift 4' );
+
+$scroller->unshift( Tickit::Widget::Scroller::Item::Text->new( "An item of text that wraps" ) );
+
+flush_tickit;
+
+is_termlog( [ SETBG(undef),
+ SCROLLRECT(0,0,6,20, -1,0),
+ GOTO(0,0),
+ SETPEN,
+ PRINT("wraps"),
+ SETBG(undef),
+ ERASECH(15),
+ GOTO(7,0) ],
+ 'Termlog after unshift scroll' );
+
+is_display( [ [TEXT("wraps")],
+ [TEXT("Another line 4")],
+ [TEXT("Another line 3")],
+ [TEXT("Another line 2")],
+ [TEXT("Another line 1")],
+ [TEXT("A line of text")] ],
+ 'Display after unshift scroll' );
+
+is_cursorpos( 7, 0, 'Cursor position after unshift scroll' );
+
+$scroller->unshift( reverse
+ map { Tickit::Widget::Scroller::Item::Text->new( "Another line $_" ) } 5 .. 10,
+);
+
+flush_tickit;
+
+is_termlog( [],
+ 'Termlog after unshift 6' );
+
+is_display( [ [TEXT("wraps")],
+ [TEXT("Another line 4")],
+ [TEXT("Another line 3")],
+ [TEXT("Another line 2")],
+ [TEXT("Another line 1")],
+ [TEXT("A line of text")] ],
+ 'Display after unshift 6' );
+
+is_cursorpos( 7, 0, 'Cursor position after unshift 6' );
+
+$scroller->set_window( undef );
+
+$scroller->unshift( Tickit::Widget::Scroller::Item::Text->new( "A line while offscreen" ) );
+
+$scroller->set_window( $win );
+
+flush_tickit;
+drain_termlog;
+
+is_display( [ [TEXT("wraps")],
+ [TEXT("Another line 4")],
+ [TEXT("Another line 3")],
+ [TEXT("Another line 2")],
+ [TEXT("Another line 1")],
+ [TEXT("A line of text")] ],
+ 'Display after unshift while offscreen' );
+
+$scroller->scroll_to_bottom;
+
+flush_tickit;
+drain_termlog;
+
+is_display( [ [TEXT("wraps")],
+ [TEXT("Another line 4")],
+ [TEXT("Another line 3")],
+ [TEXT("Another line 2")],
+ [TEXT("Another line 1")],
+ [TEXT("A line of text")] ],
+ 'Display after scroll_to_bottom' );
+
+is_cursorpos( 7, 0, 'Cursor position after unshift scroll_to_bottom' );
+
+$scroller->unshift(
+ Tickit::Widget::Scroller::Item::Text->new( "Unseen line" ),
+);
+
+is_termlog( [],
+ 'Termlog empty after unshift at head' );
+
+is_display( [ [TEXT("wraps")],
+ [TEXT("Another line 4")],
+ [TEXT("Another line 3")],
+ [TEXT("Another line 2")],
+ [TEXT("Another line 1")],
+ [TEXT("A line of text")] ],
+ 'Display after unshift at head' );
+
+is_cursorpos( 7, 0, 'Cursor position after unshift at head' );
+
+done_testing;
diff --git a/t/22unshift-top.t b/t/22unshift-top.t
new file mode 100644
index 0000000..9fe302f
--- /dev/null
+++ b/t/22unshift-top.t
@@ -0,0 +1,190 @@
+#!/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;
+
+# TODO: mk_window once Tickit::Test can take a size there too
+my ( $term, $rootwin ) = mk_term_and_window cols => 20, lines => 8;
+my $win = $rootwin->make_sub( 0, 0, 6, 20 );
+
+$rootwin->focus( 7, 0 );
+
+my $scroller = Tickit::Widget::Scroller->new(
+ gravity => "top",
+);
+
+$scroller->set_window( $win );
+
+flush_tickit;
+
+is_termlog( [ ( map { GOTO($_,0), SETBG(undef), ERASECH(20) } 0 .. 5 ),
+ GOTO(7,0) ],
+ 'Termlog initially' );
+
+is_display( [ ],
+ 'Display initially' );
+
+is_cursorpos( 7, 0, 'Cursor position intially' );
+
+$scroller->unshift(
+ Tickit::Widget::Scroller::Item::Text->new( "A line of text" ),
+);
+
+flush_tickit;
+
+is_termlog( [ GOTO(0,0),
+ SETPEN,
+ PRINT("A line of text"),
+ SETBG(undef),
+ ERASECH(6),
+ GOTO(7,0) ],
+ 'Termlog after unshift' );
+
+is_display( [ [TEXT("A line of text")] ],
+ 'Display after unshift' );
+
+is_cursorpos( 7, 0, 'Cursor position after unshift' );
+
+$scroller->unshift( reverse
+ map { Tickit::Widget::Scroller::Item::Text->new( "Another line $_" ) } 1 .. 4,
+);
+
+flush_tickit;
+
+is_termlog( [ SETBG(undef),
+ SCROLLRECT(0,0,6,20, -4,0),
+ GOTO(0,0),
+ SETPEN,
+ PRINT("Another line 4"),
+ SETBG(undef),
+ ERASECH(6),
+ GOTO(1,0),
+ SETPEN,
+ PRINT("Another line 3"),
+ SETBG(undef),
+ ERASECH(6),
+ GOTO(2,0),
+ SETPEN,
+ PRINT("Another line 2"),
+ SETBG(undef),
+ ERASECH(6),
+ GOTO(3,0),
+ SETPEN,
+ PRINT("Another line 1"),
+ SETBG(undef),
+ ERASECH(6),
+ GOTO(7,0) ],
+ 'Termlog after unshift 4' );
+
+is_display( [ [TEXT("Another line 4")],
+ [TEXT("Another line 3")],
+ [TEXT("Another line 2")],
+ [TEXT("Another line 1")],
+ [TEXT("A line of text")] ],
+ 'Display after unshift 4' );
+
+is_cursorpos( 7, 0, 'Cursor position after unshift 4' );
+
+$scroller->unshift( Tickit::Widget::Scroller::Item::Text->new( "An item of text that wraps" ) );
+
+flush_tickit;
+
+is_termlog( [ SETBG(undef),
+ SCROLLRECT(0,0,6,20, -2,0),
+ GOTO(0,0),
+ SETPEN,
+ PRINT("An item of text that"),
+ GOTO(1,0),
+ SETPEN,
+ PRINT("wraps"),
+ SETBG(undef),
+ ERASECH(15),
+ GOTO(7,0) ],
+ 'Termlog after unshift wrapping' );
+
+is_display( [ [TEXT("An item of text that")],
+ [TEXT("wraps")],
+ [TEXT("Another line 4")],
+ [TEXT("Another line 3")],
+ [TEXT("Another line 2")],
+ [TEXT("Another line 1")] ],
+ 'Display after unshift wrapping' );
+
+is_cursorpos( 7, 0, 'Cursor position after unshift wrapping' );
+
+$scroller->unshift( reverse
+ map { Tickit::Widget::Scroller::Item::Text->new( "Another line $_" ) } 5 .. 10,
+);
+
+flush_tickit;
+drain_termlog;
+
+is_display( [ [TEXT("Another line 10")],
+ [TEXT("Another line 9")],
+ [TEXT("Another line 8")],
+ [TEXT("Another line 7")],
+ [TEXT("Another line 6")],
+ [TEXT("Another line 5")], ],
+ 'Display after unshift 6' );
+
+is_cursorpos( 7, 0, 'Cursor position after unshift 6' );
+
+$scroller->set_window( undef );
+
+$scroller->unshift( Tickit::Widget::Scroller::Item::Text->new( "A line while offscreen" ) );
+
+$scroller->set_window( $win );
+
+flush_tickit;
+drain_termlog;
+
+is_display( [ [TEXT("Another line 10")],
+ [TEXT("Another line 9")],
+ [TEXT("Another line 8")],
+ [TEXT("Another line 7")],
+ [TEXT("Another line 6")],
+ [TEXT("Another line 5")], ],
+ 'Display after unshift while offscreen' );
+
+is_cursorpos( 7, 0, 'Cursor position after unshift while offscreen' );
+
+$scroller->scroll_to_bottom;
+
+flush_tickit;
+drain_termlog;
+
+is_display( [ [TEXT("wraps")],
+ [TEXT("Another line 4")],
+ [TEXT("Another line 3")],
+ [TEXT("Another line 2")],
+ [TEXT("Another line 1")],
+ [TEXT("A line of text")] ],
+ 'Display after scroll_to_bottom' );
+
+is_cursorpos( 7, 0, 'Cursor position after scroll_to_bottom' );
+
+$scroller->unshift(
+ Tickit::Widget::Scroller::Item::Text->new( "Unseen line" ),
+);
+
+is_termlog( [],
+ 'Termlog empty after unshift at tail' );
+
+is_display( [ [TEXT("wraps")],
+ [TEXT("Another line 4")],
+ [TEXT("Another line 3")],
+ [TEXT("Another line 2")],
+ [TEXT("Another line 1")],
+ [TEXT("A line of text")] ],
+ 'Display after unshift at tail' );
+
+is_cursorpos( 7, 0, 'Cursor position after unshift at tail' );
+
+done_testing;
diff --git a/t/23pop-bottom.t b/t/23pop-bottom.t
new file mode 100644
index 0000000..f9da224
--- /dev/null
+++ b/t/23pop-bottom.t
@@ -0,0 +1,170 @@
+#!/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;
+
+# TODO: mk_window once Tickit::Test can take a size there too
+my ( $term, $rootwin ) = mk_term_and_window cols => 20, lines => 8;
+my $win = $rootwin->make_sub( 0, 0, 6, 20 );
+
+$rootwin->focus( 7, 0 );
+
+my $scroller = Tickit::Widget::Scroller->new(
+ gravity => "bottom",
+);
+
+$scroller->set_window( $win );
+
+$scroller->unshift(
+ Tickit::Widget::Scroller::Item::Text->new( "Existing line $_" ),
+) for 1 .. 20;
+
+flush_tickit;
+
+is_termlog( [ ( map { SETBG(undef),
+ SCROLLRECT(0,0,6,20, -1,0) } 1 .. 5, ),
+ ( map { GOTO(6-$_,0),
+ SETPEN,
+ PRINT("Existing line $_"),
+ SETBG(undef),
+ ERASECH(5) } reverse 1 .. 6 ),
+ GOTO(7,0) ],
+ 'Termlog initially' );
+
+is_display( [ [TEXT("Existing line 6")],
+ [TEXT("Existing line 5")],
+ [TEXT("Existing line 4")],
+ [TEXT("Existing line 3")],
+ [TEXT("Existing line 2")],
+ [TEXT("Existing line 1")] ],
+ 'Display initially' );
+
+is_cursorpos( 7, 0, 'Cursor position intially' );
+
+my ( $item ) = $scroller->pop;
+
+isa_ok( $item, "Tickit::Widget::Scroller::Item::Text" );
+
+flush_tickit;
+
+is_termlog( [ SETBG(undef),
+ SCROLLRECT(0,0,6,20, -1,0),
+ GOTO(0,0),
+ SETPEN,
+ PRINT("Existing line 7"),
+ SETBG(undef),
+ ERASECH(5),
+ GOTO(7,0) ],
+ 'Termlog after pop' );
+
+is_display( [ [TEXT("Existing line 7")],
+ [TEXT("Existing line 6")],
+ [TEXT("Existing line 5")],
+ [TEXT("Existing line 4")],
+ [TEXT("Existing line 3")],
+ [TEXT("Existing line 2")] ],
+ 'Display after pop' );
+
+is_cursorpos( 7, 0, 'Cursor position after pop' );
+
+$scroller->pop( 3 );
+
+flush_tickit;
+
+is_termlog( [ SETBG(undef),
+ SCROLLRECT(0,0,6,20, -3,0),
+ GOTO(0,0),
+ SETPEN,
+ PRINT("Existing line 10"),
+ SETBG(undef),
+ ERASECH(4),
+ GOTO(1,0),
+ SETPEN,
+ PRINT("Existing line 9"),
+ SETBG(undef),
+ ERASECH(5),
+ GOTO(2,0),
+ SETPEN,
+ PRINT("Existing line 8"),
+ SETBG(undef),
+ ERASECH(5),
+ GOTO(7,0) ],
+ 'Termlog after pop 3' );
+
+is_display( [ [TEXT("Existing line 10")],
+ [TEXT("Existing line 9")],
+ [TEXT("Existing line 8")],
+ [TEXT("Existing line 7")],
+ [TEXT("Existing line 6")],
+ [TEXT("Existing line 5")] ],
+ 'Display after pop 3' );
+
+is_cursorpos( 7, 0, 'Cursor position after pop 3' );
+
+$scroller->scroll_to_top;
+flush_tickit;
+drain_termlog;
+
+is_display( [ [TEXT("Existing line 20")],
+ [TEXT("Existing line 19")],
+ [TEXT("Existing line 18")],
+ [TEXT("Existing line 17")],
+ [TEXT("Existing line 16")],
+ [TEXT("Existing line 15")] ],
+ 'Display after scroll_to_top' );
+
+$scroller->pop;
+
+flush_tickit;
+
+is_termlog( [],
+ 'Termlog empty after pop at top' );
+
+is_display( [ [TEXT("Existing line 20")],
+ [TEXT("Existing line 19")],
+ [TEXT("Existing line 18")],
+ [TEXT("Existing line 17")],
+ [TEXT("Existing line 16")],
+ [TEXT("Existing line 15")] ],
+ 'Display unchanged after pop at top' );
+
+$scroller->scroll_to_bottom;
+flush_tickit;
+drain_termlog;
+
+is_display( [ [TEXT("Existing line 11")],
+ [TEXT("Existing line 10")],
+ [TEXT("Existing line 9")],
+ [TEXT("Existing line 8")],
+ [TEXT("Existing line 7")],
+ [TEXT("Existing line 6")] ],
+ 'Display after scroll_to_bottom' );
+
+$scroller->pop( 6 );
+
+flush_tickit;
+
+is_termlog( [ ( map { GOTO(17-$_,0),
+ SETPEN,
+ PRINT("Existing line $_"),
+ SETBG(undef),
+ ERASECH(4) } reverse 12 .. 17 ),
+ GOTO(7,0) ],
+ 'Termlog after pop 6 at bottom' );
+
+is_display( [ [TEXT("Existing line 17")],
+ [TEXT("Existing line 16")],
+ [TEXT("Existing line 15")],
+ [TEXT("Existing line 14")],
+ [TEXT("Existing line 13")],
+ [TEXT("Existing line 12")] ],
+ 'Display after pop 6 at bottom' );
+
+done_testing;
diff --git a/t/23pop-top.t b/t/23pop-top.t
new file mode 100644
index 0000000..9e1e2e9
--- /dev/null
+++ b/t/23pop-top.t
@@ -0,0 +1,182 @@
+#!/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;
+
+# TODO: mk_window once Tickit::Test can take a size there too
+my ( $term, $rootwin ) = mk_term_and_window cols => 20, lines => 8;
+my $win = $rootwin->make_sub( 0, 0, 6, 20 );
+
+$rootwin->focus( 7, 0 );
+
+my $scroller = Tickit::Widget::Scroller->new(
+ gravity => "top",
+);
+
+$scroller->set_window( $win );
+
+$scroller->unshift(
+ Tickit::Widget::Scroller::Item::Text->new( "Existing line $_" ),
+) for 1 .. 20;
+
+$scroller->scroll_to_bottom;
+
+flush_tickit;
+
+is_termlog( [ ( map { SETBG(undef),
+ SCROLLRECT(0,0,6,20, -1,0) } 1 .. 19 ),
+ ( map { GOTO($_,0),
+ SETPEN,
+ PRINT("Existing line " . ( 6 - $_ )),
+ SETBG(undef),
+ ERASECH(5) } 0 .. 5 ),
+ GOTO(7,0) ],
+ 'Termlog initially' );
+
+is_display( [ [TEXT("Existing line 6")],
+ [TEXT("Existing line 5")],
+ [TEXT("Existing line 4")],
+ [TEXT("Existing line 3")],
+ [TEXT("Existing line 2")],
+ [TEXT("Existing line 1")] ],
+ 'Display initially' );
+
+is_cursorpos( 7, 0, 'Cursor position intially' );
+
+$scroller->pop;
+
+flush_tickit;
+
+is_termlog( [ SETBG(undef),
+ SCROLLRECT(0,0,6,20, -1,0),
+ GOTO(0,0),
+ SETPEN,
+ PRINT("Existing line 7"),
+ SETBG(undef),
+ ERASECH(5),
+ GOTO(7,0) ],
+ 'Termlog after pop' );
+
+is_display( [ [TEXT("Existing line 7")],
+ [TEXT("Existing line 6")],
+ [TEXT("Existing line 5")],
+ [TEXT("Existing line 4")],
+ [TEXT("Existing line 3")],
+ [TEXT("Existing line 2")] ],
+ 'Display after pop' );
+
+is_cursorpos( 7, 0, 'Cursor position after pop' );
+
+$scroller->pop( 3 );
+
+flush_tickit;
+
+is_termlog( [ SETBG(undef),
+ SCROLLRECT(0,0,6,20, -3,0),
+ GOTO(0,0),
+ SETPEN,
+ PRINT("Existing line 10"),
+ SETBG(undef),
+ ERASECH(4),
+ GOTO(1,0),
+ SETPEN,
+ PRINT("Existing line 9"),
+ SETBG(undef),
+ ERASECH(5),
+ GOTO(2,0),
+ SETPEN,
+ PRINT("Existing line 8"),
+ SETBG(undef),
+ ERASECH(5),
+ GOTO(7,0) ],
+ 'Termlog after pop 3' );
+
+is_display( [ [TEXT("Existing line 10")],
+ [TEXT("Existing line 9")],
+ [TEXT("Existing line 8")],
+ [TEXT("Existing line 7")],
+ [TEXT("Existing line 6")],
+ [TEXT("Existing line 5")] ],
+ 'Display after pop 3' );
+
+is_cursorpos( 7, 0, 'Cursor position after pop 3' );
+
+$scroller->scroll_to_top;
+flush_tickit;
+drain_termlog;
+
+is_display( [ [TEXT("Existing line 20")],
+ [TEXT("Existing line 19")],
+ [TEXT("Existing line 18")],
+ [TEXT("Existing line 17")],
+ [TEXT("Existing line 16")],
+ [TEXT("Existing line 15")] ],
+ 'Display after scroll_to_top' );
+
+$scroller->pop;
+
+flush_tickit;
+
+is_termlog( [],
+ 'Termlog empty after pop at top' );
+
+is_display( [ [TEXT("Existing line 20")],
+ [TEXT("Existing line 19")],
+ [TEXT("Existing line 18")],
+ [TEXT("Existing line 17")],
+ [TEXT("Existing line 16")],
+ [TEXT("Existing line 15")] ],
+ 'Display unchanged after pop at top' );
+
+$scroller->scroll_to_bottom;
+flush_tickit;
+drain_termlog;
+
+is_display( [ [TEXT("Existing line 11")],
+ [TEXT("Existing line 10")],
+ [TEXT("Existing line 9")],
+ [TEXT("Existing line 8")],
+ [TEXT("Existing line 7")],
+ [TEXT("Existing line 6")] ],
+ 'Display after scroll_to_bottom' );
+
+$scroller->pop( 6 );
+
+flush_tickit;
+
+is_termlog( [ ( map { GOTO(17-$_,0),
+ SETPEN,
+ PRINT("Existing line $_"),
+ SETBG(undef),
+ ERASECH(4) } reverse 12 .. 17 ),
+ GOTO(7,0) ],
+ 'Termlog after pop 6 at bottom' );
+
+is_display( [ [TEXT("Existing line 17")],
+ [TEXT("Existing line 16")],
+ [TEXT("Existing line 15")],
+ [TEXT("Existing line 14")],
+ [TEXT("Existing line 13")],
+ [TEXT("Existing line 12")] ],
+ 'Display after pop 6 at bottom' );
+
+$scroller->pop( 4 );
+
+flush_tickit;
+
+is_display( [ [TEXT("Existing line 20")],
+ [TEXT("Existing line 19")],
+ [TEXT("Existing line 18")],
+ [TEXT("Existing line 17")],
+ [TEXT("Existing line 16")],
+ [TEXT("Existing line 15")] ],
+ 'Display after pop to expose top' );
+
+done_testing;
diff --git a/t/30indicator.t b/t/30indicator.t
new file mode 100644
index 0000000..1a7502e
--- /dev/null
+++ b/t/30indicator.t
@@ -0,0 +1,143 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+
+use Tickit::Test;
+
+use Tickit::Widget::Scroller;
+use Tickit::Widget::Scroller::Item::Text;
+# These tests depend on the new Window rendering behaviour added in Tickit 0.43
+# but the actual functionallity will work fine before that
+eval { require Tickit::Window; Tickit::Window->VERSION( '0.43' ) }
+ or plan skip_all => "Tickit::Window older than 0.43; these tests won't work";
+
+my $win = mk_window;
+
+my $scroller = Tickit::Widget::Scroller->new(
+ gen_top_indicator => sub {
+ my $self = shift;
+ # TODO: This is a fragile API, needs fixing
+ return sprintf "-- Start{%d/%d} items{%d} --",
+ $self->{start_item}, $self->{start_partial}, scalar @{ $self->{items} };
+ },
+);
+
+$scroller->push(
+ map { Tickit::Widget::Scroller::Item::Text->new( "Line $_ of content" ) } 1 .. 50
+);
+
+$scroller->set_window( $win );
+flush_tickit;
+
+# At Tickit::Window 0.44, rendering is done in one go.
+if( $Tickit::Window::VERSION >= '0.44' ) {
+ is_termlog( [ GOTO(0,0),
+ SETPEN,
+ PRINT("Line 1 of content"),
+ SETBG(undef),
+ ERASECH(37,1),
+ SETPEN(rv=>1),
+ PRINT("-- Start{0/0} items{50} --"),
+
+ ( map { GOTO($_-1,0),
+ SETPEN,
+ PRINT("Line $_ of content"),
+ SETBG(undef),
+ ERASECH(64-length $_), } 2 .. 25 ) ],
+ 'Termlog initially' );
+}
+else {
+ is_termlog( [ GOTO(0,0),
+ SETPEN,
+ PRINT("Line 1 of content"),
+ SETBG(undef),
+ ERASECH(37),
+ ( map { GOTO($_-1,0),
+ SETPEN,
+ PRINT("Line $_ of content"),
+ SETBG(undef),
+ ERASECH(64-length $_), } 2 .. 25 ),
+ GOTO(0,54),
+ SETPEN(rv=>1),
+ PRINT("-- Start{0/0} items{50} --") ],
+ 'Termlog initially' );
+}
+
+is_display( [ [TEXT("Line 1 of content" . (" "x37)), TEXT("-- Start{0/0} items{50} --",rv=>1) ],
+ map { "Line $_ of content" } 2 .. 25 ],
+ 'Display initially' );
+
+$scroller->scroll( 2 );
+flush_tickit;
+
+if( $Tickit::Window::VERSION >= '0.44' ) {
+ is_termlog( [ SETPEN,
+ SCROLLRECT(1,0,24,80,2,0),
+ GOTO(0,0),
+ SETPEN,
+ PRINT("Line 3 of content"),
+ SETBG(undef),
+ ERASECH(37,1),
+ SETPEN(rv=>1),
+ PRINT("-- Start{2/0} items{50} --"),
+ ( map { GOTO($_-3,0),
+ SETPEN,
+ PRINT("Line $_ of content"),
+ SETBG(undef),
+ ERASECH(64-length $_), } 26 .. 27 ) ],
+ 'Termlog after ->scroll' );
+}
+else {
+ is_termlog( [ SETPEN,
+ SCROLLRECT(1,0,24,80,2,0),
+ GOTO(0,0),
+ SETPEN,
+ PRINT("Line 3 of content"),
+ SETBG(undef),
+ ERASECH(37),
+ GOTO(0,54),
+ SETPEN(rv=>1),
+ PRINT("-- Start{2/0} items{50} --"),
+ ( map { GOTO($_-3,0),
+ SETPEN,
+ PRINT("Line $_ of content"),
+ SETBG(undef),
+ ERASECH(64-length $_), } 26 .. 27 ) ],
+ 'Termlog after ->scroll' );
+}
+
+is_display( [ [TEXT("Line 3 of content" . (" "x37)), TEXT("-- Start{2/0} items{50} --",rv=>1) ],
+ map { "Line $_ of content" } 4 .. 27 ],
+ 'Display after ->scroll' );
+
+$scroller->set_gen_top_indicator( undef );
+flush_tickit;
+
+is_termlog( [ GOTO(0,54),
+ SETPEN,
+ ERASECH(26) ],
+ 'Termlog after removing top indicator' );
+
+is_display( [ map { "Line $_ of content" } 3 .. 27 ],
+ 'Display after removing top indicator' );
+
+$scroller->set_gen_bottom_indicator( sub {
+ my $self = shift;
+ defined $self->item2line( -1, -1 ) ? undef : "-- more --"
+} );
+
+flush_tickit;
+
+is_termlog( [ GOTO(24,70),
+ SETPEN(rv=>1),
+ PRINT("-- more --") ],
+ 'Termlog after setting bottom indicator' );
+
+is_display( [ ( map { "Line $_ of content" } 3 .. 26 ),
+ [TEXT("Line 27 of content" . (" "x52)), TEXT("-- more --",rv=>1) ] ],
+ 'Display after setting bottom indicator' );
+
+done_testing;
diff --git a/t/99pod.t b/t/99pod.t
new file mode 100644
index 0000000..eb319fb
--- /dev/null
+++ b/t/99pod.t
@@ -0,0 +1,11 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+
+eval "use Test::Pod 1.00";
+plan skip_all => "Test::Pod 1.00 required for testing POD" if $@;
+
+all_pod_files_ok();