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

Incorrect LayoutFlags adjustment when adding unmanaged children to SceneGraph

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Resolved
    • Priority: P3
    • Resolution: Fixed
    • Affects Version/s: 8u45
    • Fix Version/s: 9
    • Component/s: javafx
    • Labels:

      Backports

        Description

        FULL PRODUCT VERSION :
        java version "1.8.0_45"
        Java(TM) SE Runtime Environment (build 1.8.0_45-b15)
        Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)


        ADDITIONAL OS VERSION INFORMATION :
        Microsoft Windows [Version 6.3.9600]

        A DESCRIPTION OF THE PROBLEM :
        The main problem is that layout() is not called on a Control that is requesting a layout in a certain situation.

        In detail, when a control is a child of an unmanaged container which has its layoutflag not set to LayoutFlags.CLEAN (due to the control requesting a layout call) and this container is added to the SceneGraph at a parent which has its layoutflag set to LayoutFlags.CLEAN, the CLEAN flag remains on the parent (instead of being switched to DIRTY_BRANCH) and the requestLayout() calls of the control in the subgraph never get attention.

        The effect is that the control is never displayed correctly.


        We believe that the bug is in line 265 of class Parent:

        if (node.isManaged()) {
          relayout = true;
        }

        should be

        if (node.isManaged() || (node instanceof Parent && ((Parent)node).layoutFlag != LayoutFlags.CLEAN)) {
          relayout = true;
        }

        STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
        Start the test application. Step through with the button "perform next step" repeatedly.

        EXPECTED VERSUS ACTUAL BEHAVIOR :
        EXPECTED -
        Step 1: A ProgressBar appears. This step is necessary for the ProgressBar to create a skin first.
        Step 2: The ProgressBar disappears.
        Step 3: The progress property of the ProgressBar is set to a value to invoke a requestLayout call and the container of the ProgressBar becomes dirty. The progress is set to 0.6 which means "a bit more than half full"
        Step 4: The ProgressBar appears again. The progress of 0.6 is clearly visible indicated by a bar inside the ProgressBar
        Any subsequent step: The progress of the ProgressBar is changed and the change becomes visible immediately.

        ACTUAL -
        Step 1: A ProgressBar appears. This step is necessary for the ProgressBar to create a skin first.
        Step 2: The ProgressBar disappears.
        Step 3: The progress property of the ProgressBar is set to a value to invoke a requestLayout call and the container of the ProgressBar becomes dirty. The progress is set to 0.6 which means "a bit more than half full"
        Step 4: The ProgressBar appears again. The progress of 0.6 is not visible.
        Any subsequent step: The progress of the ProgressBar is changed but the change is not visible.


        REPRODUCIBILITY :
        This bug can be reproduced always.

        ---------- BEGIN SOURCE ----------

        import javafx.application.Application;
        import javafx.scene.Group;
        import javafx.scene.Scene;
        import javafx.scene.control.Button;
        import javafx.scene.control.ProgressBar;
        import javafx.scene.layout.BorderPane;
        import javafx.stage.Stage;

        public class UnmanagedParentBugDemo extends Application {

          private int step;
          private Group intermediate;
          Group innerGroup;
          Group outerGroup;

          @Override
          public void start(Stage primaryStage) throws Exception {
            ProgressBar progressBar = new ProgressBar(0);
            progressBar.setPrefWidth(100);
            progressBar.setMinWidth(100);
            progressBar.setMinHeight(20);
            progressBar.setPrefHeight(20);
            progressBar.setLayoutX(100);
            progressBar.setLayoutY(100);
            progressBar.autosize();
            progressBar.layout();

            innerGroup = new Group();
            innerGroup.setManaged(false);
            innerGroup.getChildren().add(progressBar);

            outerGroup = new Group();
            outerGroup.setManaged(false);

            BorderPane borderPane = new BorderPane(outerGroup);
            Button button = new Button("Perform Next Step");
            button.setOnAction(event -> {
              switch(step){
                case 0:
                  /*
                     Scene
                       |
                       |___BorderPane
                                 |
                                 |_____ Outergroup (clean)

                     Innergroup (dirty)
                        |
                        |____________Progressbar
                   */
                  if (outerGroup.getChildren().isEmpty()) {
                    outerGroup.getChildren().add(innerGroup);
                  }
                  /*
                     Scene
                       |
                       |___BorderPane
                                 |
                                 |_____ Outergroup (clean)
                                           |
                                           |________Innergroup (dirty)
                                                       |
                                                       |____________Progressbar
                   */
                  step++;
                  break;
                case 1:
                  outerGroup.getChildren().remove(innerGroup);
                  /*
                     Scene
                       |
                       |___BorderPane
                                 |
                                 |_____ Outergroup (clean)


                      Innergroup (clean)
                          |
                          |____________Progressbar
                   */
                  intermediate = new Group();
                  intermediate.setManaged(false);
                  intermediate.setAutoSizeChildren(false);
                  outerGroup.getChildren().add(intermediate);

                  /*
                     Scene
                       |
                       |___BorderPane
                                 |
                                 |_____ Outergroup (clean)
                                           |
                                           |_________Intermediategroup (dirty)


                      Innergroup (clean)
                          |
                          |____________Progressbar
                   */
                  step++;
                  break;
                case 2:
                  progressBar.setProgress(0.6); // layoutflag in the Innergroup is set to dirty because of this

                  /*
                     Scene
                       |
                       |___BorderPane
                                 |
                                 |_____ Outergroup (clean)
                                           |
                                           |_________Intermediategroup (dirty)


                      Innergroup (dirty)
                          |
                          |____________Progressbar
                   */
                  step++;
                  break;
                case 3:
                  intermediate.getChildren().add(innerGroup);

                  /*
                     Scene
                       |
                       |___BorderPane
                                 |
                                 |_____ Outergroup (clean)
                                           |
                                           |_________Intermediategroup (dirty)
                                                             |
                                                             |___________Innergroup (dirty)
                                                                              |
                                                                              |____________Progressbar
                   */
                  step++;
                  break;
                case 4:
                  progressBar.setProgress(Math.random());
                  System.out.println("progressBar = " + progressBar.getProgress());
                  break;
              }
              System.out.println("step = " + step);
            });
            borderPane.setTop(button);

            primaryStage.setScene(new Scene(borderPane, 500, 500));
            primaryStage.show();
          }
        }

        ---------- END SOURCE ----------

        CUSTOMER SUBMITTED WORKAROUND :
        * Call this.requestLayout() every time a child is added to a parent. This is not feasible for production code, obviously.
        * Call layout() on the control by yourself.

          Attachments

            Issue Links

              Activity

                People

                Assignee:
                ckyang Chien Yang (Inactive)
                Reporter:
                webbuggrp Webbug Group
                Votes:
                0 Vote for this issue
                Watchers:
                4 Start watching this issue

                  Dates

                  Created:
                  Updated:
                  Resolved: