Commit | Line | Data |
---|---|---|
051e2913 KL |
1 | /* |
2 | * Jexer - Java Text User Interface | |
3 | * | |
4 | * The MIT License (MIT) | |
5 | * | |
a69ed767 | 6 | * Copyright (C) 2019 Kevin Lamonte |
051e2913 KL |
7 | * |
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: | |
14 | * | |
15 | * The above copyright notice and this permission notice shall be included in | |
16 | * all copies or substantial portions of the Software. | |
17 | * | |
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. | |
25 | * | |
26 | * @author Kevin Lamonte [kevin.lamonte@gmail.com] | |
27 | * @version 1 | |
28 | */ | |
29 | package jexer; | |
30 | ||
31 | import java.util.List; | |
32 | ||
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.*; | |
38 | ||
39 | /** | |
40 | * TComboBox implements a combobox containing a drop-down list and edit | |
41 | * field. Alt-Down can be used to show the drop-down. | |
42 | */ | |
43 | public class TComboBox extends TWidget { | |
44 | ||
45 | // ------------------------------------------------------------------------ | |
46 | // Variables -------------------------------------------------------------- | |
47 | // ------------------------------------------------------------------------ | |
48 | ||
49 | /** | |
50 | * The list of items in the drop-down. | |
51 | */ | |
52 | private TList list; | |
53 | ||
54 | /** | |
55 | * The edit field containing the value to return. | |
56 | */ | |
57 | private TField field; | |
58 | ||
59 | /** | |
60 | * The action to perform when the user selects an item (clicks or enter). | |
61 | */ | |
62 | private TAction updateAction = null; | |
63 | ||
a69ed767 KL |
64 | /** |
65 | * If true, the field cannot be updated to a value not on the list. | |
66 | */ | |
67 | private boolean limitToListValue = true; | |
68 | ||
8ab60a33 KL |
69 | /** |
70 | * The maximum height of the values drop-down when it is visible. | |
71 | */ | |
72 | private int maxValuesHeight = 3; | |
73 | ||
051e2913 KL |
74 | // ------------------------------------------------------------------------ |
75 | // Constructors ----------------------------------------------------------- | |
76 | // ------------------------------------------------------------------------ | |
77 | ||
78 | /** | |
79 | * Public constructor. | |
80 | * | |
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 | |
87 | * value | |
8ab60a33 KL |
88 | * @param maxValuesHeight the maximum height of the values drop-down when |
89 | * it is visible | |
051e2913 KL |
90 | * @param updateAction action to call when a new value is selected from |
91 | * the list or enter is pressed in the edit field | |
92 | */ | |
93 | public TComboBox(final TWidget parent, final int x, final int y, | |
94 | final int width, final List<String> values, final int valuesIndex, | |
8ab60a33 | 95 | final int maxValuesHeight, final TAction updateAction) { |
051e2913 KL |
96 | |
97 | // Set parent and window | |
98 | super(parent, x, y, width, 1); | |
99 | ||
a69ed767 KL |
100 | assert (values != null); |
101 | ||
051e2913 | 102 | this.updateAction = updateAction; |
8ab60a33 | 103 | this.maxValuesHeight = maxValuesHeight; |
051e2913 | 104 | |
382bc294 | 105 | field = addField(0, 0, width - 3, false, "", updateAction, null); |
8ab60a33 | 106 | if ((valuesIndex >= 0) && (valuesIndex < values.size())) { |
051e2913 KL |
107 | field.setText(values.get(valuesIndex)); |
108 | } | |
109 | ||
8ab60a33 KL |
110 | list = addList(values, 0, 1, width, |
111 | Math.max(3, Math.min(values.size() + 1, maxValuesHeight)), | |
051e2913 KL |
112 | new TAction() { |
113 | public void DO() { | |
114 | field.setText(list.getSelected()); | |
115 | list.setEnabled(false); | |
116 | list.setVisible(false); | |
117 | TComboBox.this.setHeight(1); | |
a69ed767 KL |
118 | if (TComboBox.this.limitToListValue == false) { |
119 | TComboBox.this.activate(field); | |
120 | } | |
051e2913 KL |
121 | if (updateAction != null) { |
122 | updateAction.DO(); | |
123 | } | |
124 | } | |
125 | } | |
126 | ); | |
a69ed767 KL |
127 | if (valuesIndex >= 0) { |
128 | list.setSelectedIndex(valuesIndex); | |
129 | } | |
051e2913 KL |
130 | |
131 | list.setEnabled(false); | |
132 | list.setVisible(false); | |
133 | setHeight(1); | |
a69ed767 KL |
134 | if (limitToListValue) { |
135 | field.setEnabled(false); | |
136 | } else { | |
137 | activate(field); | |
138 | } | |
051e2913 KL |
139 | } |
140 | ||
141 | // ------------------------------------------------------------------------ | |
142 | // Event handlers --------------------------------------------------------- | |
143 | // ------------------------------------------------------------------------ | |
144 | ||
145 | /** | |
146 | * Returns true if the mouse is currently on the down arrow. | |
147 | * | |
148 | * @param mouse mouse event | |
149 | * @return true if the mouse is currently on the down arrow | |
150 | */ | |
151 | private boolean mouseOnArrow(final TMouseEvent mouse) { | |
152 | if ((mouse.getY() == 0) | |
a69ed767 KL |
153 | && (mouse.getX() >= getWidth() - 3) |
154 | && (mouse.getX() <= getWidth() - 1) | |
051e2913 KL |
155 | ) { |
156 | return true; | |
157 | } | |
158 | return false; | |
159 | } | |
160 | ||
161 | /** | |
162 | * Handle mouse down clicks. | |
163 | * | |
164 | * @param mouse mouse button down event | |
165 | */ | |
166 | @Override | |
167 | public void onMouseDown(final TMouseEvent mouse) { | |
168 | if ((mouseOnArrow(mouse)) && (mouse.isMouse1())) { | |
169 | // Make the list visible or not. | |
170 | if (list.isActive()) { | |
8ab60a33 | 171 | hideList(); |
051e2913 | 172 | } else { |
8ab60a33 | 173 | showList(); |
051e2913 KL |
174 | } |
175 | } | |
a69ed767 KL |
176 | |
177 | // Pass to parent for the things we don't care about. | |
178 | super.onMouseDown(mouse); | |
051e2913 KL |
179 | } |
180 | ||
181 | /** | |
182 | * Handle keystrokes. | |
183 | * | |
184 | * @param keypress keystroke event | |
185 | */ | |
186 | @Override | |
187 | public void onKeypress(final TKeypressEvent keypress) { | |
a69ed767 KL |
188 | if (keypress.equals(kbEsc)) { |
189 | if (list.isActive()) { | |
8ab60a33 | 190 | hideList(); |
a69ed767 KL |
191 | return; |
192 | } | |
193 | } | |
194 | ||
051e2913 | 195 | if (keypress.equals(kbAltDown)) { |
8ab60a33 | 196 | showList(); |
051e2913 KL |
197 | return; |
198 | } | |
199 | ||
200 | if (keypress.equals(kbTab) | |
201 | || (keypress.equals(kbShiftTab)) | |
202 | || (keypress.equals(kbBackTab)) | |
203 | ) { | |
204 | if (list.isActive()) { | |
8ab60a33 | 205 | hideList(); |
051e2913 KL |
206 | return; |
207 | } | |
208 | } | |
209 | ||
210 | // Pass to parent for the things we don't care about. | |
211 | super.onKeypress(keypress); | |
212 | } | |
213 | ||
214 | // ------------------------------------------------------------------------ | |
215 | // TWidget ---------------------------------------------------------------- | |
216 | // ------------------------------------------------------------------------ | |
217 | ||
218 | /** | |
219 | * Draw the combobox down arrow. | |
220 | */ | |
221 | @Override | |
222 | public void draw() { | |
223 | CellAttributes comboBoxColor; | |
224 | ||
a69ed767 KL |
225 | if (!isAbsoluteActive()) { |
226 | // We lost focus, turn off the list. | |
227 | if (list.isActive()) { | |
8ab60a33 | 228 | hideList(); |
a69ed767 KL |
229 | } |
230 | } | |
231 | ||
e23ea538 | 232 | if (isAbsoluteActive()) { |
051e2913 KL |
233 | comboBoxColor = getTheme().getColor("tcombobox.active"); |
234 | } else { | |
235 | comboBoxColor = getTheme().getColor("tcombobox.inactive"); | |
236 | } | |
237 | ||
a69ed767 KL |
238 | putCharXY(getWidth() - 3, 0, GraphicsChars.DOWNARROWLEFT, |
239 | comboBoxColor); | |
240 | putCharXY(getWidth() - 2, 0, GraphicsChars.DOWNARROW, | |
241 | comboBoxColor); | |
242 | putCharXY(getWidth() - 1, 0, GraphicsChars.DOWNARROWRIGHT, | |
051e2913 KL |
243 | comboBoxColor); |
244 | } | |
245 | ||
246 | // ------------------------------------------------------------------------ | |
247 | // TComboBox -------------------------------------------------------------- | |
248 | // ------------------------------------------------------------------------ | |
249 | ||
8ab60a33 KL |
250 | /** |
251 | * Hide the drop-down list. | |
252 | */ | |
253 | public void hideList() { | |
254 | list.setEnabled(false); | |
255 | list.setVisible(false); | |
256 | setHeight(1); | |
257 | if (limitToListValue == false) { | |
258 | activate(field); | |
259 | } | |
260 | } | |
261 | ||
262 | /** | |
263 | * Show the drop-down list. | |
264 | */ | |
265 | public void showList() { | |
266 | list.setEnabled(true); | |
267 | list.setVisible(true); | |
268 | setHeight(list.getHeight() + 1); | |
269 | activate(list); | |
270 | } | |
271 | ||
051e2913 KL |
272 | /** |
273 | * Get combobox text value. | |
274 | * | |
275 | * @return text in the edit field | |
276 | */ | |
277 | public String getText() { | |
278 | return field.getText(); | |
279 | } | |
280 | ||
281 | /** | |
282 | * Set combobox text value. | |
283 | * | |
284 | * @param text the new text in the edit field | |
285 | */ | |
286 | public void setText(final String text) { | |
af56159c KL |
287 | setText(text, true); |
288 | } | |
289 | ||
290 | /** | |
291 | * Set combobox text value. | |
292 | * | |
293 | * @param text the new text in the edit field | |
294 | * @param caseSensitive if true, perform a case-sensitive search for the | |
295 | * list item | |
296 | */ | |
297 | public void setText(final String text, final boolean caseSensitive) { | |
051e2913 KL |
298 | field.setText(text); |
299 | for (int i = 0; i < list.getMaxSelectedIndex(); i++) { | |
af56159c KL |
300 | if (caseSensitive == true) { |
301 | if (list.getListItem(i).equals(text)) { | |
302 | list.setSelectedIndex(i); | |
303 | return; | |
304 | } | |
305 | } else { | |
306 | if (list.getListItem(i).toLowerCase().equals(text.toLowerCase())) { | |
307 | list.setSelectedIndex(i); | |
308 | return; | |
309 | } | |
051e2913 KL |
310 | } |
311 | } | |
312 | list.setSelectedIndex(-1); | |
313 | } | |
314 | ||
a69ed767 KL |
315 | /** |
316 | * Set combobox text to one of the list values. | |
317 | * | |
318 | * @param index the index in the list | |
319 | */ | |
320 | public void setIndex(final int index) { | |
321 | list.setSelectedIndex(index); | |
322 | field.setText(list.getSelected()); | |
323 | } | |
324 | ||
325 | /** | |
326 | * Get a copy of the list of strings to display. | |
327 | * | |
328 | * @return the list of strings | |
329 | */ | |
330 | public final List<String> getList() { | |
331 | return list.getList(); | |
332 | } | |
333 | ||
334 | /** | |
335 | * Set the new list of strings to display. | |
336 | * | |
337 | * @param list new list of strings | |
338 | */ | |
339 | public final void setList(final List<String> list) { | |
340 | this.list.setList(list); | |
8ab60a33 KL |
341 | this.list.setHeight(Math.max(3, Math.min(list.size() + 1, |
342 | maxValuesHeight))); | |
a69ed767 KL |
343 | field.setText(""); |
344 | } | |
345 | ||
051e2913 | 346 | } |