modules/controls/src/main/java/com/sun/javafx/scene/control/skin/ComboBoxListViewSkin.java

Print this page
rev 8726 : [mq]: rt40280

@@ -24,31 +24,23 @@
  */
 
 package com.sun.javafx.scene.control.skin;
 
 import com.sun.javafx.scene.control.behavior.ComboBoxListViewBehavior;
-import com.sun.javafx.scene.input.ExtendedInputMethodRequests;
 
-import java.lang.ref.WeakReference;
 import java.util.List;
 
-import com.sun.javafx.scene.traversal.Algorithm;
-import com.sun.javafx.scene.traversal.Direction;
-import com.sun.javafx.scene.traversal.ParentTraversalEngine;
-import com.sun.javafx.scene.traversal.TraversalContext;
 import javafx.beans.InvalidationListener;
 import javafx.beans.Observable;
 import javafx.beans.WeakInvalidationListener;
 import javafx.collections.FXCollections;
 import javafx.collections.ListChangeListener;
 import javafx.collections.ObservableList;
 import javafx.collections.WeakListChangeListener;
 import javafx.css.PseudoClass;
 import javafx.event.ActionEvent;
-import javafx.event.EventHandler;
 import javafx.event.EventTarget;
-import javafx.geometry.Point2D;
 import javafx.scene.AccessibleAttribute;
 import javafx.scene.AccessibleRole;
 import javafx.scene.Node;
 import javafx.scene.Parent;
 import javafx.scene.control.ComboBox;

@@ -81,11 +73,10 @@
     private final ComboBox<T> comboBox;
     private ObservableList<T> comboBoxItems;
     
     private ListCell<T> buttonCell;
     private Callback<ListView<T>, ListCell<T>> cellFactory;
-    private TextField textField;
     
     private final ListView<T> listView;
     private ObservableList<T> listViewItems;
     
     private boolean listSelectionLock = false;

@@ -109,28 +100,10 @@
     private final InvalidationListener itemsObserver;
     
     private final WeakListChangeListener<T> weakListViewItemsListener =
             new WeakListChangeListener<T>(listViewItemsListener);
 
