Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8097017

ClassCastException occurs when using custom CSS Insets property

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Cannot Reproduce
    • Icon: P4 P4
    • 8u40
    • 8u5, 8u20
    • javafx
    • None
    • Mac OS X 10.9.5

      I'm trying to use a custom CSS property of type Insets. I have set up a SimpleStyleableObjectProperty<Insets> to hold it, and use StyleConverter.getInsetsConverter() to read it from the CSS file. However, when I set a ChangeListener<Insets> on the property, I get a ClassCastException because the JavaFX runtime is trying to invoke my ChangeListener<Insets>.changed method with a Double as the new value. I think I'm not introducing the Double anywhere myself, but see the code below. Code to reproduce, confirmed exception on Mac OS X 8u5 and 8u20:

      import java.util.ArrayList;
      import java.util.List;
      import java.util.function.Function;

      import javafx.application.Application;
      import javafx.beans.value.ChangeListener;
      import javafx.css.CssMetaData;
      import javafx.css.SimpleStyleableObjectProperty;
      import javafx.css.StyleConverter;
      import javafx.css.Styleable;
      import javafx.css.StyleableObjectProperty;
      import javafx.css.StyleableProperty;
      import javafx.geometry.Insets;
      import javafx.geometry.Pos;
      import javafx.scene.Node;
      import javafx.scene.Scene;
      import javafx.scene.control.Label;
      import javafx.scene.layout.BorderPane;
      import javafx.scene.layout.VBox;
      import javafx.stage.Stage;


      public class TestInsets extends Application
      {
          // Sets the VBox insets property via CSS
          private static class BorderPaneCustomInsets extends BorderPane
          {
              private final SimpleStyleableObjectProperty<Insets> frameInsetsProperty = new SimpleStyleableObjectProperty<Insets>(FRAME_INSETS_META_DATA, new Insets(0.0));
              
              public BorderPaneCustomInsets(Node item)
              {
                  // If you change <Insets> to <Object> here, you avoid the exception but can see we are being passed a Double:
                  frameInsetsProperty.addListener((ChangeListener<Insets>)(a, b, newVal) -> System.out.println(newVal.getClass()));
                  getChildren().add(item);
                  BorderPane.setAlignment(item, Pos.CENTER);
                  getStyleClass().add("myclass");
              }
              
              private static final CssMetaData<BorderPaneCustomInsets, Insets> FRAME_INSETS_META_DATA =
                      cssInsets("-my-frame-insets", b -> b.frameInsetsProperty);
                   
              private static final List <CssMetaData <? extends Styleable, ? > > cssMetaDataList = new ArrayList<CssMetaData <? extends Styleable, ? >>() {
                  {
                      addAll(BorderPane.getClassCssMetaData());
                      add(FRAME_INSETS_META_DATA);
                  }
              };
                   
              public static List <CssMetaData <? extends Styleable, ? > > getClassCssMetaData() { return cssMetaDataList; }
              @Override public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() { return getClassCssMetaData(); }
          }
          
          public static <T extends Styleable> CssMetaData<T, Insets> cssInsets(String propertyName, Function<T, StyleableObjectProperty<Insets>> propGetter)
          {
              return new CssMetaData <T, Insets >(propertyName, StyleConverter.getInsetsConverter(), new Insets(0.0)) {
                  @Override
                  public boolean isSettable(T node)
                  {
                      return true;
                  }

                  @Override
                  public StyleableProperty <Insets> getStyleableProperty(T node) {
                      return propGetter.apply(node);
                  }
              };
          }

          @Override
          public void start(Stage stage) throws Exception
          {
              VBox vbox = new VBox();
              
              vbox.getChildren().addAll(new BorderPaneCustomInsets(new Label("Label in pane")) );
              
              Scene scene = new Scene(vbox);
              scene.getStylesheets().add("file:custom-insets.css"); // CSS file which contains:
              // .myclass {
              // -my-frame-insets: 5 0 5 0;
              // }
              stage.setScene(scene);
              
              stage.show();
              
          }
          
      }

      Thankfully, the workaround is quite simple: use 4 Double properties instead of one Insets until this works.

            dgrieve David Grieve
            nbrownjfx Neil Brown (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported: