JavaFX 8: Missing caret in switch-editable ComboBox

故事扮演 提交于 2019-12-12 04:32:42

问题


The caret of an editable ComboBox is missing after porting an application from JavaFX 2.2 to JavaFX 8. The ComboBox should be switched to editable on selecting an item. I tested it under Windows 8.1 with Oracle JDK 1.8 Update 102 and Update 112.

When the ComboBox lost focus and regained focus, the caret is visible.

It actually works on JavaFX 2.2 after changing the lambda to an interface implementation and removing the Platform.runLater.

I included a SSCCE for testing:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class TestEditableComboBox extends Application{

   @Override
   public void start(Stage primaryStage) throws Exception{

      ComboBox<String> comboBox = new ComboBox<String>(FXCollections.observableArrayList("item 1",
                                                                                         "item 2",
                                                                                         "editable"));
      comboBox.setMinWidth(100D);
      comboBox.setMaxWidth(100D);
      comboBox.valueProperty().addListener((observable,
                                            oldValue,
                                            newValue) -> {

         if (newValue != null){
            if ("editable".equals(newValue)){
               // JavaFX 2.2: comboBox.setEditable(true);
               Platform.runLater(() -> comboBox.setEditable(true));
            }
            else{
               // JavaFX 2.2: comboBox.setEditable(true);
               Platform.runLater(() -> {
                  comboBox.setEditable(false);
                  comboBox.getSelectionModel().select(newValue);
               });
            }
         }
      });

      VBox vBox = new VBox(new Label("Broken caret"),
                           comboBox);
      Scene scene = new Scene(vBox);
      primaryStage.setScene(scene);
      primaryStage.show();
   }

   public static void main(String[] args){

      Application.launch(args);
   }
}

Anyone have an idea to work this around? Or is this a JavaFX 8 regression bug?

PS: Without the Platform.runLater the ComboBox throws a java.lang.IndexOutOfBoundsException because its model is modified while another modification is in progress.


回答1:


I found a workaround for this issue.

The internal implementation of ComboBox at class com.sun.javafx.scene.control.skin.ComboBoxPopupControl have an specialized class called FakeFocusTextField which extends javafx.scene.control.TextField. An instance of this class is given back when calling ComboBox::getEditor.

Surprisingly the FakeFocusTextField class have a public method called setFakeFocus which gives the focus to the text field. The requestFocus method in this class gives the focus to its parent.

The workaround is to change the line of code which sets the comboBox editable from:

Platform.runLater(() -> comboBox.setEditable(true));

to

Platform.runLater(() -> {
    comboBox.setEditable(true);
    if (this.getEditor() instanceof FakeFocusTextField){
       ((FakeFocusTextField) this.getEditor()).setFakeFocus(true);
    }
}

Unfortunately this workaround uses JavaFX classes outside of the JavaFX 8 API. It may break if the implementation is changed and might not work on future releases (like Java 9).




回答2:


I failed to find a better strategy than fireandfuel, however, I would add some extra guards around the non-standard API calls. Only attempting the "bodge" if the JavaFX version is 8, and surrounding with a try catch, in order to minimise the chance of failure with future versions of JavaFX.

Also of interest, if you call the setFakeFocus "bodge" to TWO ComboBoxes, it is possible for BOTH of them to appear to have selection. My fix for this, is to request focus in the normal way first before applying the "bodge".

Here's my full solution in Kotlin :

val javaFXVersion by lazy { System.getProperties().getProperty("javafx.runtime.version") }

fun ComboBox<*>.requestFocusWithCaret() {
    requestFocus()

    if (javaFXVersion.startsWith("8.")) {
        try {
            val theEditor = editor
            if (theEditor is FakeFocusTextField) {
                theEditor.setFakeFocus(true)
            }
        } catch (e: Exception) {
        }
    }
}

Which can then be called using :

myComboBox.requestFocusWithCaret()


来源:https://stackoverflow.com/questions/40239400/javafx-8-missing-caret-in-switch-editable-combobox

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!