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

AccordionSkin faulty height computation on pane removal

    XMLWordPrintable

Details

    • Bug
    • Resolution: Unresolved
    • P3
    • tbd
    • jfx11, 8, jfx17, jfx18, jfx19
    • javafx
    • generic
    • generic

    Description

      ADDITIONAL SYSTEM INFORMATION :
      OS: Windows 10
      Java version: OpenJDK 11.0.13

      A DESCRIPTION OF THE PROBLEM :
      Given an expaned TitledPane (with the animatedProperty set to true) is remove from the Accordion the AccordionSkin#computeMinHeight and computePrefHeight will return incorrect values leading to the Accordion not shrinking in height until clicked again.
      This seems to be the case, because the AccordionSkin keeps a reference to the current and previously expanded pane and uses those for calculating the heights. However these are not set to null after removal of the pane, so they are still taken into account in the computation. Please have a look into the Stackoverflow post I created discussing this problem: https://stackoverflow.com/questions/71684310/how-to-resize-accordion-after-removing-a-pane-in-javafx?noredirect=1#comment126744696_71684310

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      - Create JavaFX application
      - Create a Scene and set it to be used on the primary stage
      - Create a view containing an Accordion
      - Add a TitledPane containing a Button
      - Implement an EventHandler and bind it to the onAction event of the Button, which on calling the handle method will remove the TitledPane from the accordion. This way you have to first expand the TitledPane to remove it from the Accordion.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The TitledPane holding the clicked Button will be removed from the Accordion and it will resize itself immediately.
      ACTUAL -
      The TitledPane holding the clicked Button is removed from the Accordion, but it's height will stay the same util you click on the Accordion element.

      ---------- BEGIN SOURCE ----------
      public class App extends Application {

          @Override
          public void start(Stage stage) throws IOException {
              FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource("parentView.fxml"));
              Scene scene = new Scene(fxmlLoader.load(), 640, 480);
              stage.setScene(scene);
              stage.show();
          }

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

      }

      public class ParentController {

          @FXML
          private Accordion accordion;

          public void onAddAction() {
              var itemControl = new ItemControl();
              EventHandler<ActionEvent> removeEventHandler = event -> {
                  accordion.getPanes().remove(itemControl);
              };
              itemControl.setOnRemoveProperty(removeEventHandler);
              accordion.getPanes().add(itemControl);
          }
      }

      <StackPane xmlns="http://javafx.com/javafx/16"
                 xmlns:fx="http://javafx.com/fxml/1"
                 fx:controller="org.example.ParentController">
         <Group StackPane.alignment="CENTER">
            <VBox>
               <Accordion fx:id="accordion"/>
               <Button onAction="#onAddAction" text="Add"/>
            </VBox>
         </Group>
      </StackPane>

      public class ItemControl extends TitledPane {

          private final UUID id = UUID.randomUUID();

          private final ObjectProperty<EventHandler<ActionEvent>> onRemoveProperty = new SimpleObjectProperty<>();

          @FXML
          private Button removeButton;

          public ItemControl() {
              FXMLLoader fxmlLoader = new FXMLLoader(ItemControl.class.getResource("itemControl.fxml"));
              fxmlLoader.setRoot(this);
              fxmlLoader.setController(this);

              try {
                  fxmlLoader.load();
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }

          @FXML
          public void initialize() {
              removeButton.onActionProperty().bind(onRemoveProperty);
          }

          public void setOnRemoveProperty(EventHandler<ActionEvent> onRemoveProperty) {
              this.onRemoveProperty.set(onRemoveProperty);
          }

          @Override
          public boolean equals(Object o) {
              if (this == o) return true;
              if (o == null || getClass() != o.getClass()) return false;
              ItemControl that = (ItemControl) o;
              return Objects.equals(id, that.id);
          }

          @Override
          public int hashCode() {
              return Objects.hash(id);
          }
      }

      <fx:root type="javafx.scene.control.TitledPane" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1">
          <VBox>
              <Button fx:id="removeButton" text="Remove"/>
          </VBox>
      </fx:root>


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

      CUSTOMER SUBMITTED WORKAROUND :
      Setting the instance variables previousPane and expandedPane to null (on removal of the TitledPane) in the AccordionSkin will fix this problem, but it requires the use of Reflections, since both previousPane and expandedPane are not accessible from outside of AccordionSkin.

      FREQUENCY : always


      Attachments

        Activity

          People

            kpk Karthik P K
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

              Created:
              Updated: