2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2019 Kevin Lamonte
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
31 import java
.util
.List
;
33 import jexer
.bits
.CellAttributes
;
34 import jexer
.bits
.GraphicsChars
;
35 import jexer
.event
.TKeypressEvent
;
36 import jexer
.event
.TMouseEvent
;
37 import static jexer
.TKeypress
.*;
40 * TComboBox implements a combobox containing a drop-down list and edit
41 * field. Alt-Down can be used to show the drop-down.
43 public class TComboBox
extends TWidget
{
45 // ------------------------------------------------------------------------
46 // Variables --------------------------------------------------------------
47 // ------------------------------------------------------------------------
50 * The list of items in the drop-down.
55 * The edit field containing the value to return.
60 * The action to perform when the user selects an item (clicks or enter).
62 private TAction updateAction
= null;
65 * If true, the field cannot be updated to a value not on the list.
67 private boolean limitToListValue
= true;
70 * The maximum height of the values drop-down when it is visible.
72 private int maxValuesHeight
= 3;
74 // ------------------------------------------------------------------------
75 // Constructors -----------------------------------------------------------
76 // ------------------------------------------------------------------------
81 * @param parent parent widget
82 * @param x column relative to parent
83 * @param y row relative to parent
84 * @param width visible combobox width, including the down-arrow
85 * @param values the possible values for the box, shown in the drop-down
86 * @param valuesIndex the initial index in values, or -1 for no default
88 * @param maxValuesHeight the maximum height of the values drop-down when
90 * @param updateAction action to call when a new value is selected from
91 * the list or enter is pressed in the edit field
93 public TComboBox(final TWidget parent
, final int x
, final int y
,
94 final int width
, final List
<String
> values
, final int valuesIndex
,
95 final int maxValuesHeight
, final TAction updateAction
) {
97 // Set parent and window
98 super(parent
, x
, y
, width
, 1);
100 assert (values
!= null);
102 this.updateAction
= updateAction
;
103 this.maxValuesHeight
= maxValuesHeight
;
105 field
= addField(0, 0, width
- 3, false, "", updateAction
, null);
106 if ((valuesIndex
>= 0) && (valuesIndex
< values
.size())) {
107 field
.setText(values
.get(valuesIndex
));
110 list
= addList(values
, 0, 1, width
,
111 Math
.max(3, Math
.min(values
.size() + 1, maxValuesHeight
)),
114 field
.setText(list
.getSelected());
115 list
.setEnabled(false);
116 list
.setVisible(false);
117 TComboBox
.super.setHeight(1);
118 if (TComboBox
.this.limitToListValue
== false) {
119 TComboBox
.this.activate(field
);
121 if (updateAction
!= null) {
127 if (valuesIndex
>= 0) {
128 list
.setSelectedIndex(valuesIndex
);
131 list
.setEnabled(false);
132 list
.setVisible(false);
134 if (limitToListValue
) {
135 field
.setEnabled(false);
141 // ------------------------------------------------------------------------
142 // Event handlers ---------------------------------------------------------
143 // ------------------------------------------------------------------------
146 * Returns true if the mouse is currently on the down arrow.
148 * @param mouse mouse event
149 * @return true if the mouse is currently on the down arrow
151 private boolean mouseOnArrow(final TMouseEvent mouse
) {
152 if ((mouse
.getY() == 0)
153 && (mouse
.getX() >= getWidth() - 3)
154 && (mouse
.getX() <= getWidth() - 1)
162 * Handle mouse down clicks.
164 * @param mouse mouse button down event
167 public void onMouseDown(final TMouseEvent mouse
) {
168 if ((mouseOnArrow(mouse
)) && (mouse
.isMouse1())) {
169 // Make the list visible or not.
170 if (list
.isActive()) {
177 // Pass to parent for the things we don't care about.
178 super.onMouseDown(mouse
);
184 * @param keypress keystroke event
187 public void onKeypress(final TKeypressEvent keypress
) {
188 if (keypress
.equals(kbEsc
)) {
189 if (list
.isActive()) {
195 if (keypress
.equals(kbAltDown
)) {
200 if (keypress
.equals(kbTab
)
201 || (keypress
.equals(kbShiftTab
))
202 || (keypress
.equals(kbBackTab
))
204 if (list
.isActive()) {
210 // Pass to parent for the things we don't care about.
211 super.onKeypress(keypress
);
214 // ------------------------------------------------------------------------
215 // TWidget ----------------------------------------------------------------
216 // ------------------------------------------------------------------------
219 * Override TWidget's width: we need to set child widget widths.
221 * @param width new widget width
224 public void setWidth(final int width
) {
226 field
.setWidth(width
- 3);
229 list
.setWidth(width
);
231 super.setWidth(width
);
235 * Override TWidget's height: we can only set height at construction
238 * @param height new widget height (ignored)
241 public void setHeight(final int height
) {
246 * Draw the combobox down arrow.
250 CellAttributes comboBoxColor
;
252 if (!isAbsoluteActive()) {
253 // We lost focus, turn off the list.
254 if (list
.isActive()) {
259 if (isAbsoluteActive()) {
260 comboBoxColor
= getTheme().getColor("tcombobox.active");
262 comboBoxColor
= getTheme().getColor("tcombobox.inactive");
265 putCharXY(getWidth() - 3, 0, GraphicsChars
.DOWNARROWLEFT
,
267 putCharXY(getWidth() - 2, 0, GraphicsChars
.DOWNARROW
,
269 putCharXY(getWidth() - 1, 0, GraphicsChars
.DOWNARROWRIGHT
,
273 // ------------------------------------------------------------------------
274 // TComboBox --------------------------------------------------------------
275 // ------------------------------------------------------------------------
278 * Hide the drop-down list.
280 public void hideList() {
281 list
.setEnabled(false);
282 list
.setVisible(false);
284 if (limitToListValue
== false) {
290 * Show the drop-down list.
292 public void showList() {
293 list
.setEnabled(true);
294 list
.setVisible(true);
295 super.setHeight(list
.getHeight() + 1);
300 * Get combobox text value.
302 * @return text in the edit field
304 public String
getText() {
305 return field
.getText();
309 * Set combobox text value.
311 * @param text the new text in the edit field
313 public void setText(final String text
) {
318 * Set combobox text value.
320 * @param text the new text in the edit field
321 * @param caseSensitive if true, perform a case-sensitive search for the
324 public void setText(final String text
, final boolean caseSensitive
) {
326 for (int i
= 0; i
< list
.getMaxSelectedIndex(); i
++) {
327 if (caseSensitive
== true) {
328 if (list
.getListItem(i
).equals(text
)) {
329 list
.setSelectedIndex(i
);
333 if (list
.getListItem(i
).toLowerCase().equals(text
.toLowerCase())) {
334 list
.setSelectedIndex(i
);
339 list
.setSelectedIndex(-1);
343 * Set combobox text to one of the list values.
345 * @param index the index in the list
347 public void setIndex(final int index
) {
348 list
.setSelectedIndex(index
);
349 field
.setText(list
.getSelected());
353 * Get a copy of the list of strings to display.
355 * @return the list of strings
357 public final List
<String
> getList() {
358 return list
.getList();
362 * Set the new list of strings to display.
364 * @param list new list of strings
366 public final void setList(final List
<String
> list
) {
367 this.list
.setList(list
);
368 this.list
.setHeight(Math
.max(3, Math
.min(list
.size() + 1,