-    private EventHandler<KeyEvent> textFieldKeyEventHandler = event -> {
-        if (textField == null || ! getSkinnable().isEditable()) return;
-        handleKeyEvent(event, true);
-    };
-    private EventHandler<MouseEvent> textFieldMouseEventHandler = event -> {
-        ComboBoxBase<T> comboBox = getSkinnable();
-        if (event.getTarget().equals(comboBox)) return;
-        comboBox.fireEvent(event.copyFor(comboBox, comboBox));
-        event.consume();
-    };
-    private EventHandler<DragEvent> textFieldDragEventHandler = event -> {
-        ComboBoxBase<T> comboBox = getSkinnable();
-        if (event.getTarget().equals(comboBox)) return;
-        comboBox.fireEvent(event.copyFor(comboBox, comboBox));
-        event.consume();
-    };
-    
-    
     
     /***************************************************************************
      *                                                                         *
      * Constructors                                                            *
      *                                                                         *

@@ -145,20 +118,10 @@
             updateComboBoxItems();
             updateListViewItems();
         };
         this.comboBox.itemsProperty().addListener(new WeakInvalidationListener(itemsObserver));
         
-        // editable input node
-        this.textField = comboBox.isEditable() ? getEditableInputNode() : null;
-        
-        // Fix for RT-29565. Without this the textField does not have a correct
-        // pref width at startup, as it is not part of the scenegraph (and therefore
-        // has no pref width until after the first measurements have been taken).
-        if (this.textField != null) {
-            getChildren().add(textField);
-        }
-        
         // listview for popup
         this.listView = createListView();
         
         // Fix for RT-21207. Additional code related to this bug is further below.
         this.listView.setManaged(false);

@@ -168,66 +131,13 @@
         updateListViewItems();
         updateCellFactory();
         
         updateButtonCell();
 
-        // move fake focus in to the textfield if the comboBox is editable
-        comboBox.focusedProperty().addListener((ov, t, hasFocus) -> {
-            if (comboBox.isEditable()) {
-                // Fix for the regression noted in a comment in RT-29885.
-                ((FakeFocusTextField)textField).setFakeFocus(hasFocus);
-            }
-        });
-
-        comboBox.addEventFilter(KeyEvent.ANY, ke -> {
-            if (textField == null || ! comboBox.isEditable()) {
-                handleKeyEvent(ke, false);
-            } else {
-                // This prevents a stack overflow from our rebroadcasting of the
-                // event to the textfield that occurs in the final else statement
-                // of the conditions below.
-                if (ke.getTarget().equals(textField)) return;
-
-                // Fix for the regression noted in a comment in RT-29885.
-                // This forwards the event down into the TextField when
-                // the key event is actually received by the ComboBox.
-                textField.fireEvent(ke.copyFor(textField, textField));
-                ke.consume();
-            }
-        });
-
-        // RT-38978: Forward input method events to TextField if editable.
-        if (comboBox.getOnInputMethodTextChanged() == null) {
-            comboBox.setOnInputMethodTextChanged(event -> {
-                if (textField != null && comboBox.isEditable() && comboBox.getScene().getFocusOwner() == comboBox) {
-                    if (textField.getOnInputMethodTextChanged() != null) {
-                        textField.getOnInputMethodTextChanged().handle(event);
-                    }
-                }
-            });
-        }
-
-        updateEditable();
-        
         // Fix for RT-19431 (also tested via ComboBoxListViewSkinTest)
         updateValue();
 
-        // Fix for RT-36902, where focus traversal was getting stuck inside the ComboBox
-        comboBox.setImpl_traversalEngine(new ParentTraversalEngine(comboBox, new Algorithm() {
-            @Override public Node select(Node owner, Direction dir, TraversalContext context) {
-                return null;
-            }
-
-            @Override public Node selectFirst(TraversalContext context) {
-                return null;
-            }
-
-            @Override public Node selectLast(TraversalContext context) {
-                return null;
-            }
-        }));
-        
         registerChangeListener(comboBox.itemsProperty(), "ITEMS");
         registerChangeListener(comboBox.promptTextProperty(), "PROMPT_TEXT");
         registerChangeListener(comboBox.cellFactoryProperty(), "CELL_FACTORY");
         registerChangeListener(comboBox.visibleRowCountProperty(), "VISIBLE_ROW_COUNT");
         registerChangeListener(comboBox.converterProperty(), "CONVERTER");

@@ -270,65 +180,21 @@
         } else if ("EDITABLE".equals(p)) {
             updateEditable();
         }
     }
 
-    private void updateEditable() {
-        TextField newTextField = comboBox.getEditor();
-
-        if (!comboBox.isEditable()) {
-            // remove event filters
-            if (textField != null) {
-                textField.removeEventFilter(KeyEvent.ANY, textFieldKeyEventHandler);
-                textField.removeEventFilter(MouseEvent.DRAG_DETECTED, textFieldMouseEventHandler);
-                textField.removeEventFilter(DragEvent.ANY, textFieldDragEventHandler);
-
-                comboBox.setInputMethodRequests(null);
-            }
-        } else if (newTextField != null) {
-            // add event filters
-            newTextField.addEventFilter(KeyEvent.ANY, textFieldKeyEventHandler);
-
-            // Fix for RT-31093 - drag events from the textfield were not surfacing
-            // properly for the ComboBox.
-            newTextField.addEventFilter(MouseEvent.DRAG_DETECTED, textFieldMouseEventHandler);
-            newTextField.addEventFilter(DragEvent.ANY, textFieldDragEventHandler);
-
-            // RT-38978: Forward input method requests to TextField.
-            comboBox.setInputMethodRequests(new ExtendedInputMethodRequests() {
-                @Override public Point2D getTextLocation(int offset) {
-                    return newTextField.getInputMethodRequests().getTextLocation(offset);
-                }
-
-                @Override public int getLocationOffset(int x, int y) {
-                    return newTextField.getInputMethodRequests().getLocationOffset(x, y);
-                }
-
-                @Override public void cancelLatestCommittedText() {
-                    newTextField.getInputMethodRequests().cancelLatestCommittedText();
-                }
-
-                @Override public String getSelectedText() {
-                    return newTextField.getInputMethodRequests().getSelectedText();
-                }
-
-                @Override public int getInsertPositionOffset() {
-                    return ((ExtendedInputMethodRequests)newTextField.getInputMethodRequests()).getInsertPositionOffset();
-                }
-
-                @Override public String getCommittedText(int begin, int end) {
-                    return ((ExtendedInputMethodRequests)newTextField.getInputMethodRequests()).getCommittedText(begin, end);
+    @Override protected TextField getEditor() {
+        // Return null if editable is false, even if the ComboBox has an editor set.
+        // Use getSkinnable() here because this method is called from the super
+        // constructor before comboBox is initialized.
+        return getSkinnable().isEditable() ? ((ComboBox)getSkinnable()).getEditor() : null;
                 }
 
-                @Override public int getCommittedTextLength() {
-                    return ((ExtendedInputMethodRequests)newTextField.getInputMethodRequests()).getCommittedTextLength();
-                }
-            });
+    @Override protected StringConverter<T> getConverter() {
+        return ((ComboBox)getSkinnable()).getConverter();
         }
 
-        textField = newTextField;
-    }
     
     /** {@inheritDoc} */
     @Override public Node getDisplayNode() {
         Node displayNode;
         if (comboBox.isEditable()) {

@@ -431,33 +297,10 @@
      *                                                                         *
      * Private methods                                                         *
      *                                                                         *
      **************************************************************************/
 
-    private void handleKeyEvent(KeyEvent ke, boolean doConsume) {
-        // When the user hits the enter or F4 keys, we respond before
-        // ever giving the event to the TextField.
-        if (ke.getCode() == KeyCode.ENTER) {
-            setTextFromTextFieldIntoComboBoxValue();
-
-            if (doConsume) ke.consume();
-        } else if (ke.getCode() == KeyCode.F4) {
-            if (ke.getEventType() == KeyEvent.KEY_RELEASED) {
-                if (comboBox.isShowing()) comboBox.hide();
-                else comboBox.show();
-            }
-            ke.consume(); // we always do a consume here (otherwise unit tests fail)
-        } else if (ke.getCode() == KeyCode.F10 || ke.getCode() == KeyCode.ESCAPE) {
-            // RT-23275: The TextField fires F10 and ESCAPE key events
-            // up to the parent, which are then fired back at the
-            // TextField, and this ends up in an infinite loop until
-            // the stack overflows. So, here we consume these two
-            // events and stop them from going any further.
-            if (doConsume) ke.consume();
-        }
-    }
-
     private void updateValue() {
         T newValue = comboBox.getValue();
         
         SelectionModel<T> listViewSM = listView.getSelectionModel();
         

@@ -494,63 +337,16 @@
                 }
             }
         }
     }
     
-    private String initialTextFieldValue = null;
-    private TextField getEditableInputNode() {
-        if (textField != null) return textField;
-        
-        textField = comboBox.getEditor();
-        textField.focusTraversableProperty().bindBidirectional(comboBox.focusTraversableProperty());
-        textField.promptTextProperty().bind(comboBox.promptTextProperty());
-        textField.tooltipProperty().bind(comboBox.tooltipProperty());
-
-        // Fix for RT-21406: ComboBox do not show initial text value
-        initialTextFieldValue = textField.getText();
-        // End of fix (see updateDisplayNode below for the related code)
-
-        textField.focusedProperty().addListener((ov, t, hasFocus) -> {
-            if (! comboBox.isEditable()) return;
-
-            // Fix for RT-29885
-            comboBox.getProperties().put("FOCUSED", hasFocus);
-            // --- end of RT-29885
-
-            // RT-21454 starts here
-            if (! hasFocus) {
-                setTextFromTextFieldIntoComboBoxValue();
-                pseudoClassStateChanged(CONTAINS_FOCUS_PSEUDOCLASS_STATE, false);
-            } else {
-                pseudoClassStateChanged(CONTAINS_FOCUS_PSEUDOCLASS_STATE, true);
-            }
-            // --- end of RT-21454
-        });
 
-        return textField;
-    }
-    
-    private void updateDisplayNode() {
-        StringConverter<T> c = comboBox.getConverter();
-        if (c == null) return;
-              
-        T value = comboBox.getValue();
-        if (comboBox.isEditable()) {
-            if (initialTextFieldValue != null && ! initialTextFieldValue.isEmpty()) {
-                // Remainder of fix for RT-21406: ComboBox do not show initial text value
-                textField.setText(initialTextFieldValue);
-                initialTextFieldValue = null;
-                // end of fix
-            } else {
-                String stringValue = c.toString(value);
-                if (value == null || stringValue == null) {
-                    textField.setText("");
-                } else if (! stringValue.equals(textField.getText())) {
-                    textField.setText(stringValue);
-                }
-            }
+    @Override protected void updateDisplayNode() {
+        if (getEditor() != null) {
+            super.updateDisplayNode();
         } else {
+            T value = comboBox.getValue();
             int index = getIndexOfComboBoxValueInItemsList();
             if (index > -1) {
                 buttonCell.setItem(null);
                 buttonCell.updateIndex(index);
             } else {

@@ -594,30 +390,10 @@
             cell.setGraphic(null);
             return s == null || s.isEmpty();
         }
     }
     
-    private void setTextFromTextFieldIntoComboBoxValue() {
-        if (! comboBox.isEditable()) return;
-        
-        StringConverter<T> c = comboBox.getConverter();
-        if (c == null) return;
-        
-        T oldValue = comboBox.getValue();
-        String text = textField.getText();
-        
-        // conditional check here added due to RT-28245
-        T value = oldValue == null && (text == null || text.isEmpty()) ? null : c.fromString(textField.getText());
-        
-        if ((value == null && oldValue == null) || (value != null && value.equals(oldValue))) {
-            // no point updating values needlessly (as they are the same)
-            return;
-        }
-        
-        comboBox.setValue(value);
-    }
-    
     private int getIndexOfComboBoxValueInItemsList() {
         T value = comboBox.getValue();
         int index = comboBoxItems.indexOf(value);
         return index;
     }

@@ -779,48 +555,20 @@
      *                                                                         *
      * Stylesheet Handling                                                     *
      *                                                                         *
      **************************************************************************/
     
-    private static PseudoClass CONTAINS_FOCUS_PSEUDOCLASS_STATE = PseudoClass.getPseudoClass("contains-focus");    
-    
     // These three pseudo class states are duplicated from Cell
     private static final PseudoClass PSEUDO_CLASS_SELECTED =
             PseudoClass.getPseudoClass("selected");
     private static final PseudoClass PSEUDO_CLASS_EMPTY =
             PseudoClass.getPseudoClass("empty");
     private static final PseudoClass PSEUDO_CLASS_FILLED =
             PseudoClass.getPseudoClass("filled");
 
 
 
-    /***************************************************************************
-     *                                                                         *
-     * Support classes                                                         *
-     *                                                                         *
-     **************************************************************************/
-
-    public static final class FakeFocusTextField extends TextField {
-
-        public void setFakeFocus(boolean b) {
-            setFocused(b);
-        }
-
-        @Override
-        public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) {
-            switch (attribute) {
-                case FOCUS_ITEM: 
-                    /* Internally comboBox reassign its focus the text field.
-                     * For the accessibility perspective it is more meaningful
-                     * if the focus stays with the comboBox control.
-                     */
-                    return getParent();
-                default: return super.queryAccessibleAttribute(attribute, parameters);
-            }
-        }
-    }
-
     @Override
     public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) {
         switch (attribute) {
             case FOCUS_ITEM: {
                 if (comboBox.isShowing()) {

@@ -834,18 +582,18 @@
                 return null;
             }
             case TEXT: {
                 String accText = comboBox.getAccessibleText();
                 if (accText != null && !accText.isEmpty()) return accText;
-                String title = comboBox.isEditable() ? textField.getText() : buttonCell.getText();
+                String title = comboBox.isEditable() ? getEditor().getText() : buttonCell.getText();
                 if (title == null || title.isEmpty()) {
                     title = comboBox.getPromptText();
                 }
                 return title;
             }
-            case SELECTION_START: return textField.getSelection().getStart();
-            case SELECTION_END: return textField.getSelection().getEnd();
+            case SELECTION_START: return getEditor().getSelection().getStart();
+            case SELECTION_END: return getEditor().getSelection().getEnd();
             default: return super.queryAccessibleAttribute(attribute, parameters);
         }
     }
 }