Java 8 DatePicker and editable ComboBox behavior change between 8u51 and 8u60

前端 未结 2 1564
一生所求
一生所求 2021-01-14 15:47

We have searched this forum and the web extensively and have not found anything related to this issue so I felt compelled to post this question here...

We have obse

相关标签:
2条回答
  • 2021-01-14 16:06

    Here's my workaround:

        combo.getEditor().focusedProperty().addListener((obs, old, isFocused) -> { 
            if (!isFocused) { 
                combo.setValue(combo.getConverter().fromString(combo.getEditor().getText()));
            } 
        }); 
    

    As is pointed out in the official bug report, you might want to check to see if the value is already set to avoid any potential side-effects of calling it again.

    0 讨论(0)
  • 2021-01-14 16:30

    The implementation of ComboBoxPopupControl (which in fact is the relevant base skin for all combo-like controls) changed somewhere between u40 and u60: the textField - including all internal wirings - was pulled up from the concrete skins (like f.i. ComboBoxListViewSkin) into the base.

    Along with this mere technicality the handler for ENTER was changed:

    • was: forward all keyEvents that are received by the combo to the textField
    • is: handle and consume the ENTER in the base skin. This implementation manually commits the input text from the field to the combo's value.

    A dirty (!because needs access to the internal package com.sun.whatever.skin) way out is a custom skin that listens to the focusProperty and calls the commit method, something like:

    public class MyComboSkin<T> extends ComboBoxListViewSkin<T> {
    
        public MyComboSkin(ComboBox<T> comboBox) {
            super(comboBox);
            getSkinnable().focusedProperty().addListener((source, ov, nv) -> {
                if (!nv) {
                    setTextFromTextFieldIntoComboBoxValue();
                }
            });
        }
    }
    

    The advantage of applying a custom skin is that it can be applied once per scene by adding a styleSheet:

    // defining the skin in a css mycomboskin.css 
    .combo-box {
        -fx-skin: "mypackage.MyComboSkin";
    }
    
    // apply to a scene
    String commitCSS = getClass().getResource("mycomboskin.css").toExternalForm();
    scene.getStylesheets().add(commitCSS);
    
    // or to the application (using internal api again ;-)
    StyleManager.getInstance().addUserAgentStylesheet(commitCSS) 
    

    BTW: I think the new implementation in core is rather dirty - all keyBindings should be handled either in the XXBehaviour or alternatively left to the lower-level children (like the textField itself). The change in behaviour (verified against 8u45) might be considered a bug.


    Update

    An alternative trick is using a TextFormatter on the combo's editor and bidi-bind it's valueProperty to the combo's valueProperty, something like:

    TextFormatter formatter = 
            new TextFormatter<>(comboBox.getConverter());
    comboBox.getEditor().setTextFormatter(formatter);
    comboBox.valueProperty().bindBidirectional(formatter.valueProperty());
    

    This does work because the formatter guarantees to commit - aka: sync its own value to the textField's text - on focusLost (more details in a similar requirement for Spinner) Note that a side-effect of this approach is that the text is committed on navigation inside the drop-down which might or not be acceptable depending on context. Also, it's more experimenting with TextFormatters than a dedicated workaround - needs the same per-instance manipalution as the workaround in the other solution by Scott.


    The bug is fixed in jdk9 and backported 8u72, so any workaround hopefully is short-lived, choosing the once or other and going as dirty as necessary is likely a matter of taste :-)

    0 讨论(0)
提交回复
热议问题