Audio-Nama

 view release on metacpan or  search on metacpan

lib/Audio/Nama/Completion.pm  view on Meta::CPAN

=head1 DESCRIPTION

This package applies code to a L<Tickit::Widget::Entry> instance to implement
word-completion logic while editing. This logic is activated by pressing the
C<< <Tab> >> key.

If the word currently being edited has a unique match in the list of words,
then the word is completed entirely, followed by a space.

If there are multiple words that could complete from the word at the cursor,
then a popup menu is presented showing the next available characters or
matches. The user can continue typing more characters to narrow down the
choice until a unique match is found.

=cut

=head1 METHODS

=cut

=head2 apply

lib/Audio/Nama/Completion.pm  view on Meta::CPAN


The underlying entry widget instance.

=back

=item words => ARRAY

A shortcut to providing C<gen_words>; a reference to an array containing all
the possible words, in no particular order, that are offered for completion.

=item use_popup => BOOL

Optional. If false, do not display a popup menu. Defaults to true.

When this is disabled, the completion logic will apply longest-prefix matching
on the set of available words, but will not otherwise display or offer any
interactive UI on the list of matches.

=item ignore_case => BOOL

Optional. If true, word matching will be performed ignoring case, by using the
C</i> regexp flag. Defaults to false. When the completion logic has selected a
word to insert, it may change the case of the text already in the buffer to

lib/Audio/Nama/Completion.pm  view on Meta::CPAN


   $entry->bind_keys(
      Tab => sub { $plugin->key_complete },
   );

   # Need to disable style-applied keypress binding so this takes effect
   $entry->set_style( '<Tab>' => "" );
}

field $_ignore_case       :param //= 0;
field $_use_popup         :param //= 1;
field $_append_after_word :param //= " ";

field $_gen_words :param = undef;

ADJUST :params (
   :$words = undef,
) {
   if( $words and !$_gen_words ) {
      $_gen_words = sub { return $words->@* };
   }

   $_gen_words or
      croak "Require either 'gen_words' or 'words'";
}

field $_entry :param;

field $_popup_window;

method key_complete
{
   my ( $partial ) = substr( $_entry->text, 0, $_entry->position ) =~ m/(\S*)$/;
   my $plen = length $partial or return 1;

   my $match = $_ignore_case ? qr/^\Q$partial\E/i : qr/^\Q$partial\E/;
   my @completions = grep { $_ =~ $match } $_gen_words->(
      word    => $partial,
      wordpos => $_entry->position - $plen,

lib/Audio/Nama/Completion.pm  view on Meta::CPAN

   }

   if( @completions == 1 ) {
      # No other completions, so we have a complete match
      $add .= $_append_after_word unless $add =~ m{ / $ }x;
   }

   $_entry->text_splice( $_entry->position - $plen, $plen, $add );

   return 1 if @completions < 2;
   return 1 unless $_use_popup;

   # Split matches on next letter
   my %next;
   foreach ( @completions ) {
      my $l = substr( $_, $plen, 1 );
      push @{ $next{$l} }, $_;
   }

   my @possibles = map {
      @{ $next{$_} } == 1 ? $next{$_}[0]
                          : substr( $next{$_}[0], 0, $plen + 1 ) . "..."
   } sort keys %next;

   # Popup above, unless there's no room at which point, go below
   my $popup_line = ( $_entry->window->abs_top >= @possibles ) ? -(scalar @possibles) : +1;
   my $popup = $_entry->make_popup_at_cursor(
      $popup_line, -$plen,
      scalar @possibles, max( map { textwidth($_) } @possibles ),
   );

   # TODO: Some style stuff here
   $popup->pen->chattrs({ bg => 'green', fg => 'black' });

   $popup->bind_event( expose => sub ( $win, $, $info, @ ) {
      my $rb = $info->rb;

      foreach my $line ( 0 .. $#possibles ) {
         my $str = $possibles[$line];

         $rb->goto( $line, 0 );

         $rb->text( substr( $str, 0, $plen + 1 ), PEN_UNDER );
         $rb->text( substr( $str, $plen + 1 ) ) if length $str > $plen + 1;
         $rb->erase_to( $win->cols );
      }
   } );
   $popup->bind_event( key => sub ( $win, $, $info, @ ) {
      my $redo_complete;

      my $str = $info->str;

      if( $info->type eq "text" ) {
         $_entry->text_splice( $_entry->position, 0, $str );
         $redo_complete++;
      }
      elsif( $str eq "Backspace" ) {
         $_entry->text_splice( $_entry->position - 1, 1, "" );
         $redo_complete++;
      }
      elsif( $str eq "Escape" ) {
         # OK, just dismiss
      }
      else {
         # TODO: Handle at least Enter, maybe arrows to select?
         print STDERR "TODO: Unsure how to handle key $str in popup menu\n";
      }

      $popup->hide;
      undef $_popup_window;
      $_entry->take_focus;

      $self->key_complete if $redo_complete;
      return 1;
   } );
   $popup->cursor_at( 0, $plen );
   $popup->take_focus;

   $popup->show;

   $_popup_window = $popup;

   return 1;
}

=head1 AUTHOR

Paul Evans <leonerd@leonerd.org.uk>

=cut

lib/Audio/Nama/Entry.pm  view on Meta::CPAN


      if( defined $value ) {
         $_keybindings{$str} = $value;
      }
      else {
         delete $_keybindings{$str};
      }
   }
}

=head2 make_popup_at_cursor

   $win = $entry->make_popup_at_cursor( $top_offset, $left_offset, $lines, $cols );

I<Since version 0.33.>

Creates a new popup window, as if calling L<Tickit::Window/make_popup> on the
widget's main window, but with an offset relative to the current cursor
position.

An offet of (0, 0) will position the popup window's top left corner exactly over
the cursor; this is likely not what you want.

To position the popup just below the widget, use a top offset of +1:

   $win = $entry->make_popup_at_cursor( +1, 0, $lines, $cols );

To position the popup just above the widget, use a top offset of negative the
number of lines:

   $win = $entry->make_popup_at_cursor( -$lines, 0, $lines, $cols );

=cut

method make_popup_at_cursor
{
   my ( $topoff, $leftoff, $lines, $cols ) = @_;

   $self->window or
      croak "Cannot ->make_popup_at_cursor on an Entry widget with no window";

   my $pos_x = $self->char2col( $_pos_ch ) - $_scrolloffs_co;

   return $self->window->make_popup( $topoff, $pos_x + $leftoff, $lines, $cols );
}

=head1 TEXT MODEL METHODS

These methods operate on the text input buffer directly, updating the stored
text and changing the rendered display to reflect the changes. They can be
used by a program to directly manipulate the text.

=cut

lib/Audio/Nama/Terminal.pm  view on Meta::CPAN

		text 	 => prompt(),
		on_enter => $do_command,
	);

	show_prompt();
}
sub setup_key_bindings {

	Tickit::Widget::Entry::Plugin::Completion->apply($entry, 
		gen_words => \&gen_words, 
		use_popup => 0, 
		ignore_case => 1); 

	my $backspace  = sub { 
		my $stop_pos = length prompt();
		$entry->text_delete( $entry->position - 1, 1 ) 
			unless $entry->position <= $stop_pos 
	};
	my $left = sub { 
		my $stop_pos = length prompt();
		$entry->set_position( $entry->position - 1 ) 



( run in 2.519 seconds using v1.01-cache-2.11-cpan-364913b4093 )