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

Provide a way for a custom control to indicate that its userAgentStyleSheet has changed

XMLWordPrintable

    • generic
    • generic

      ADDITIONAL SYSTEM INFORMATION :
      Windows 10, Java 14.0.2

      A DESCRIPTION OF THE PROBLEM :
      In JavaFX 8 there was the possibility to change the user agent stylesheet of a Node at runtime and then call impl_reapplyCSS() or reapplyCss() with reflection and the control style would be updated.
      In the next versions the behaviour has been changed, impl_reapplyCSS() has been removed because deprecated and if I call reapplyCss() with reflection the control's style is not updated anymore.
      The only way to change the control style now is to force it by calling getStylesheets().setAll(STYLESHEET)
      I think this regression may be related to JDK-8088253.
      Please fix it as this should not be the intended behavior.

      REGRESSION : Last worked in version 8u281

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Execute the first example with Java 8 and JavaFX 8 and then the second example with newer versions.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The control's style should update if the user agent stylesheet is updated
      ACTUAL -
      The CSS is not reloaded and the control's style is not updated.

      ---------- BEGIN SOURCE ----------
      EXAMPLE 1
      import custombutton.DynamicButton;
      import javafx.application.Application;
      import javafx.geometry.Pos;
      import javafx.scene.Scene;
      import javafx.scene.control.Button;
      import javafx.scene.layout.Region;
      import javafx.scene.layout.StackPane;
      import javafx.scene.layout.VBox;
      import javafx.stage.Stage;

      import static custombutton.DynamicButton.Styles.STYLE1;
      import static custombutton.DynamicButton.Styles.STYLE2;

      public class Test extends Application {
          @Override
          public void start(Stage primaryStage) throws Exception {
              StackPane pane = new StackPane();

              DynamicButton dynamicButton = new DynamicButton("TO CHANGE");
              Button button = new Button("CHANGE!");
              button.setOnAction(event -> {
                  dynamicButton.setDynamicStyle(
                          dynamicButton.getDynamicStyle() == STYLE1 ? STYLE2 : STYLE1
                  );
              });

              VBox box = new VBox(40, dynamicButton, button);
              box.setAlignment(Pos.TOP_CENTER);
              box.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
              pane.getChildren().add(box);

              primaryStage.setScene(new Scene(pane, 800, 600));
              primaryStage.show();
          }

          public static void main(String[] args) {
              launch(args);
          }
      }

      package custombutton;

      import javafx.beans.property.ObjectProperty;
      import javafx.beans.property.SimpleObjectProperty;
      import javafx.scene.control.Button;

      public class DynamicButton extends Button {
          private final String STYLE_CLASS = "dynamic-button";
          private String STYLESHEET = getClass().getResource("dynamic1.css").toString();

          public static enum Styles {
              STYLE1,
              STYLE2
          }

          private final ObjectProperty<Styles> style = new SimpleObjectProperty<>(Styles.STYLE1);

          public DynamicButton() {
              initialize();
          }

          public DynamicButton(String text) {
              super(text);
              initialize();
          }

          private void initialize() {
              getStyleClass().add(STYLE_CLASS);

              style.addListener((observable, oldValue, newValue) -> {
                  if (newValue == Styles.STYLE1) {
                      STYLESHEET = getClass().getResource("dynamic1.css").toString();
                  } else {
                      STYLESHEET = getClass().getResource("dynamic2.css").toString();
                  }

                  impl_reapplyCSS();
                  System.out.println("Style Changed");
              });
          }

          public Styles getDynamicStyle() {
              return style.get();
          }

          public ObjectProperty<Styles> dynamicStyleProperty() {
              return style;
          }

          public void setDynamicStyle(Styles style) {
              this.style.set(style);
          }

          @Override
          public String getUserAgentStylesheet() {
              System.out.println("getUserAgentStylesheet Called");
              return STYLESHEET;
          }
      }


      EXAMPLE 2
      import custombutton.DynamicButton;
      import javafx.application.Application;
      import javafx.geometry.Pos;
      import javafx.scene.Scene;
      import javafx.scene.control.Button;
      import javafx.scene.layout.Region;
      import javafx.scene.layout.StackPane;
      import javafx.scene.layout.VBox;
      import javafx.stage.Stage;

      import static custombutton.DynamicButton.Styles.STYLE1;
      import static custombutton.DynamicButton.Styles.STYLE2;

      public class Test extends Application {
          @Override
          public void start(Stage primaryStage) throws Exception {
              StackPane pane = new StackPane();

              DynamicButton dynamicButton = new DynamicButton("TO CHANGE");
              Button button = new Button("CHANGE!");
              button.setOnAction(event -> {
                  dynamicButton.setDynamicStyle(
                          dynamicButton.getDynamicStyle() == STYLE1 ? STYLE2 : STYLE1
                  );
              });

              VBox box = new VBox(40, dynamicButton, button);
              box.setAlignment(Pos.TOP_CENTER);
              box.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
              pane.getChildren().add(box);

              primaryStage.setScene(new Scene(pane, 800, 600));
              primaryStage.show();
          }

          public static void main(String[] args) {
              launch(args);
          }
      }

      package custombutton;

      import javafx.beans.property.ObjectProperty;
      import javafx.beans.property.SimpleObjectProperty;
      import javafx.scene.Node;
      import javafx.scene.control.Button;

      import java.lang.reflect.InvocationTargetException;
      import java.lang.reflect.Method;

      public class DynamicButton extends Button {
          private final String STYLE_CLASS = "dynamic-button";
          private String STYLESHEET = getClass().getResource("dynamic1.css").toString();

          public static enum Styles {
              STYLE1,
              STYLE2
          }

          private final ObjectProperty<Styles> style = new SimpleObjectProperty<>(Styles.STYLE1);

          public DynamicButton() {
              initialize();
          }

          public DynamicButton(String text) {
              super(text);
              initialize();
          }

          private void initialize() {
              getStyleClass().add(STYLE_CLASS);

              style.addListener((observable, oldValue, newValue) -> {
                  if (newValue == Styles.STYLE1) {
                      STYLESHEET = getClass().getResource("dynamic1.css").toString();
                  } else {
                      STYLESHEET = getClass().getResource("dynamic2.css").toString();
                  }

                  try {
                      Method method = Node.class.getDeclaredMethod("reapplyCss");
                      method.setAccessible(true);
                      Object call = method.invoke(this);
                  } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                      e.printStackTrace();
                  }
                  System.out.println("Style Changed");
              });
          }

          public Styles getDynamicStyle() {
              return style.get();
          }

          public ObjectProperty<Styles> dynamicStyleProperty() {
              return style;
          }

          public void setDynamicStyle(Styles style) {
              this.style.set(style);
          }

          @Override
          public String getUserAgentStylesheet() {
              System.out.println("getUserAgentStylesheet Called");
              return STYLESHEET;
          }
      }

      CSS FILES
      dynamic1.css
      .dynamic-button {
          -fx-border-color: blue;
      }

      dynamic2.css
      .dynamic-button {
          -fx-border-color: red;
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Since the CSS file is cached in STYLESHEET, the only way to update the control's style is to call getStylesheets().setAll(STYLESHEET);

      FREQUENCY : always


        1. Example1.zip
          2 kB
        2. Example2.zip
          2 kB
        3. Additional_info.png
          Additional_info.png
          210 kB
        4. CSSBug.zip
          874 kB

            Unassigned Unassigned
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated: