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 = ('1st', '2nd', '3rd');
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   80 use v5.14;
  8         21  
40 8     8   44 use strictures;
  8         13  
  8         33  
41 8     8   1099 no indirect 'fatal';
  8         15  
  8         28  
42 8     8   367 no multidimensional;
  8         13  
  8         28  
43 8     8   231 use warnings 'once';
  8         19  
  8         387  
44              
45             our $VERSION = '0.24';
46              
47 8     8   41 use UI::Various::core;
  8         18  
  8         37  
48 8     8   45 use UI::Various::widget;
  8         15  
  8         481  
49 8     8   35 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 777 return get('first', $_[0]);
69             }
70              
71             =item height [rw, fixed]
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   81 unless (m/^\d+$/ and $_ > 0)
86             {
87 2         5 error('parameter__1_must_be_a_positive_integer',
88             'height');
89 2         10 $_ = 5;
90             }
91             },
92 28     28 1 154 @_);
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   9 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 17 @_);
119             }
120              
121             =item selection [rw, fixed, 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             Note that changing the value after displaying the listbox will not work
136             without recreating its container.
137              
138             =cut
139              
140             sub selection($;$)
141             {
142             return access('selection',
143             sub{
144 5 100   5   22 unless (m/^[012]$/)
145             {
146 1         4 error('parameter__1_must_be_in__2__3',
147             'selection', 0, 2);
148 1         4 $_ = 2;
149             }
150             },
151 23     23 1 78 @_);
152             }
153              
154             =item texts [rw, fixed, recommended]
155              
156             the texts of the elements of the listbox as strings
157              
158             The default is an empty list.
159              
160             Note that the content of the list may only be modified with the methods
161             provided by C (C> and
162             C>). The only exception is when the
163             listbox did not yet contain any element.
164              
165             =cut
166              
167             sub texts($\@)
168             {
169             return
170             access
171             ('texts',
172             sub{
173 6 100   6   14 unless (ref($_) eq 'ARRAY')
174             {
175 1         3 error('_1_attribute_must_be_a_2_reference', 'texts', 'ARRAY');
176 1         11 return undef;
177             }
178 5         9 my ($self) = @_;
179 5 100       11 if ($self->{_initialised})
180             {
181 1         5 error('_1_may_not_be_modified_directly_after_initialisation',
182             'texts');
183 1         11 return undef;
184             }
185 4         6 my $entries = @$_;
186 4 100       8 if ($entries > 0)
187             {
188 3         5 local $_ = 0;
189 3         14 $self->{_selected} = [ (' ') x $entries ];
190 3         6 $self->{_initialised} = 1;
191 3         9 $self->{first} = 0;
192             }
193             else
194 1         3 { $self->{first} = -1; }
195             },
196 40     40 1 754 @_);
197             }
198              
199             #########################################################################
200             #
201             # internal constants and data:
202              
203 8         631 use constant ALLOWED_PARAMETERS =>
204             (UI::Various::widget::COMMON_PARAMETERS,
205 8     8   51 qw(first on_select selection texts));
  8         15  
206 8     8   48 use constant DEFAULT_ATTRIBUTES => (first => -1, selection => 2, texts => []);
  8         25  
  8         6473  
207              
208             #########################################################################
209             #########################################################################
210              
211             =back
212              
213             =head1 METHODS
214              
215             Besides the accessors (attributes) described above and by
216             L and the methods
217             inherited from L only the
218             constructor is provided by the C class itself:
219              
220             =cut
221              
222             #########################################################################
223              
224             =head2 B - constructor
225              
226             see L
227             constructor for UI elements>
228              
229             =cut
230              
231             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
232              
233             sub new($;\[@$])
234             {
235 10     10 1 4015 debug(3, __PACKAGE__, '::new');
236 10         84 local $_ = construct({ DEFAULT_ATTRIBUTES },
237             '^(?:' . join('|', ALLOWED_PARAMETERS) . ')$',
238             @_);
239 10 100       35 unless (defined $_->{height})
240             {
241 1         5 error('mandatory_parameter__1_is_missing', 'height');
242 1         11 return undef;
243             }
244 9         30 return $_;
245             }
246              
247             #########################################################################
248              
249             =head2 B - add new element
250              
251             $listbox->add($text, ...);
252              
253             =head3 example:
254              
255             $self->add('one more');
256             $self->add('one more', 'still one more');
257              
258             =head3 parameters:
259              
260             $text another text to be added to the end of the listbox
261              
262             =head3 description:
263              
264             This method adds one or more new elements at the end of the listbox.
265              
266             =cut
267              
268             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
269             sub add($@)
270             {
271 5     5 1 2146 my $self = shift;
272              
273             # sanity checks:
274 5 100       25 $self->isa(__PACKAGE__)
275             or fatal('invalid_object__1_in_call_to__2__3',
276             ref($self), __PACKAGE__, 'add');
277              
278 4         6 push @{$self->{texts}}, @_;
  4         22  
279 4         7 push @{$self->{_selected}}, (' ') x scalar(@_);
  4         15  
280             # call UI-specific implementation, if applicable:
281 4 50       22 if ($self->can('_add'))
282 0         0 { $self->_add(@_); }
283             }
284              
285             #########################################################################
286              
287             =head2 B - remove element
288              
289             $listbox->remove($index);
290              
291             =head3 example:
292              
293             $self->remove(2);
294              
295             =head3 parameters:
296              
297             $index the index of the element to be removed from the listbox
298              
299             =head3 description:
300              
301             This method removes an element from the listbox. The element to be removed
302             is identified by its index. Indices start with 0.
303              
304             =cut
305              
306             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
307             sub remove($$)
308             {
309 18     18 1 6725 my ($self, $index) = (@_);
310              
311             # sanity checks:
312 18 100       58 $self->isa(__PACKAGE__)
313             or fatal('invalid_object__1_in_call_to__2__3',
314             ref($self), __PACKAGE__, 'remove');
315 17 100       91 unless ($index =~ m/^\d+$/)
316             {
317 1         5 error('parameter__1_must_be_a_positive_integer_in_call_to__2__3',
318             'index', __PACKAGE__, 'remove');
319 1         5 return;
320             }
321 16 100       21 if ($index <= $#{$self->{texts}})
  16         37  
322             {
323 15         20 splice @{$self->{texts}}, $index, 1;
  15         21  
324 15         20 splice @{$self->{_selected}}, $index, 1;
  15         20  
325 15         38 $self->first <= $#{$self->{texts}} or
326 15 100       31 $self->{first} = 0 < @{$self->{texts}} ? 0 : -1;
  3 100       9  
327             }
328             # call UI-specific implementation, if applicable:
329 16 50       74 if ($self->can('_remove'))
330 0         0 { $self->_remove($index); }
331             }
332              
333             #########################################################################
334              
335             =head2 B - get current selection of listbox
336              
337             $selection = $listbox->selected(); # C 1>
338             @selection = $listbox->selected(); # C 2>
339              
340             =head3 description:
341              
342             This method returns the sorted indices of the currently selected element(s)
343             of the listbox. Indices start with 0. If there is nothing selected at all,
344             the method returns C for C 1> and an empty list for
345             C 2>.
346              
347             =head3 returns:
348              
349             selected element(s) (or C for C 0>)
350              
351             =cut
352              
353             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
354              
355             sub selected($)
356             {
357 10     10 1 4320 my ($self) = @_;
358 10 100       21 unless ($self->{selection})
359             {
360 1         7 error('invalid_call_to__1__2', __PACKAGE__, 'selected');
361 1         4 return undef;
362             }
363 9         13 my @selected = ();
364 9 50       43 if ($self->can('_selected'))
365             {
366 0         0 @selected = $self->_selected; # call UI-specific implementation
367             }
368             else
369             {
370 9         13 local $_ = 0;
371 9         13 foreach (0..$#{$self->texts})
  9         17  
372             {
373 74 100       128 $self->{_selected}[$_] ne ' ' and push @selected, $_;
374             }
375             }
376             return
377 9 100       17 $self->selection > 1 ? @selected :
    100          
378             0 < @selected ? $selected[0] : undef;
379             }
380              
381             1;
382              
383             #########################################################################
384             #########################################################################
385              
386             =head1 SEE ALSO
387              
388             L
389              
390             =head1 LICENSE
391              
392             Copyright (C) Thomas Dorner.
393              
394             This library is free software; you can redistribute it and/or modify it
395             under the same terms as Perl itself. See LICENSE file for more details.
396              
397             =head1 AUTHOR
398              
399             Thomas Dorner Edorner (at) cpan (dot) orgE
400              
401             =cut