*/
package jexer;
+import java.util.ArrayList;
import java.util.List;
import jexer.bits.CellAttributes;
import jexer.bits.GraphicsChars;
import jexer.event.TKeypressEvent;
import jexer.event.TMouseEvent;
+import jexer.event.TResizeEvent;
+import jexer.event.TResizeEvent.Type;
import static jexer.TKeypress.*;
/**
* If true, the field cannot be updated to a value not on the list.
*/
private boolean limitToListValue = true;
+
+ /**
+ * The height of the list of values when it is shown, or -1 to use the
+ * number of values in the list as the height.
+ */
+ private int valuesHeight = -1;
+
+ /**
+ * The values shown by the drop-down list.
+ */
+ private List<String> values = new ArrayList<String>();
+
+ /**
+ * When looking for a link between the displayed text and the list
+ * of values, do a case sensitive search.
+ */
+ private boolean caseSensitive = true;
+
+ /**
+ * The maximum height of the values drop-down when it is visible.
+ */
+ private int maxValuesHeight = 3;
// ------------------------------------------------------------------------
// Constructors -----------------------------------------------------------
* @param valuesIndex the initial index in values, or -1 for no default
* value
* @param valuesHeight the height of the values drop-down when it is
- * visible
+ * visible, or -1 to use the number of values as the height of the list
* @param updateAction action to call when a new value is selected from
* the list or enter is pressed in the edit field
*/
assert (values != null);
this.updateAction = updateAction;
+ this.values = values;
+ this.valuesHeight = valuesHeight;
- field = new TField(this, 0, 0, width - 3, false, "",
+ field = new TField(this, 0, 0, Math.max(0, width - 3), false, "",
updateAction, null);
if (valuesIndex >= 0) {
field.setText(values.get(valuesIndex));
}
- list = new TList(this, values, 0, 1, width, valuesHeight,
- new TAction() {
- public void DO() {
- field.setText(list.getSelected());
- list.setEnabled(false);
- list.setVisible(false);
- TComboBox.this.setHeight(1);
- if (TComboBox.this.limitToListValue == false) {
- TComboBox.this.activate(field);
- }
- if (updateAction != null) {
- updateAction.DO();
- }
- }
- }
- );
- if (valuesIndex >= 0) {
- list.setSelectedIndex(valuesIndex);
- }
-
- list.setEnabled(false);
- list.setVisible(false);
setHeight(1);
if (limitToListValue) {
field.setEnabled(false);
public void onMouseDown(final TMouseEvent mouse) {
if ((mouseOnArrow(mouse)) && (mouse.isMouse1())) {
// Make the list visible or not.
- if (list.isActive()) {
- list.setEnabled(false);
- list.setVisible(false);
- setHeight(1);
- if (limitToListValue == false) {
- activate(field);
- }
+ if (list != null) {
+ hideDropdown();
} else {
- list.setEnabled(true);
- list.setVisible(true);
- setHeight(list.getHeight() + 1);
- activate(list);
+ displayDropdown();
}
}
@Override
public void onKeypress(final TKeypressEvent keypress) {
if (keypress.equals(kbEsc)) {
- if (list.isActive()) {
- list.setEnabled(false);
- list.setVisible(false);
- setHeight(1);
- if (limitToListValue == false) {
- activate(field);
- }
+ if (list != null) {
+ hideDropdown();
return;
}
}
if (keypress.equals(kbAltDown)) {
- list.setEnabled(true);
- list.setVisible(true);
- setHeight(list.getHeight() + 1);
- activate(list);
+ displayDropdown();
return;
}
|| (keypress.equals(kbShiftTab))
|| (keypress.equals(kbBackTab))
) {
- if (list.isActive()) {
- list.setEnabled(false);
- list.setVisible(false);
- setHeight(1);
- if (limitToListValue == false) {
- activate(field);
- }
+ if (list != null) {
+ hideDropdown();
return;
}
}
// TWidget ----------------------------------------------------------------
// ------------------------------------------------------------------------
+ /**
+ * Override TWidget's width: we need to set child widget widths.
+ *
+ * @param width new widget width
+ */
+ @Override
+ public void setWidth(final int width) {
+ if (field != null) {
+ field.setWidth(width - 3);
+ }
+ if (list != null) {
+ list.setWidth(width);
+ }
+ super.setWidth(width);
+ }
+
+ /**
+ * Override TWidget's height: we can only set height at construction
+ * time.
+ *
+ * @param height new widget height (ignored)
+ */
+ @Override
+ public void setHeight(final int height) {
+ // Do nothing
+ }
+
/**
* Draw the combobox down arrow.
*/
if (!isAbsoluteActive()) {
// We lost focus, turn off the list.
- if (list.isActive()) {
- list.setEnabled(false);
- list.setVisible(false);
- setHeight(1);
- if (limitToListValue == false) {
- activate(field);
- }
- }
+ hideDropdown();
}
if (isAbsoluteActive()) {
// TComboBox --------------------------------------------------------------
// ------------------------------------------------------------------------
+ /**
+ * Hide the drop-down list.
+ */
+ public void hideList() {
+ list.setEnabled(false);
+ list.setVisible(false);
+ super.setHeight(1);
+ if (limitToListValue == false) {
+ activate(field);
+ }
+ }
+
+ /**
+ * Show the drop-down list.
+ */
+ public void showList() {
+ list.setEnabled(true);
+ list.setVisible(true);
+ super.setHeight(list.getHeight() + 1);
+ activate(list);
+ }
+
/**
* Get combobox text value.
*
* @param text the new text in the edit field
*/
public void setText(final String text) {
- field.setText(text);
- for (int i = 0; i < list.getMaxSelectedIndex(); i++) {
- if (list.getSelected().equals(text)) {
- list.setSelectedIndex(i);
- return;
- }
+ setText(text, true);
+ }
+
+ /**
+ * Set combobox text value.
+ *
+ * @param text the new text in the edit field
+ * @param caseSensitive if true, perform a case-sensitive search for the
+ * list item
+ */
+ public void setText(final String text, final boolean caseSensitive) {
+ this.caseSensitive = caseSensitive;
+ field.setText(text);
+ if (list != null) {
+ displayDropdown();
}
- list.setSelectedIndex(-1);
}
/**
*/
public final void setList(final List<String> list) {
this.list.setList(list);
+ this.list.setHeight(Math.max(3, Math.min(list.size() + 1,
+ maxValuesHeight)));
field.setText("");
}
+
+ /**
+ * Make sure the widget displays all its elements correctly according to
+ * the current size and content.
+ */
+ public void reflowData() {
+ // TODO: why setW/setH/reflow not enough for the scrollbars?
+ TList list = this.list;
+ if (list != null) {
+ int valuesHeight = this.valuesHeight;
+ if (valuesHeight < 0) {
+ valuesHeight = values == null ? 0 : values.size() + 1;
+ }
+
+ list.onResize(new TResizeEvent(Type.WIDGET, getWidth(),
+ valuesHeight));
+ setHeight(valuesHeight + 1);
+ }
+
+ field.onResize(new TResizeEvent(Type.WIDGET, getWidth(),
+ field.getHeight()));
+ }
+
+ @Override
+ public void onResize(TResizeEvent resize) {
+ super.onResize(resize);
+ reflowData();
+ }
+ /**
+ * Display the drop-down menu represented by {@link TComboBox#list}.
+ */
+ private void displayDropdown() {
+ if (this.list != null) {
+ hideDropdown();
+ }
+
+ int valuesHeight = this.valuesHeight;
+ if (valuesHeight < 0) {
+ valuesHeight = values == null ? 0 : values.size() + 1;
+ }
+
+ TList list = new TList(this, values, 0, 1, getWidth(), valuesHeight,
+ new TAction() {
+ @Override
+ public void DO() {
+ TList list = TComboBox.this.list;
+ if (list == null) {
+ return;
+ }
+
+ field.setText(list.getSelected());
+ hideDropdown();
+
+ if (updateAction != null) {
+ updateAction.DO();
+ }
+ }
+ }
+ );
+
+ int i = -1;
+ if (values != null) {
+ String current = field.getText();
+ for (i = 0 ; i < values.size() ; i++) {
+ String value = values.get(i);
+ if ((caseSensitive && current.equals(value))
+ || (!caseSensitive && current.equalsIgnoreCase(value))) {
+ break;
+ }
+ }
+
+ if (i >= values.size()) {
+ i = -1;
+ }
+ }
+ list.setSelectedIndex(i);
+
+ list.setEnabled(true);
+ list.setVisible(true);
+
+ this.list = list;
+
+ reflowData();
+ activate(list);
+ }
+
+ /**
+ * Hide the drop-down menu represented by {@link TComboBox#list}.
+ */
+ private void hideDropdown() {
+ TList list = this.list;
+
+ if (list != null) {
+ list.setEnabled(false);
+ list.setVisible(false);
+ removeChild(list);
+
+ setHeight(1);
+ if (limitToListValue == false) {
+ activate(field);
+ }
+
+ this.list = null;
+ }
+ }
}