Merge branch 'upstream-sep2019-tcombo' into subtree
authorNiki Roo <niki@nikiroo.be>
Thu, 24 Oct 2019 17:44:01 +0000 (19:44 +0200)
committerNiki Roo <niki@nikiroo.be>
Thu, 24 Oct 2019 17:44:01 +0000 (19:44 +0200)
1  2 
TComboBox.java
TWidget.java

diff --combined TComboBox.java
index b64dbde058ad006d4a5d80fd83e9ddd4303e8ba2,1164e6c53f9699e224611f304391e22ee966471b..1164e6c53f9699e224611f304391e22ee966471b
   */
  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.*;
  
  /**
@@@ -65,6 -68,23 +68,23 @@@ public class TComboBox extends TWidget 
       * 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.
       * @param values the possible values for the box, shown in the drop-down
       * @param valuesIndex the initial index in values, or -1 for no default
       * value
-      * @param maxValuesHeight the maximum height of the values drop-down when
-      * it is visible
+      * @param valuesHeight the height of the values drop-down when it is
+      * 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
       */
      public TComboBox(final TWidget parent, final int x, final int y,
          final int width, final List<String> values, final int valuesIndex,
-         final int maxValuesHeight, final TAction updateAction) {
+         final int valuesHeight, final TAction updateAction) {
  
          // Set parent and window
          super(parent, x, y, width, 1);
          assert (values != null);
  
          this.updateAction = updateAction;
-         this.maxValuesHeight = maxValuesHeight;
+         this.values = values;
+         this.valuesHeight = valuesHeight;
  
-         field = addField(0, 0, width - 3, false, "", updateAction, null);
-         if ((valuesIndex >= 0) && (valuesIndex < values.size())) {
-             field.setText(values.get(valuesIndex));
-         }
-         list = addList(values, 0, 1, width,
-             Math.max(3, Math.min(values.size() + 1, maxValuesHeight)),
-             new TAction() {
-                 public void DO() {
-                     field.setText(list.getSelected());
-                     list.setEnabled(false);
-                     list.setVisible(false);
-                     TComboBox.super.setHeight(1);
-                     if (TComboBox.this.limitToListValue == false) {
-                         TComboBox.this.activate(field);
-                     }
-                     if (updateAction != null) {
-                         updateAction.DO(TComboBox.this);
-                     }
-                 }
-             }
-         );
+         field = new TField(this, 0, 0, Math.max(0, width - 3), false, "",
+             updateAction, null);
          if (valuesIndex >= 0) {
-             list.setSelectedIndex(valuesIndex);
+             field.setText(values.get(valuesIndex));
          }
  
-         list.setEnabled(false);
-         list.setVisible(false);
-         super.setHeight(1);
+         setHeight(1);
          if (limitToListValue) {
              field.setEnabled(false);
          } else {
      public void onMouseDown(final TMouseEvent mouse) {
          if ((mouseOnArrow(mouse)) && (mouse.isMouse1())) {
              // Make the list visible or not.
-             if (list.isActive()) {
-                 hideList();
+             if (list != null) {
+                 hideDropdown();
              } else {
-                 showList();
+                 displayDropdown();
              }
          }
  
      @Override
      public void onKeypress(final TKeypressEvent keypress) {
          if (keypress.equals(kbEsc)) {
-             if (list.isActive()) {
-                 hideList();
+             if (list != null) {
+                 hideDropdown();
                  return;
              }
          }
  
          if (keypress.equals(kbAltDown)) {
-             showList();
+             displayDropdown();
              return;
          }
  
              || (keypress.equals(kbShiftTab))
              || (keypress.equals(kbBackTab))
          ) {
-             if (list.isActive()) {
-                 hideList();
+             if (list != null) {
+                 hideDropdown();
                  return;
              }
          }
  
          if (!isAbsoluteActive()) {
              // We lost focus, turn off the list.
-             if (list.isActive()) {
-                 hideList();
-             }
+             hideDropdown();
          }
  
          if (isAbsoluteActive()) {
       * list item
       */
      public void setText(final String text, final boolean caseSensitive) {
-         field.setText(text);
-         for (int i = 0; i < list.getMaxSelectedIndex(); i++) {
-             if (caseSensitive == true) {
-                 if (list.getListItem(i).equals(text)) {
-                     list.setSelectedIndex(i);
-                     return;
-                 }
-             } else {
-                 if (list.getListItem(i).toLowerCase().equals(text.toLowerCase())) {
-                     list.setSelectedIndex(i);
-                     return;
-                 }
-             }
+         this.caseSensitive = caseSensitive;
+       field.setText(text);
+         if (list != null) {
+               displayDropdown();
          }
-         list.setSelectedIndex(-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;
+       }
+     }
  }
diff --combined TWidget.java
index e94fed25077c68ca763b8dc90e76b98d36898469,eb06175092d9a8dcd53c20a4d8e75b856d200967..eb06175092d9a8dcd53c20a4d8e75b856d200967
@@@ -184,14 -184,7 +184,7 @@@ public abstract class TWidget implement
       * @param enabled if true assume enabled
       */
      protected TWidget(final TWidget parent, final boolean enabled) {
-         this.enabled = enabled;
-         this.parent = parent;
-         children = new ArrayList<TWidget>();
-         if (parent != null) {
-             this.window = parent.window;
-             parent.addChild(this);
-         }
+         this(parent, enabled, 0, 0, 0, 0);
      }
  
      /**
       * @return difference between this.tabOrder and that.tabOrder, or
       * difference between this.z and that.z, or String.compareTo(text)
       */
+     @Override
      public final int compareTo(final TWidget that) {
          if ((this instanceof TWindow)
              && (that instanceof TWindow)
              children.get(i).tabOrder = i;
          }
      }
+     
+     /**
+      * Remove and {@link TWidget#close()} the given child from this {@link TWidget}.
+      * <p>
+      * Will also reorder the tab values of the remaining children.
+      * 
+      * @param child the child to remove
+      * 
+      * @return TRUE if the child was removed, FALSE if it was not found
+      */
+     public boolean removeChild(final TWidget child) {
+         if (children.remove(child)) {
+                 child.close();
+                 child.parent = null;
+                 child.window = null;
+                 
+                 resetTabOrder();
+                 
+                 return true;
+         }
+         
+         return false;
+     }
  
      /**
       * Switch the active child.