File Coverage

blib/lib/Term/Graille/IO.pm
Criterion Covered Total %
statement 18 71 25.3
branch 0 32 0.0
condition 0 5 0.0
subroutine 6 13 46.1
pod 7 7 100.0
total 31 128 24.2


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Term::Graille::IO
4              
5             Allows user interaction in Graille Applications (or perhaps on any
6             pterminal application). Depends on Term::ReadKey and Time::HiRes;
7             When integrated with Term::Graille::Menu allows a modal drop down menu
8             that
9              
10             =head1 SYNOPSIS
11              
12             my $io=Term::Graille::IO->new();
13             $io->addAction( # add action for key press
14             "Am", # Am is returned for up arrow
15             {note=>"up arrow:cursor up ", # For drawing a menu
16             proc=>sub{my $self.@args)=@_ ...} # the action
17             } );
18             $io->run($io,@args); # start trapping keypresses
19             ...
20             $io->stop(); # stop
21            
22             =cut
23              
24             package Term::Graille::IO;
25              
26             our $VERSION=0.09;
27              
28 1     1   821 use strict; use warnings;
  1     1   2  
  1         24  
  1         4  
  1         2  
  1         25  
29 1     1   4 use Time::HiRes ("sleep"); # allow fractional sleeps
  1         2  
  1         4  
30 1     1   71 use utf8; # allow utf characters in print
  1         2  
  1         3  
31             binmode STDOUT, ":utf8";
32 1     1   427 use Term::ReadKey; # allow reading from keyboard
  1         2007  
  1         61  
33 1     1   6 use Term::Graille qw/colour paint printAt cursorAt clearScreen border/;
  1         2  
  1         627  
34              
35             =head1 FUNCTIONS
36              
37             =cut
38              
39             =head3 Cnew(%params)>
40              
41             Creates a new IO object for user interaction.
42             Three modes are available; C, means the key presses are captured
43             and not echoed, C requires the setting of C<$io-E{menu}>,
44             using C<$io-EaddMenu($menu), and C when the key presses are
45             read normally
46              
47             =cut
48              
49             sub new{
50 0     0 1   my $class = shift; #
51 0           my $self={};
52 0           $self->{actions}={};
53 0           $self->{refreshRate}=20;
54 0           $self->{key}="";
55 0           $self->{mode}="free";# one of qw/free menu normal/
56 0           ($self->{terminalWidth},$self->{terminalHeight},$self->{terminalXPixels},$self->{terminalYPixels})=GetTerminalSize;
57 0           bless $self,$class;
58 0           return $self;
59             }
60              
61             =head3 CaddMenu($menu,$trigger)>
62              
63             Uses a topbar dropdown menu of class Term::Graille::Menu. If C<$trigger> is specified
64             that activates or deactivates the menu; if not specified the 'm' key activates the menu.
65            
66             =cut
67              
68             sub addMenu{
69 0     0 1   my ($self,$menu,$trigger)=@_;
70 0   0       $self->{menuTrigger}=$trigger//"m";
71 0           $self->{menu}=$menu;
72             }
73              
74              
75             =head3 CaddAction($menu,$key,$actionData)>
76              
77             Determines what happens when a key is pressed in C mode. Functions in the
78             users scripts have to be "fully qualified" e.g. C<&main::function()>
79              
80             $io->addAction("s",{note=>"s key saves sprite",proc=>sub{
81             my ($self,$canvas,$sprite,$cursor)=@_; # these are the objects passed as parameters
82             &main::saveSprite($sprite);
83             &main::flashCursor($sprite,$cursor);
84             &main::restoreIO();},} );
85            
86              
87             =cut
88              
89             sub addAction{
90 0     0 1   my ($self,$key, $actionData)=@_;
91 0           my %args=%$actionData;
92 0           foreach my $k (keys %args){
93 0           $self->{actions}->{$key}->{$k}=$args{$k};
94             }
95             }
96              
97             =head3 Crun($io,@objects)>
98              
99             Iniiating the capture of the key presses may trigger actions. These
100             actions may need parameters including the $io object itself, it is useful
101             to select all possible objects that may need to be passed to the anonymous
102             subroutines added by C above.
103              
104             =cut
105              
106             sub run{
107 0     0 1   my ($self,@objects)=@_;
108 0           ReadMode 'cbreak';
109 0           my $n=0; my @modifiers=();
  0            
110 0           while(1){
111 0           sleep 1/$self->{refreshRate};
112 0           $self->{key} = ReadKey(-1); # -1 means non-blocking read
113 0 0         if ($self->{key}){
114 0           my $OrdKey = ord($self->{key});
115 0 0         if ($OrdKey ==27){push @modifiers, $OrdKey;}
  0            
116             else{
117 0 0         my $pressed=chr($OrdKey).(@modifiers?"m":"");
118 0 0         $pressed="enter" if ($OrdKey==10);
119 0           printAt (20,60,"key pressed=$OrdKey $pressed ");
120 0 0         if ($self->{mode} eq "free"){
    0          
121 0 0 0       if (defined $self->{actions}->{$pressed}->{proc}){
    0          
122 0           $self->{actions}->{$pressed}->{proc}->(@objects)
123             }
124             elsif((exists $self->{menuTrigger})&&($pressed eq $self->{menuTrigger})){
125 0           $self->startMenu();
126             }
127             }
128             elsif ($self->{mode} eq "menu"){
129 0 0         if ($pressed eq"Am"){ #up arrow
    0          
    0          
    0          
    0          
    0          
130             $self->{menu}->upArrow()
131 0           }
132             elsif ($pressed eq"Bm"){ #down arrow
133             $self->{menu}->downArrow()
134 0           }
135             elsif ($pressed eq"Cm"){ #left arrow
136             $self->{menu}->leftArrow()
137 0           }
138             elsif ($pressed eq"Dm"){ #right arrow
139             $self->{menu}->rightArrow()
140 0           }
141             elsif ($pressed eq"enter"){ #enter key
142             $self->{menu}->openItem()
143 0           }
144             elsif ($pressed eq $self->{menuTrigger}){ #right arrow
145             $self->{menu}->closeMenu()
146 0           }
147             }
148             }
149             }
150             else {
151 0           @modifiers=();
152             }
153 0 0         $self->{actions}->{update}->(@objects) if exists $self->{actions}->{update};
154 0           $n++;
155             }
156 0           ReadMode 'normal';
157             }
158              
159              
160             =head3 CstartMenu()>
161              
162             Starts a menu as described in Term::Graille::Menu. The $io object enters a "menu" mode
163             when Arrow, Enter and the Trigger key (see above) are passed to the Menu object
164            
165             =cut
166              
167             sub startMenu{
168 0     0 1   my $self=shift;
169 0 0         if (exists $self->{menu}){
170 0           $self->{mode}="menu";
171 0           $self->{menu}->drawMenu();
172             }
173             }
174              
175             =head3 CstopMenu()>
176              
177             Stops menu and returns to C mode
178            
179             =cut
180              
181             sub stopMenu{
182 0     0 1   my $self=shift;
183 0           $self->{mode}="free";
184             }
185              
186             =head3 Cstop()>
187              
188             stops capturing key presses and enters normal mode. Useful for exeample, when the
189             user needs to enter data
190            
191             =cut
192              
193              
194             sub stop{
195 0     0 1   my $self=shift;
196 0           ReadMode 'normal';
197             }