resizing fixes
[fanfix.git] / src / jexer / TComboBox.java
CommitLineData
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 */
29package jexer;
30
31import java.util.List;
32
33import jexer.bits.CellAttributes;
34import jexer.bits.GraphicsChars;
35import jexer.event.TKeypressEvent;
36import jexer.event.TMouseEvent;
37import 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 */
43public 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);
d8dc8aea 117 TComboBox.super.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);
d8dc8aea 133 super.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
d8dc8aea 218 /**
8f62f06e 219 * Override TWidget's width: we need to set child widget widths.
d8dc8aea 220 *
8f62f06e 221 * @param width new widget width
d8dc8aea
KL
222 */
223 @Override
224 public void setWidth(final int width) {
8f62f06e
KL
225 field.setWidth(width - 3);
226 list.setWidth(width);
227 super.setWidth(width);
d8dc8aea
KL
228 }
229
230 /**
231 * Override TWidget's height: we can only set height at construction
232 * time.
233 *
234 * @param height new widget height (ignored)
235 */
236 @Override
237 public void setHeight(final int height) {
238 // Do nothing
239 }
240
051e2913
KL
241 /**
242 * Draw the combobox down arrow.
243 */
244 @Override
245 public void draw() {
246 CellAttributes comboBoxColor;
247
a69ed767
KL
248 if (!isAbsoluteActive()) {
249 // We lost focus, turn off the list.
250 if (list.isActive()) {
8ab60a33 251 hideList();
a69ed767
KL
252 }
253 }
254
e23ea538 255 if (isAbsoluteActive()) {
051e2913
KL
256 comboBoxColor = getTheme().getColor("tcombobox.active");
257 } else {
258 comboBoxColor = getTheme().getColor("tcombobox.inactive");
259 }
260
a69ed767
KL
261 putCharXY(getWidth() - 3, 0, GraphicsChars.DOWNARROWLEFT,
262 comboBoxColor);
263 putCharXY(getWidth() - 2, 0, GraphicsChars.DOWNARROW,
264 comboBoxColor);
265 putCharXY(getWidth() - 1, 0, GraphicsChars.DOWNARROWRIGHT,
051e2913
KL
266 comboBoxColor);
267 }
268
269 // ------------------------------------------------------------------------
270 // TComboBox --------------------------------------------------------------
271 // ------------------------------------------------------------------------
272
8ab60a33
KL
273 /**
274 * Hide the drop-down list.
275 */
276 public void hideList() {
277 list.setEnabled(false);
278 list.setVisible(false);
d8dc8aea 279 super.setHeight(1);
8ab60a33
KL
280 if (limitToListValue == false) {
281 activate(field);
282 }
283 }
284
285 /**
286 * Show the drop-down list.
287 */
288 public void showList() {
289 list.setEnabled(true);
290 list.setVisible(true);
d8dc8aea 291 super.setHeight(list.getHeight() + 1);
8ab60a33
KL
292 activate(list);
293 }
294
051e2913
KL
295 /**
296 * Get combobox text value.
297 *
298 * @return text in the edit field
299 */
300 public String getText() {
301 return field.getText();
302 }
303
304 /**
305 * Set combobox text value.
306 *
307 * @param text the new text in the edit field
308 */
309 public void setText(final String text) {
af56159c
KL
310 setText(text, true);
311 }
312
313 /**
314 * Set combobox text value.
315 *
316 * @param text the new text in the edit field
317 * @param caseSensitive if true, perform a case-sensitive search for the
318 * list item
319 */
320 public void setText(final String text, final boolean caseSensitive) {
051e2913
KL
321 field.setText(text);
322 for (int i = 0; i < list.getMaxSelectedIndex(); i++) {
af56159c
KL
323 if (caseSensitive == true) {
324 if (list.getListItem(i).equals(text)) {
325 list.setSelectedIndex(i);
326 return;
327 }
328 } else {
329 if (list.getListItem(i).toLowerCase().equals(text.toLowerCase())) {
330 list.setSelectedIndex(i);
331 return;
332 }
051e2913
KL
333 }
334 }
335 list.setSelectedIndex(-1);
336 }
337
a69ed767
KL
338 /**
339 * Set combobox text to one of the list values.
340 *
341 * @param index the index in the list
342 */
343 public void setIndex(final int index) {
344 list.setSelectedIndex(index);
345 field.setText(list.getSelected());
346 }
347
348 /**
349 * Get a copy of the list of strings to display.
350 *
351 * @return the list of strings
352 */
353 public final List<String> getList() {
354 return list.getList();
355 }
356
357 /**
358 * Set the new list of strings to display.
359 *
360 * @param list new list of strings
361 */
362 public final void setList(final List<String> list) {
363 this.list.setList(list);
8ab60a33
KL
364 this.list.setHeight(Math.max(3, Math.min(list.size() + 1,
365 maxValuesHeight)));
a69ed767
KL
366 field.setText("");
367 }
368
051e2913 369}