File Coverage

blib/lib/UI/Various/Listbox.pm
Criterion Covered Total %
statement 94 97 96.9
branch 37 40 92.5
condition 3 3 100.0
subroutine 23 23 100.0
pod 9 9 100.0
total 166 172 96.5


line stmt bran cond sub pod time code
1             package UI::Various::Listbox;
2              
3             # Author, Copyright and License: see end of file
4              
5             =head1 NAME
6              
7             UI::Various::Listbox - general listbox widget of L
8              
9             =head1 SYNOPSIS
10              
11             use UI::Various;
12             my $main = UI::Various::main();
13             my $variable = 0;
14             $main->window(...
15             UI::Various::Listbox->new(height => 5,
16             selection => 2,
17             texts => \@variable),
18             ...);
19             $main->mainloop();
20              
21             =head1 ABSTRACT
22              
23             This module defines the general listbox widget of an application using
24             L.
25              
26             =head1 DESCRIPTION
27              
28             Besides the common attributes inherited from C the
29             C widget knows the following additional attributes:
30              
31             =head2 Attributes
32              
33             =over
34              
35             =cut
36              
37             #########################################################################
38              
39 8     8   72 use v5.14;
  8         19  
40 8     8   32 use strictures;
  8         11  
  8         36  
41 8     8   1054 no indirect 'fatal';
  8         11  
  8         29  
42 8     8   389 no multidimensional;
  8         23  
  8         29  
43 8     8   194 use warnings 'once';
  8         12  
  8         347  
44              
45             our $VERSION = '0.23';
46              
47 8     8   39 use UI::Various::core;
  8         9  
  8         34  
48 8     8   37 use UI::Various::widget;
  8         15  
  8         369  
49 8     8   29 BEGIN { require 'UI/Various/' . UI::Various::core::using() . '/Listbox.pm'; }
50              
51             require Exporter;
52             our @ISA = qw(UI::Various::widget);
53             our @EXPORT_OK = qw();
54              
55             #########################################################################
56              
57             =item first [ro]
58              
59             the index of the first element to be shown
60              
61             The last element shown will have the index C + C - 1, if
62             C is long enough.
63              
64             =cut
65              
66             sub first($)
67             {
68 28     28 1 710 return get('first', $_[0]);
69             }
70              
71             =item height [rw]
72              
73             the height of the listbox is the maximum number of elements shown
74              
75             Other then in other UI elements it is a mandatory parameter. Note the the
76             C<*Term> UIs use one additional line for the position information at the top
77             of the listbox.
78              
79             =cut
80              
81             sub height($;$)
82             {
83             return access('height',
84             sub{
85 11 100 100 11   70 unless (m/^\d+$/ and $_ > 0)
86             {
87 2         6 error('parameter__1_must_be_a_positive_integer',
88             'height');
89 2         11 $_ = 5;
90             }
91             },
92 28     28 1 124 @_);
93             }
94              
95             =item on_select [rw, optional]
96              
97             an optional callback called after changing the selection
98              
99             Note that the callback routine is called without parameters. If you need to
100             access the current selection, use the method C
101             current selection of listbox>>. Also note that when a user drags a
102             selection in L the callback is called for each and every change, not
103             only for the final one after releasing the mouse button.
104              
105             =cut
106              
107             sub on_select($;$)
108             {
109             return access('on_select',
110             sub{
111 2 100   2   12 unless (ref($_) eq 'CODE')
112             {
113 1         3 error('_1_attribute_must_be_a_2_reference',
114             'on_select', 'CODE');
115 1         7 return undef;
116             }
117             },
118 2     2 1 15 @_);
119             }
120              
121             =item selection [rw, recommended]
122              
123             the selection type of the listbox, a number between 0 and 2, defaults to 2:
124              
125             =over
126              
127             =item 0 - the elements are not selectable
128              
129             =item 1 - only single selection
130              
131             =item 2 - multiple selection is possible
132              
133             =back
134              
135             =cut
136              
137             sub selection($;$)
138             {
139             return access('selection',
140             sub{
141 5 100   5   20 unless (m/^[012]$/)
142             {
143 1         4 error('parameter__1_must_be_in__2__3',
144             'selection', 0, 2);
145 1         4 $_ = 2;
146             }
147             },
148 23     23 1 68 @_);
149             }
150              
151             =item texts [ro, recommended]
152              
153             the texts of the elements of the listbox as strings
154              
155             The default is an empty list.
156              
157             Note that the content of the list may only be modified with the methods
158             provided by C (C> and
159             C>). The only exception is when the
160             listbox did not yet contain any element.
161              
162             =cut
163              
164             sub texts($\@)
165             {
166             return
167             access
168             ('texts',
169             sub{
170 6 100   6   12 unless (ref($_) eq 'ARRAY')
171             {
172 1         3 error('_1_attribute_must_be_a_2_reference', 'texts', 'ARRAY');
173 1         10 return undef;
174             }
175 5         12 my ($self) = @_;
176 5 100       12 if ($self->{_initialised})
177             {
178 1         2 error('_1_may_not_be_modified_directly_after_initialisation',
179             'texts');
180 1         10 return undef;
181             }
182 4         4 my $entries = @$_;
183 4 100       8 if ($entries > 0)
184             {
185 3         5 local $_ = 0;
186 3         11 $self->{_selected} = [ (' ') x $entries ];
187 3         5 $self->{_initialised} = 1;
188 3         11 $self->{first} = 0;
189             }
190             else
191 1         3 { $self->{first} = -1; }
192             },
193 40     40 1 731 @_);
194             }
195              
196             #########################################################################
197             #
198             # internal constants and data:
199              
200 8         571 use constant ALLOWED_PARAMETERS =>
201             (UI::Various::widget::COMMON_PARAMETERS,
202 8     8   47 qw(first on_select selection texts));
  8         13  
