import java.util.Comparator; 

//import com.sun.javafx.scene.control.skin.ComboBoxListViewSkin; 
import javafx.application.Application; 
import javafx.beans.property.DoubleProperty; 
import javafx.beans.property.SimpleDoubleProperty; 
import javafx.collections.FXCollections; 
import javafx.geometry.HPos; 
import javafx.geometry.Insets; 
import javafx.scene.Node; 
import javafx.scene.Parent; 
import javafx.scene.Scene; 
import javafx.scene.control.Button; 
import javafx.scene.control.ComboBox; 
import javafx.scene.control.Label; 
import javafx.scene.control.ListCell; 
import javafx.scene.control.ScrollPane; 
import javafx.scene.control.Skin; 
import javafx.scene.control.TextField; 
import javafx.scene.control.TitledPane; 
import javafx.scene.layout.GridPane; 
import javafx.scene.layout.Priority; 
import javafx.scene.layout.VBox; 
import javafx.stage.Stage; 

public class ComboBoxTest extends Application 
{ 
    public static void main( String[] args ) 
    { 
        Application.launch( args ); 
    } 

    @Override 
    public void start( Stage stage ) 
    { 
        System.err.println( System.getProperty( "javafx.runtime.version" ) ); 
        Parent content = createScrollPane(); 
        Scene scene = new Scene( content, 800, 600 ); 
        stage.setScene( scene ); 
        stage.show(); 
    } 

    public Parent createScrollPane() 
    { 
        final VBox vBox = new VBox(); 
        final Label description = new Label( 
            "Please change ComboBox's selected value, when width is greater than prefWidth, and watch prefWidth value." ); 
        description.setWrapText( true ); 
        VBox.setMargin( description, new Insets( 5 ) ); 
        final TitledPane buggy = new TitledPane( "ComboBox original behaviour", createContent( false ) ); 
        buggy.setCollapsible( false ); 
        final TitledPane workaround = new TitledPane( "ComboBox with workaround", createContent( true ) ); 
        workaround.setCollapsible( false ); 
        vBox.getChildren().addAll( description, buggy, workaround ); 
        final ScrollPane content = new ScrollPane( vBox ); 
        content.setFitToHeight( true ); 
        content.setFitToWidth( true ); 
        return content; 
    } 

    private Node createContent( boolean isWorkaround ) 
    { 
        final GridPane pane = new GridPane(); 
        // pane.setGridLinesVisible(true); 
        pane.setHgap( 4 ); 
        pane.setVgap( 4 ); 
        pane.setPadding( new Insets( 10 ) ); 

        final TextField textField1 = new TextField( "test" ); 
        pane.add( textField1, 0, 0, 2, 1 ); 
        GridPane.setHgrow( textField1, Priority.ALWAYS ); 
        GridPane.setFillWidth( textField1, true ); 
        GridPane.setHalignment( textField1, HPos.CENTER ); 

        final TextField textField2 = new TextField( "test2" ); 
        pane.add( textField2, 2, 0, 1, 1 ); 
        GridPane.setHgrow( textField2, Priority.ALWAYS ); 
        GridPane.setFillWidth( textField2, true ); 
        GridPane.setHalignment( textField2, HPos.CENTER ); 

        final DoubleProperty computedPrefWidthProperty = new SimpleDoubleProperty(); 
        final ComboBox< Object > comboBox = createComboBox( computedPrefWidthProperty, isWorkaround ); 
        pane.add( comboBox, 0, 1, 3, 1 ); 

        pane.add( new Label( "combo's width: " ), 0, 2, 1, 1 ); 
        final TextField widthTextField = new TextField(); 
        widthTextField.setEditable( false ); 
        widthTextField.textProperty().bind( comboBox.widthProperty().asString() ); 
        GridPane.setHgrow( widthTextField, Priority.ALWAYS ); 
        GridPane.setFillWidth( widthTextField, true ); 
        pane.add( widthTextField, 1, 2, 2, 1 ); 

        pane.add( new Label( "combo's computedPrefWidth: " ), 0, 3, 2, 1 ); 
        final TextField prefWidthTextField = new TextField(); 
        prefWidthTextField.setEditable( false ); 
        prefWidthTextField.textProperty().bind( computedPrefWidthProperty.asString() ); 
        pane.add( prefWidthTextField, 1, 3, 2, 1 ); 
        GridPane.setHgrow( prefWidthTextField, Priority.ALWAYS ); 
        GridPane.setFillWidth( prefWidthTextField, true ); 

        final Button addLongestItem = new Button( "Add longest item to combo" ); 
        addLongestItem.setOnAction( event -> 
        { 
            final Object longestItem = 
                comboBox.getItems().stream().max( Comparator.comparingInt( aO -> aO.toString().length() ) ) 
                    .get(); 
            final Object newLongestItem = longestItem.toString() + "-even-longer"; 
            comboBox.getItems().add( newLongestItem ); 
        } ); 
        pane.add( addLongestItem, 0, 4, 1, 1 ); 

        return pane; 
    } 

    private ComboBox< Object > createComboBox( DoubleProperty computedPrefWidthProperty, 
        boolean isWorkaroundCombo ) 
    { 
        final ComboBox< Object > combo; 
        /*if( isWorkaroundCombo ) 
        { 
            combo = new ComboBoxWithWorkaround() 
            { 
                @Override 
                protected double computePrefWidth( final double height ) 
                { 
                    double ret = super.computePrefWidth( height ); 
                    computedPrefWidthProperty.setValue( ret ); 
                    return ret; 
                } 
            }; 
        } 
        else 
        { */
            combo = new ComboBox< Object >() 
            { 
                @Override 
                protected double computePrefWidth( final double height ) 
                { 
                    double ret = super.computePrefWidth( height ); 
                    computedPrefWidthProperty.setValue( ret ); 
                    return ret; 
                } 
            }; 
        //} 
        combo.setItems( FXCollections.observableArrayList( "One", "Two", 
            "long long long three long long long three long long long three" ) ); 
        combo.setMaxWidth( Double.MAX_VALUE ); 
        combo.getSelectionModel().select( "One" ); 
        combo.setEditable( false ); 
        GridPane.setHgrow( combo, Priority.ALWAYS ); 
        GridPane.setFillWidth( combo, true ); 
        GridPane.setHalignment( combo, HPos.CENTER ); 
        return combo; 
    } 

    /*private static class ComboBoxWithWorkaround extends ComboBox< Object > 
    { 

        @Override 
        protected Skin< ? > createDefaultSkin() 
        { 
            @SuppressWarnings( "restriction" ) 
            Skin skin = new ComboBoxListViewSkin< Object >( this ) 
            { 

                @SuppressWarnings( "restriction" ) 
                protected double computePrefWidth( double height, double topInset, double rightInset, 
                    double bottomInset, double leftInset ) 
                { 
                    double computePrefWidth = 
                        super.computePrefWidth( height, topInset, rightInset, bottomInset, leftInset ); 
                    { 
                        try 
                        { 
                            double result = 
                                superComputePrefWidth( height, topInset, rightInset, bottomInset, leftInset ); 
                            return result; 
                        } 
                        catch( Exception aEx ) 
                        { 
                            // use standard if skin gets errors 
                        } 
                    } 
                    return computePrefWidth; 
                } 

                ; 

                protected double superComputePrefWidth( double height, double topInset, double rightInset, 
                    double bottomInset, double leftInset ) 
                { 
                    if( getDisplayNode() == null ) 
                    { 
                        updateDisplayArea(); 
                    } 

                    final double arrowWidth = snapSize( arrow.prefWidth( -1 ) ); 
                    final double arrowButtonWidth = isButtonMode() ? 
                        0 : 
                        arrowButton.snappedLeftInset() + arrowWidth + arrowButton.snappedRightInset(); 

                    double displayNodeWidth = 0; 

                    if( getDisplayNode() instanceof ListCell ) 
                    { 
                        ListCell< Object > buttonCell = (ListCell)getDisplayNode(); 
                        int index = buttonCell.getIndex(); 
                        for( int i = 0; i < getItems().size(); i++ ) 
                        { 
                            buttonCell.updateIndex( i ); 
                            displayNodeWidth = Math.max( displayNodeWidth, buttonCell.prefWidth( height ) ); 
                        } 
                        buttonCell.updateIndex( index ); 
                    } 

                    displayNodeWidth = Math.max( 50, Math.max( displayNodeWidth, 
                        getDisplayNode() == null ? 0 : getDisplayNode().prefWidth( height ) ) ); 

                    final double totalWidth = displayNodeWidth + arrowButtonWidth; 
                    return leftInset + totalWidth + rightInset; 
                } 

                private boolean isButtonMode() 
                { 
                    return getMode() != null && ( (Object)getMode() ).toString().toUpperCase() 
                        .startsWith( "BUTTON" ); 
                } 

            }; 
            return skin; 
        } 

    } */

} 
