Tickit-Widget-Scroller

 view release on metacpan or  search on metacpan

lib/Tickit/Widget/Scroller/Item/Text.pm  view on Meta::CPAN


sub BUILDARGS ( $class, $text, %opts ) { return ( text => $text, %opts ) }

field $_indent       :param = undef;
field $_margin_left         = 0;
field $_margin_right        = 0;
field @_chunks;
field $_pen          :param = undef;

field $_width; # width for which the @_lineruns are valid
field @_lineruns;

ADJUST :params (
   :$margin_left  = undef,
   :$margin_right = undef,
   :$margin       = undef,
   :$text         = undef,
) {
   $margin_left  //= $margin;
   $margin_right //= $margin;

   $_margin_left  = $margin_left  if defined $margin_left;
   $_margin_right = $margin_right if defined $margin_right;

   @_chunks = $self->_build_chunks_for( $text );
}

=head1 METHODS

=cut

=head2 chunks

   @chunks = $item->chunks;

Returns the chunks of text displayed by this item. Each chunk is represented
by an ARRAY reference of three fields, giving the text string, its width in
columns, and various options

   [ $text, $width, %opts ]

Recognised options are:

=over 8

=item pen => Tickit::Pen

Pen to render the chunk with.

=item linebreak => BOOL

If true, force a linebreak after this chunk; the next one starts on the
following line.

=back

=cut

method _build_chunks_for ( $text )
{
   my @lines = split m/\n/, $text, -1;
   @lines or @lines = ( "" ); # if blank
   my $lastline = pop @lines;
   return ( map { [ $_, textwidth( $_ ), linebreak => 1 ] } @lines ),
            [ $lastline, textwidth( $lastline ) ];
}

method chunks { @_chunks }

method height_for_width ( $width )
{
   # Just pretend the width doesn't include the margins
   $width -= ( $_margin_left + $_margin_right );

   $_width = $width;

   my @chunks = $self->chunks;
   undef @_lineruns;
   push @_lineruns, my $thisline = [];

   my $line_remaining = $width;

   while( @chunks ) {
      my $chunk = shift @chunks;
      my ( $text, $textwidth, %opts ) = @$chunk;

      if( $textwidth <= $line_remaining ) {
         push @$thisline, [ $text =~ s/\xA0/ /gr, $textwidth, $opts{pen} ];
         $line_remaining -= $textwidth;
      }
      else {
         # Split this chunk at most $line_remaining chars
         my $eol_ch = cols2chars $text, $line_remaining;

         if( $eol_ch < length $text && substr( $text, $eol_ch, 1 ) =~ m/[\S\xA0]/ ) {
            # TODO: This surely must be possible without substr()ing a temporary
            if( substr( $text, 0, $eol_ch ) =~ m/[\S\xA0]+$/ and
                ( $-[0] > 0 or @$thisline ) ) {
               $eol_ch = $-[0];
            }
         }

         my $partial_text = substr( $text, 0, $eol_ch );
         my $partial_chunk = [ $partial_text =~ s/\xA0/ /gr, textwidth( $partial_text ), $opts{pen} ];
         push @$thisline, $partial_chunk;

         my $bol_ch = pos $text = $eol_ch;
         $text =~ m/\G\s+/g and $bol_ch = $+[0];

         my $remaining_text = substr( $text, $bol_ch );
         my $remaining_chunk = [ $remaining_text, textwidth( $remaining_text ), %opts ];
         unshift @chunks, $remaining_chunk;

         $line_remaining = 0;
      }

      if( ( $line_remaining == 0 or $opts{linebreak} ) and @chunks ) {
         push @_lineruns, $thisline = [];
         $line_remaining = $width - ( $_indent || 0 );
      }
   }



( run in 0.907 second using v1.01-cache-2.11-cpan-5511b514fd6 )