1# combo styles
2package
3    cs;
4use constant Simple       =>  0;
5use constant DropDown     =>  1;
6use constant DropDownList =>  2;
7
8use strict;
9use warnings;
10
11package Prima::ComboBox;
12
13use vars qw(@ISA %listProps %editProps %listDynas $capture_mode);
14use Prima qw( InputLine Lists Utils StdBitmap);
15@ISA = qw(Prima::Widget);
16
17use constant DefButtonX => 17;
18
19# these are properties, methods and dynas of encapsulated widgets, edit and list
20
21%editProps = map { $_ => 1 } qw(
22	alignment      autoScroll  text         select_all
23	charOffset     maxLen      insertMode   firstChar
24	selection      selStart    selEnd       writeOnly
25	copy           cut         delete       paste
26	wordDelimiters readOnly    passwordChar focus
27);
28
29%listProps = map { $_ => 1 } qw(
30	focusedItem hScroll
31	integralHeight items       itemHeight
32	topItem        vScroll     gridColor
33	multiColumn    offset      autoHScroll
34	autoVScroll
35);
36
37%listDynas = map { $_ => 1} qw(onDrawItem onSelectItem);
38
39for ( keys %editProps) {
40	eval <<GENPROC;
41sub $_ { return shift-> {edit}-> $_(\@_); }
42sub Prima::ComboBox::DummyEdit::$_ {}
43GENPROC
44}
45
46for (keys %listProps) {
47	eval <<GENPROC;
48sub $_ { return shift-> {list}-> $_(\@_); }
49sub Prima::ComboBox::DummyList::$_ {}
50GENPROC
51}
52
53$capture_mode = (Prima::Application-> get_system_info-> {apc} == apc::Unix);
54
55sub profile_default
56{
57	my $f = $_[ 0]-> get_default_font;
58	return {
59		%{Prima::InputLine-> profile_default},
60		%{Prima::ListBox-> profile_default},
61		%{$_[ 0]-> SUPER::profile_default},
62		style          => cs::Simple,
63		caseSensitive  => 0,
64		autoHeight     => 1,
65
66		autoHScroll    => 0,
67		autoVScroll    => 1,
68		listVisible    => 0,
69		editHeight     => $f-> {height} + 2,
70		listHeight     => $::application-> uiScaling * 100,
71		ownerBackColor => 1,
72		selectable     => 0,
73		literal        => 1,
74		scaleChildren  => 0,
75		autoEnableChildren => 1,
76
77		editClass      => 'Prima::InputLine',
78		listClass      => 'Prima::ListBox',
79		buttonClass    => 'Prima::Widget',
80		scrollBarClass => 'Prima::ScrollBar',
81		editProfile    => {},
82		listProfile    => {},
83		buttonProfile  => {},
84		hScrollBarProfile => {},
85		vScrollBarProfile => {},
86		listDelegations   => [qw(Leave SelectItem MouseUp Click KeyDown)],
87		editDelegations   => [qw(FontChanged Create Setup KeyDown KeyUp Change Leave MouseWheel)],
88		buttonDelegations => [qw(ColorChanged FontChanged MouseDown MouseClick
89			MouseUp MouseMove MouseEnter MouseLeave Paint Enable Disable)],
90	}
91}
92
93sub profile_check_in
94{
95	my ( $self, $p, $default) = @_;
96	$self-> SUPER::profile_check_in( $p, $default);
97	my $style = exists $p-> {style} ? $p-> {style} : $default-> {style};
98	$p-> {autoHeight} = 0
99		if exists $p-> {height} || exists $p-> {size} || exists $p-> {rect} || ( exists $p-> {top} && exists $p-> {bottom});
100	if ( $style != cs::Simple) {
101		$p-> { editHeight } = exists $p-> {height} ? $p-> {height} : $default-> {height}
102			unless exists $p-> { editHeight };
103	} else {
104		my $fh = exists $p-> {font}-> {height} ?
105			$p-> {font}-> {height} :
106			$default-> {font}-> {height};
107		$p-> { editHeight } = $fh + 2
108			unless exists $p-> {editHeight };
109	}
110	$p-> {autoHScroll} = 0 if exists $p-> {hScroll};
111	$p-> {autoVScroll} = 0 if exists $p-> {vScroll};
112}
113
114sub init
115{
116	my $self = shift;
117	my %profile = @_;
118	my $visible = $profile{visible};
119	$profile{visible} = 0;
120	$self-> {edit} = bless [], q\Prima::ComboBox::DummyEdit\;
121	$self-> {list} = bless [], q\Prima::ComboBox::DummyList\;
122	%profile = $self-> SUPER::init(%profile);
123	my ( $w, $h) = ( $self-> size);
124	$self-> {style}        = $profile{style};
125	$self-> {listVisible}  = $profile{style} != cs::Simple;
126	$self-> {caseSensitive}= $profile{caseSensitive};
127	$self-> {literal}      = $profile{literal};
128	my $eh = $self-> {editHeight } = $profile{editHeight };
129	$self-> {listHeight}   = $profile{listHeight};
130	$self-> {autoHeight}   = $profile{autoHeight};
131
132	$self-> {edit} = $self-> insert( $profile{editClass} =>
133		name        => 'InputLine',
134		origin      => [ 0, $h - $eh],
135		size        => [ $w - (( $self-> {style} == cs::Simple) ? 0 : ( $::application-> uiScaling * DefButtonX)), $eh],
136		growMode    => gm::GrowHiX | gm::GrowLoY,
137		selectable  => 1,
138		tabStop     => 1,
139		current     => 1,
140		dndAware    => (( $self->{style} == cs::DropDown) ? 'Text' : 0 ),
141		delegations => $profile{editDelegations},
142		(map { $_   => $profile{$_}} keys %editProps),
143		%{$profile{editProfile}},
144	);
145
146	if ( $self->{autoHeight} && $self->{style} != cs::Simple) {
147		$self->check_auto_size;
148		( $w, $h) = ( $self-> size);
149		$eh = $self-> geomHeight;
150		$self->{edit}->set( height => $eh, bottom => $h - $eh );
151	}
152
153	my %lp = (%listProps, scrollBarClass => 1, hScrollBarProfile => 1, vScrollBarProfile => 1);
154	delete $lp{hScroll} if $profile{autoHScroll};
155	delete $lp{vScroll} if $profile{autoVScroll};
156	$self-> {list} = $self-> insert( $profile{listClass} =>
157		name         => 'List',
158		origin       => [ 0, 0],
159		selectable   => $capture_mode ? ($self->{style} == cs::Simple) : 1,
160		width        => $w,
161		height       => ( $self-> {style} == cs::Simple) ? ( $h - $eh) : $self-> {listHeight},
162		growMode     => gm::Client,
163		tabStop      => 0,
164		multiSelect  => 0,
165		clipOwner    => $self-> {style} == cs::Simple,
166		visible      => $self-> {style} == cs::Simple,
167		delegations  => $profile{listDelegations},
168		(map { $_    => $profile{$_}} grep { exists $profile{$_} ? 1 : 0} keys %listDynas),
169		(map { $_    => $profile{$_}} keys %lp),
170		%{$profile{listProfile}},
171	);
172
173	$self-> {button} = $self-> insert( $profile{buttonClass} =>
174		ownerBackColor => 1,
175		name           => 'Button',
176		origin         => [ $w - $::application-> uiScaling * DefButtonX, $h - $eh],
177		size           => [ $::application-> uiScaling * DefButtonX, $eh],
178		visible        => $self-> {style} != cs::Simple,
179		growMode       => gm::GrowLoX | gm::GrowLoY,
180		tabStop        => 0,
181		ownerFont      => 0,
182		selectable     => 0,
183		delegations    => $profile{buttonDelegations},
184		%{$profile{buttonProfile}},
185	);
186
187	$self-> visible( $visible);
188	return %profile;
189}
190
191sub check_auto_size
192{
193	my $self = $_[0];
194	$self-> geomHeight( $self-> {edit}-> default_geom_height )
195		if $self-> {autoHeight} && $self->{style} != cs::Simple && $self->{edit}->can('default_geom_height');
196}
197
198sub on_create
199{
200	my $self = $_[0];
201	$self-> InputLine_Change( $self-> {edit})
202		if $self-> {style} == cs::DropDownList;
203}
204
205sub on_size { $_[0]-> listVisible(0); }
206sub on_move { $_[0]-> listVisible(0); }
207sub on_hide { $_[0]-> listVisible(0); }
208
209sub List_Leave
210{
211	$_[0]-> listVisible( 0) if $_[0]-> {style} != cs::Simple;
212}
213
214sub List_SelectItem
215{
216	return if defined $_[1]-> {interaction};
217	$_[0]-> {edit}-> {interaction} = 1;
218	$_[0]-> {edit}-> text($_[1]-> get_item_text( $_[1]-> focusedItem));
219	$_[0]-> {edit}-> {interaction} = undef;
220	$_[0]-> notify( q(Change)) if $_[0]-> {style} == cs::Simple;
221}
222
223sub List_MouseUp
224{
225	return unless $_[2] == mb::Left || $_[1]-> capture;
226	$_[0]-> listVisible(0) if $_[0]-> {style} != cs::Simple;
227	$_[0]-> notify( q(Change)) if $_[0]-> {style} != cs::Simple;
228}
229
230sub List_Click
231{
232	my ( $self, $list) = @_;
233	$self-> {edit}-> {interaction} = 1;
234	$self-> {edit}-> text( $list-> get_item_text( $list-> focusedItem));
235	$self-> {edit}-> {interaction} = undef;
236	$self-> listVisible(0);
237	$self-> notify( q(Change));
238}
239
240sub List_KeyDown
241{
242	my ( $self, $list, $code, $key, $mod) = @_;
243	if ( $key == kb::Esc) {
244		$self-> listVisible(0);
245		$list-> clear_event;
246	} elsif ( $key == kb::Enter) {
247		$list-> notify(q(Click));
248		$list-> clear_event;
249	}
250}
251
252sub Button_ColorChanged
253{
254	my $self = shift;
255	if ( $self-> {style} != cs::Simple) {
256		$self-> {list}-> color( $self-> {button}-> color);
257		$self-> {list}-> backColor( $self-> {button}-> backColor);
258	}
259}
260
261sub Button_FontChanged
262{
263	my $self = shift;
264	if ( $self-> {style} != cs::Simple) {
265		my $f = $self-> {button}-> font;
266		$self-> {list}-> font($f);
267	}
268}
269
270sub Button_MouseDown
271{
272	$_[0]-> listVisible( !$_[0]-> {list}-> visible);
273	$_[1]-> clear_event;
274	return if !$_[0]-> {list}-> visible;
275	$_[1]-> capture(1);
276}
277
278sub Button_MouseClick
279{
280	return unless $_[-1];
281	$_[0]-> listVisible( !$_[0]-> {list}-> visible);
282	$_[1]-> clear_event;
283	return if !$_[0]-> {list}-> visible;
284	$_[1]-> capture(1);
285}
286
287
288sub Button_MouseMove
289{
290	$_[1]-> clear_event;
291	if ($_[1]-> capture) {
292		my ($x,$y,$W,$H) = ($_[3],$_[4],$_[1]-> size);
293		return unless ($x < 0) || ($y < 0) || ($x >= $W) || ($y >= $H);
294		$_[1]-> capture(0);
295		$_[0]-> {list}-> mouse_down( mb::Left, 0, 5, $_[0]-> {list}-> height - 5, 1)
296			if ($_[0]-> {list}-> visible);
297	}
298}
299
300sub Button_MouseEnter
301{
302	my ( $self, $button ) = @_;
303	if ( !$button->capture && $self->enabled) {
304		$button->{prelight} = 1;
305		$button->repaint;
306	}
307}
308
309sub Button_MouseLeave
310{
311	my ( $self, $button ) = @_;
312	if ( !$button->capture && $button->{prelight}) {
313		delete $button->{prelight};
314		$button->repaint;
315	}
316
317}
318
319sub Button_MouseUp { $_[1]-> capture(0); }
320
321sub fix_triangle
322{
323	my @spot = map { int($_ + .5) } @_;
324	my $dx = $spot[4] - $spot[0];
325	my $dy = $spot[1] - $spot[3];
326	if ($dx % 2) {
327		$spot[2] = $spot[0] + ($dx - 1) / 2;
328		$spot[4]--;
329		$dx--;
330	}
331	if ( $dx == 2 ) {
332		$spot[4]++;
333		$spot[0]--;
334		$dx += 2;
335	}
336	$spot[3]-- if $dy < $dx / 2;
337	return @spot;
338}
339
340sub Button_Paint
341{
342	my ( $owner, $self, $canvas) = @_;
343	my ( $w, $h)   = $canvas-> size;
344	my $ena    = $self-> enabled;
345	my @clr    = $ena ?
346		( $self-> color, $self-> backColor) :
347		( $self-> disabledColor, $self-> disabledBackColor);
348	$clr[1] = $self->prelight_color($clr[1]) if $self->{prelight};
349	my $lv = $owner-> listVisible;
350	my ( $rc, $lc) = ( $self-> light3DColor, $self-> dark3DColor);
351	( $rc, $lc) = ( $lc, $rc) if $lv;
352	$self-> rect_bevel(
353		$canvas, 0, 0, $w-1, $h-1,
354		fill => $self-> new_gradient(
355			palette  => [ $self-> dark3DColor, $clr[1], $self-> light3DColor ],
356			spline   => [0,0.5,1,0.5],
357			vertical => 0,
358		),
359		width => 2
360	);
361	my @triangle = fix_triangle( 4, $h * 0.6, $w/2, $h * 0.4, $w - 4, $h * 0.6 );
362	if ( $ena) {
363		$canvas-> color( $rc);
364		$canvas-> translate(1,-1);
365		$canvas-> fillpoly(\@triangle);
366		$canvas-> translate(0,0);
367	}
368	$canvas-> color( $clr[0]);
369	$canvas-> fillpoly(\@triangle);
370}
371
372sub Button_Enable  { $_[1]-> repaint }
373sub Button_Disable { $_[1]-> repaint }
374
375sub InputLine_Leave
376{
377	$_[0]-> listVisible( 0) if $capture_mode and $_[0]-> {style} != cs::Simple;
378}
379
380sub InputLine_FontChanged
381{
382	$_[0]-> editHeight ( $_[1]-> default_geom_height );
383	$_[0]-> check_auto_size;
384}
385
386sub InputLine_Create
387{
388	$_[1]-> {incline} = '';
389}
390
391sub InputLine_KeyDown
392{
393	my ( $self, $edit, $code, $key, $mod, $repeat) = @_;
394
395	if ( $self-> listVisible) {
396		$self-> {list}-> notify(q(KeyDown), $code, $key, $mod, $repeat);
397		$edit-> clear_event;
398		return;
399	}
400
401	return if $mod & km::DeadKey;
402	if (
403		( $key & 0xFF00) &&
404		( $key != kb::NoKey) &&
405		( $key != kb::Space) &&
406		( $key != kb::Backspace)
407	) {
408		return if $key == kb::Tab || $key == kb::BackTab || $key == kb::NoKey;
409		$edit-> {incline} = '';
410		$self-> listVisible(1), $edit-> clear_event
411			if $key == kb::Down && $_[0]-> {style} != cs::Simple;
412		$_[0]-> notify( q(Change)), $edit-> clear_event
413			if $key == kb::Enter && $_[0]-> {style} == cs::DropDownList;
414		return;
415	} else {
416		return unless $code;
417		return unless $_[0]-> {literal};
418		return if $_[0]-> {style} != cs::DropDownList;
419		return if $mod & ( km::Alt|km::Ctrl);
420		$edit-> {keyDown} = 1;
421		if ( $key == kb::Backspace) {
422			chop $edit-> {incline};
423		} else {
424			$code = chr ( $code);
425			$code = uc $code unless $self-> caseSensitive;
426			$edit-> {incline} .= $code;
427		}
428		my ($ftc,$i,$txt,$t);
429		$ftc = quotemeta $edit-> {incline};
430		for ( $i = 0; $i < $_[0]-> {list}-> count; $i++) {
431			$txt = $_[0]-> {list}-> get_item_text($i);
432			$t = $txt;
433			$t = uc $t unless $self-> caseSensitive;
434			last if $t =~ /^$ftc/;
435		}
436		if ( $i < $_[0]-> {list}-> count) {
437			$edit-> text( $txt);
438		} else {
439			chop $edit-> {incline};
440		}
441		$edit-> selection( 0, length $edit-> {incline});
442	}
443	$edit-> clear_event if $_[0]-> {style} == cs::DropDownList;
444}
445
446
447sub InputLine_KeyUp
448{
449	$_[1]-> {keyDown} = undef;
450}
451
452sub InputLine_Setup
453{
454	my $self = shift;
455	$self-> InputLine_Change(@_) if $self-> {style} == cs::DropDownList;
456}
457
458sub InputLine_Change
459{
460	return if defined $_[0]-> {edit}-> {interaction};
461	return unless $_[0]-> {literal};
462
463	$_[0]-> notify(q(Change)) if $_[0]-> {style} != cs::DropDownList;
464
465	$_[0]-> {list}-> {interaction} = 1;
466	my ( $self, $style, $list, $edit) = ($_[0], $_[0]-> {style}, $_[0]-> {list}, $_[1]);
467	my $i;
468	my $found = 0;
469	my $cap = $edit-> text;
470	$cap = uc $cap unless $self-> caseSensitive;
471	my @matchArray;
472	my @texts;
473	my $maxMatch = 0;
474	my $matchId = -1;
475	# filling list
476	for ( $i = 0; $i < $list-> count; $i++) {
477		my $t = $list-> get_item_text($i);
478		$t = uc $t unless $self-> caseSensitive;
479		push ( @texts, $t);
480	}
481	# trying to find exact match
482	for ( $i = 0; $i < scalar @texts; $i++) {
483		if ( $texts[$i] eq $cap) {
484			$matchId = $i;
485			last;
486		}
487	}
488	if (
489		( $style == cs::DropDown) &&
490		( $matchId < 0) &&
491		defined $edit-> {keyDown}
492	) {
493		my ($t,$txt);
494		for ( $i = 0; $i < scalar @texts; $i++) {
495			$txt = $t = $list-> get_item_text($i);
496			$t = uc $t unless $self-> caseSensitive;
497			last if $t =~ /^\Q$cap\E/;
498		}
499		# netscape 4 combo behavior
500		if ( $i < scalar @texts) {
501			$edit-> {interaction} = 1;
502			$edit-> text( $txt);
503			$edit-> {interaction} = undef;
504			$t =~ /^\Q$cap\E/;
505			$edit-> selection( length $cap, length $t);
506			$list-> focusedItem( $i);
507			return;
508		}
509	}
510	# or unexact match
511	if ( $matchId < 0) {
512		for ( $i = 0; $i < scalar @texts; $i++) {
513			my $l = 0;
514			$l++ while
515				$l < length($texts[$i]) && $l < length($cap)
516				&& substr( $texts[$i], $l, 1) eq substr( $cap, $l, 1);
517			if ( $l >= $maxMatch) {
518				@matchArray = () if $l > $maxMatch;
519				$maxMatch = $l;
520				push @matchArray, $i;
521			}
522		}
523		$matchId = $matchArray[0] if $matchId < 0;
524	}
525	$matchId = 0 unless defined $matchId;
526	$list-> focusedItem( $matchId);
527
528	if ( $style == cs::DropDownList) {
529		$edit-> {interaction} = 1;
530		$edit-> text( $list-> get_item_text( $matchId) // '');
531		$edit-> {interaction} = undef;
532	}
533	$list-> {interaction} = undef;
534}
535
536sub InputLine_MouseWheel
537{
538	my ( $self, $edit, $mod, $x, $y, $z) = @_;
539
540	my $f = $self-> {list}-> focusedItem;
541	$f += (($z > 0) ? -1 : 1);
542	return if $f < 0;
543	$self-> {list}-> focusedItem($f);
544	$self-> notify(q(Change));
545	$edit-> clear_event;
546}
547
548sub set_style
549{
550	my ( $self, $style) = @_;
551	return if $self-> {style} == $style;
552	my $decr = (( $self-> {style} == cs::Simple) || ( $style == cs::Simple)) ? 1 : 0;
553	$self-> {style} = $style;
554	if ( $style == cs::Simple) {
555		$self-> set(
556			height=> $self-> height + $self-> listHeight,
557			bottom=> $self-> bottom - $self-> listHeight,
558		);
559		$self-> {list}-> set(
560			visible    => 1,
561			origin     => [ 0, 0],
562			width      => $self-> width,
563			height     => $self-> height - $self-> editHeight ,
564			clipOwner  => 1,
565			selectable => 1,
566		);
567	} elsif ( $decr) {
568		$self-> set(
569			height   => $self-> height - $self-> listHeight,
570			bottom   => $self-> bottom + $self-> listHeight,
571		);
572		$self-> { list}-> set(
573			visible    => 0,
574			height     => $self-> {listHeight},
575			clipOwner  => 0,
576			selectable   => $capture_mode ? 0 : 1,
577		);
578		$self-> listVisible( 0);
579	}
580	$self-> {edit}-> set(
581		bottom => $self-> height - $self-> editHeight ,
582		width  => $self-> { edit}-> width + $::application-> uiScaling * DefButtonX * $decr *
583			(( $style == cs::Simple) ? 1 : -1),
584		height => $self-> editHeight ,
585		dndAware => (( $style == cs::DropDown) ? 'Text' : 0 ),
586	);
587	$self-> {button}-> set(
588		bottom => $self-> height - $self-> editHeight ,
589		height => $self-> editHeight ,
590		visible=> $style != cs::Simple,
591	);
592	if ( $style == cs::DropDownList) {
593		$self-> {edit}-> insertMode(1);
594		$self-> {edit}-> text( $self-> {edit}-> text);
595	}
596}
597
598sub set_list_visible
599{
600	my ( $self, $nlv) = @_;
601	return if ( $self-> {list}-> visible == $nlv) ||
602				( $self-> {style} == cs::Simple) ||
603				( !$self-> visible && $nlv);
604	my ( $list, $edit) = ( $self-> {list}, $self-> {edit});
605	if ( $nlv) {
606		my @gp = $edit-> client_to_screen( 0, -$list-> height);
607		$gp[1] += $edit-> height + $list-> height if $gp[1] < 0;
608		$gp[0] = $::application->width - $list->width if $gp[0] + $list->width > $::application->width;
609		$list-> origin( @gp);
610	}
611	$list-> bring_to_front if $nlv;
612	$list-> visible( $nlv);
613	$self-> {button}-> repaint;
614	if ( $capture_mode) {
615		$list-> capture( $nlv ? 1 : 0);
616		$edit-> focus;
617	} else {
618		$nlv ? $list-> focus : $edit-> focus;
619	}
620}
621
622sub set_edit_height
623{
624	my ( $self, $edit, $list, $btn, $h, $new) =
625		($_[0], $_[0]-> {edit}, $_[0]-> {list}, $_[0]-> {button}, $_[0]-> height, $_[1]);
626	if ( $self-> style != cs::Simple) {
627		$self-> height( $new);
628		$edit-> set(
629			bottom => 0,
630			height => $new
631		);
632		$btn-> set(
633			bottom => 0,
634			height => $new
635		);
636		$list-> height( $self-> {listHeight});
637	} else {
638		$edit-> set(
639			bottom => $h - $new,
640			height => $new
641		);
642		$btn-> set(
643			bottom => $h - $new,
644			height => $new
645		);
646		$list-> height( $h - $new);
647	}
648	$self-> {editHeight} = $new;
649}
650
651sub set_list_height
652{
653	my ( $self, $hei) = @_;
654	if ( $self-> style == cs::Simple) {
655		$self-> height( $self-> height + $hei - $self-> {listHeight});
656	} else {
657		$self-> {list}-> height($hei);
658	}
659	$self-> {listHeight} = $hei;
660}
661
662sub get_style       { return $_[0]-> {style}}
663sub get_list_visible{ return $_[0]-> {list} ? $_[0]-> {list}-> visible : 0}
664sub get_edit_height { return $_[0]-> {edit} ? $_[0]-> {edit}-> height : 0}
665sub get_list_height { return  $_[0]-> {list} ? $_[0]-> {list}-> height : 0}
666
667sub caseSensitive{($#_)?$_[0]-> {caseSensitive}=$_[1]:return $_[0]-> {caseSensitive};}
668sub listVisible  {($#_)?$_[0]-> set_list_visible($_[1]):return $_[0]-> get_list_visible;}
669sub style        {($#_)?$_[0]-> set_style       ($_[1]):return $_[0]-> get_style;       }
670sub editHeight   {($#_)?$_[0]-> set_edit_height($_[1]):return $_[0]-> get_edit_height;}
671sub listHeight   {($#_)?$_[0]-> set_list_height ($_[1]):return $_[0]-> get_list_height;}
672sub literal      {($#_)?$_[0]-> {literal} =      $_[1] :return $_[0]-> {literal}       }
673
6741;
675
676=pod
677
678=head1 NAME
679
680Prima::ComboBox - standard combo box widget
681
682=head1 SYNOPSIS
683
684	use Prima qw(Application ComboBox);
685
686	my $combo = Prima::ComboBox-> new( style => cs::DropDown, items => [ 1 .. 10 ]);
687	$combo-> style( cs::DropDownList );
688	print $combo-> text;
689
690	run Prima;
691
692=for podview <img src="combo.gif">
693
694=for html <p><img src="https://raw.githubusercontent.com/dk/Prima/master/pod/Prima/combo.gif">
695
696=head1 DESCRIPTION
697
698Provides a combo box widget which consists of an input line, list box of possible
699selections and eventual drop-down button. The combo box can be either in form
700with a drop-down selection list, that is shown by the command of the user,
701or in form when the selection list is always visible.
702
703The combo box is a grouping widget, and contains neither painting nor user-input
704code. All such functionality is delegated into the children widgets: input line, list
705box and button. C<Prima::ComboBox> exports a fixed list of methods and properties from
706namespaces of L<Prima::InputLine> and L<Prima::ListBox>. Since, however, it is
707possible to tweak the C<Prima::ComboBox> ( using its L<editClass> and L<listClass>
708create-only properties ) so the input line and list box would be other classes,
709it is not necessarily that all default functionality would work.
710The list of exported names is stored in package variables %listProps, %editProps
711and %listDynas. These also described in L<Exported names> section.
712
713The module defines C<cs::> package for the constants used by L<style> property.
714
715=head1 API
716
717=head2 Properties
718
719=over
720
721=item autoHeight BOOLEAN
722
723If 1, adjusts the height of the widget automatically when its font changes.
724Only when style is not C<cs::Simple>.
725
726Default value: 1
727
728=item buttonClass STRING
729
730Assigns a drop-down button class.
731
732Create-only property.
733
734Default value: C<Prima::Widget>
735
736=item buttonDelegations ARRAY
737
738Assigns a drop-down button list of delegated notifications.
739
740Create-only property.
741
742=item buttonProfile HASH
743
744Assigns hash of properties, passed to the drop-down button during the creation.
745
746Create-only property.
747
748=item caseSensitive BOOLEAN
749
750Selects whether the user input is case-sensitive or not, when a value
751is picked from the selection list.
752
753Default value: 0
754
755=item editClass STRING
756
757Assigns an input line class.
758
759Create-only property.
760
761Default value: C<Prima::InputLine>
762
763=item editProfile HASH
764
765Assigns hash of properties, passed to the input line during the creation.
766
767Create-only property.
768
769=item editDelegations ARRAY
770
771Assigns an input line list of delegated notifications.
772
773Create-only property.
774
775=item editHeight INTEGER
776
777Selects height of an input line.
778
779=item items ARRAY
780
781Mapped onto the list widget's C<items> property. See L<Prima::Lists> for details.
782
783=item listClass STRING
784
785Assigns a listbox class.
786
787Create-only property.
788
789Default value: C<Prima::ListBox>
790
791=item listHeight INTEGER
792
793Selects height of the listbox widget.
794
795Default value: 100
796
797=item listVisible BOOLEAN
798
799Sets whether the listbox is visible or not. Not writable
800when L<style> is C<cs::Simple>.
801
802=item listProfile HASH
803
804Assigns hash of properties, passed to the listbox during the creation.
805
806Create-only property.
807
808=item listDelegations ARRAY
809
810Assigns a selection listbox list of delegated notifications.
811
812Create-only property.
813
814=item literal BOOLEAN
815
816Selects whether the combo box user input routine assume that
817the listbox contains literal strings, that can be fetched via
818C<get_item_text> ( see L<Prima::Lists> ). As an example when
819this property is set to 0 is C<Prima::ColorComboBox> from L<Prima::ComboBox> package.
820
821Default value: 1
822
823=item style INTEGER
824
825Selected one of three styles:
826
827=over
828
829=item cs::Simple
830
831The listbox is always visible, and the drop-down button is not.
832
833=item cs::DropDown
834
835The listbox is not visible, but the drop-down button is. When the
836use presses the drop-down button, the listbox is shown; when the list-box
837is defocused, it gets hidden.
838
839=item cs::DropDownList
840
841Same as C<cs::DropDown>, but the user is restricted in the selection:
842the input line can only accept user input that is contained in listbox.
843If L<literal> set to 1, the auto completion feature is provided.
844
845=back
846
847=item text STRING
848
849Mapped onto the edit widget's C<text> property.
850
851=back
852
853=head2 Events
854
855=over
856
857=item Change
858
859Triggered with ComboBox value is changed.
860
861=item List events
862
863ComboBox forwards C<SelectItem> and C<DrawItem> events from the list box, and
864these are executed in the List's context (therefore $self there is not
865ComboBox, but the ComboBox->List). If you use C<SelectItem> you probably need
866C<Change> instead.
867
868See more in L<Prima::Lists>.
869
870=back
871
872=head2 Exported names
873
874=over
875
876=item %editProps
877
878	alignment      autoScroll  text         text
879	charOffset     maxLen      insertMode   firstChar
880	selection      selStart    selEnd       writeOnly
881	copy           cut         delete       paste
882	wordDelimiters readOnly    passwordChar focus
883	select_all
884
885=item %listProps
886
887		       focusedItem    hScroll
888	integralHeight items          itemHeight
889	topItem        vScroll        gridColor
890	multiColumn    offset
891
892=item %listDynas
893
894	onDrawItem
895	onSelectItem
896
897=back
898
899=head1 AUTHOR
900
901Dmitry Karasik, E<lt>dmitry@karasik.eu.orgE<gt>.
902
903=head1 SEE ALSO
904
905L<Prima>, L<Prima::InputLine>, L<Prima::Lists>, L<Prima::Dialog::ColorDialog>, L<Prima::Dialog::FileDialog>,
906F<examples/listbox.pl>.
907
908=cut
909