203 8     8   45 use constant DEFAULT_ATTRIBUTES => (first => -1, selection => 2, texts => []);
  8         12  
  8         6100  
204              
205             #########################################################################
206             #########################################################################
207              
208             =back
209              
210             =head1 METHODS
211              
212             Besides the accessors (attributes) described above and by
213             L and the methods
214             inherited from L only the
215             constructor is provided by the C class itself:
216              
217             =cut
218              
219             #########################################################################
220              
221             =head2 B - constructor
222              
223             see L
224             constructor for UI elements>
225              
226             =cut
227              
228             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
229              
230             sub new($;\[@$])
231             {
232 10     10 1 4077 debug(3, __PACKAGE__, '::new');
233 10         83 local $_ = construct({ DEFAULT_ATTRIBUTES },
234             '^(?:' . join('|', ALLOWED_PARAMETERS) . ')$',
235             @_);
236 10 100       38 unless (defined $_->{height})
237             {
238 1         3 error('mandatory_parameter__1_is_missing', 'height');
239 1         9 return undef;
240             }
241 9         23 return $_;
242             }
243              
244             #########################################################################
245              
246             =head2 B - add new element
247              
248             $listbox->add($text, ...);
249              
250             =head3 example:
251              
252             $self->add('one more');
253             $self->add('one more', 'still one more');
254              
255             =head3 parameters:
256              
257             $text another text to be added to the end of the listbox
258              
259             =head3 description:
260              
261             This method adds one or more new elements at the end of the listbox.
262              
263             =cut
264              
265             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
266             sub add($@)
267             {
268 5     5 1 1985 my $self = shift;
269              
270             # sanity checks:
271 5 100       24 $self->isa(__PACKAGE__)
272             or fatal('invalid_object__1_in_call_to__2__3',
273             ref($self), __PACKAGE__, 'add');
274              
275 4         6 push @{$self->{texts}}, @_;
  4         13  
276 4         6 push @{$self->{_selected}}, (' ') x scalar(@_);
  4         12  
277             # call UI-specific implementation, if applicable:
278 4 50       21 if ($self->can('_add'))
279 0         0 { $self->_add(@_); }
280             }
281              
282             #########################################################################
283              
284             =head2 B - remove element
285              
286             $listbox->remove($index);
287              
288             =head3 example:
289              
290             $self->remove(2);
291              
292             =head3 parameters:
293              
294             $index the index of the element to be removed from the listbox
295              
296             =head3 description:
297              
298             This method removes an element from the listbox. The element to be removed
299             is identified by its index. Indices start with 0.
300              
301             =cut
302              
303             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
304             sub remove($$)
305             {
306 18     18 1 6061 my ($self, $index) = (@_);
307              
308             # sanity checks:
309 18 100       53 $self->isa(__PACKAGE__)
310             or fatal('invalid_object__1_in_call_to__2__3',
311             ref($self), __PACKAGE__, 'remove');
312 17 100       64 unless ($index =~ m/^\d+$/)
313             {
314 1         4 error('parameter__1_must_be_a_positive_integer_in_call_to__2__3',
315             'index', __PACKAGE__, 'remove');
316 1         4 return;
317             }
318 16 100       19 if ($index <= $#{$self->{texts}})
  16         34  
319             {
320 15         16 splice @{$self->{texts}}, $index, 1;
  15         19  
321 15         18 splice @{$self->{_selected}}, $index, 1;
  15         18  
322 15         32 $self->first <= $#{$self->{texts}} or
323 15 100       22 $self->{first} = 0 < @{$self->{texts}} ? 0 : -1;
  3 100       6  
324             }
325             # call UI-specific implementation, if applicable:
326 16 50       55 if ($self->can('_remove'))
327 0         0 { $self->_remove($index); }
328             }
329              
330             #########################################################################
331              
332             =head2 B - get current selection of listbox
333              
334             $selection = $listbox->selected(); # C 1>
335             @selection = $listbox->selected(); # C 2>
336              
337             =head3 description:
338              
339             This method returns the sorted indices of the currently selected element(s)
340             of the listbox. Indices start with 0. If there is nothing selected at all,
341             the method returns C for C 1> and an empty list for
342             C 2>.
343              
344             =head3 returns:
345              
346             selected element(s) (or C for C 0>)
347              
348             =cut
349              
350             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
351              
352             sub selected($)
353             {
354 10     10 1 3797 my ($self) = @_;
355 10 100       23 unless ($self->{selection})
356             {
357 1         3 error('invalid_call_to__1__2', __PACKAGE__, 'selected');
358 1         4 return undef;
359             }
360 9         13 my @selected = ();
361 9 50       31 if ($self->can('_selected'))
362             {
363 0         0 @selected = $self->_selected; # call UI-specific implementation
364             }
365             else
366             {
367 9         12 local $_ = 0;
368 9         10 foreach (0..$#{$self->texts})
  9         12  
369             {
370 74 100       107 $self->{_selected}[$_] ne ' ' and push @selected, $_;
371             }
372             }
373             return
374 9 100       17 $self->selection > 1 ? @selected :
    100          
375             0 < @selected ? $selected[0] : undef;
376             }
377              
378             1;
379              
380             #########################################################################
381             #########################################################################
382              
383             =head1 SEE ALSO
384              
385             L
386              
387             =head1 LICENSE
388              
389             Copyright (C) Thomas Dorner.
390              
391             This library is free software; you can redistribute it and/or modify it
392             under the same terms as Perl itself. See LICENSE file for more details.
393              
394             =head1 AUTHOR
395              
396             Thomas Dorner Edorner (at) cpan (dot) orgE
397              
398             =cut