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

TabPane stops repainting itself occasionally when I add/remove Tabs in runtime

XMLWordPrintable

    • generic
    • generic

      ADDITIONAL SYSTEM INFORMATION :
      OS Name: Microsoft Windows 11 Pro
      OS Version: 10.0.22631 N/A Build 22631
      openjdk version "21.0.4" 2024-07-16 LTS
      OpenJDK Runtime Environment Corretto-21.0.4.7.1 (build 21.0.4+7-LTS)
      OpenJDK 64-Bit Server VM Corretto-21.0.4.7.1 (build 21.0.4+7-LTS, mixed mode, sharing)
      JavaFX Version 22.0.1 (gradle project)
      (!) Sorry I could not update to JavaFX 24 or 25, this would need Java update, which I cannot do right now

      A DESCRIPTION OF THE PROBLEM :
      If the Tabs are added/removed (especially in quick successiion), at some time TabPane goes into "wrong" state and stops repainting itself.


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      See the minimal test case provided.
      When I click +/- buttons (especially quick enough), the TabPane stops repainting itself and looks like frozen. The component can be "unfrozen" through moving any SplitPane or resizing the window.

      (!) Found a workaround (originating from https://stackoverflow.com/questions/47616221/javafx-tabpane-tabs-dont-update-position):
      tabPane.setStyle("-fx-close-tab-animation: NONE; -fx-open-tab-animation: NONE;");

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      TabPane stops repainting itselfs
      ACTUAL -
      TabPane reacts on Tabs being added/removed and on clicks on Tabs.

      ---------- BEGIN SOURCE ----------
      import javafx.animation.AnimationTimer;
      import javafx.application.Application;
      import javafx.beans.binding.Bindings;
      import javafx.geometry.Orientation;
      import javafx.scene.Scene;
      import javafx.scene.canvas.Canvas;
      import javafx.scene.canvas.GraphicsContext;
      import javafx.scene.control.*;
      import javafx.scene.layout.HBox;
      import javafx.scene.layout.StackPane;
      import javafx.scene.layout.VBox;
      import javafx.scene.paint.Color;
      import javafx.stage.Stage;

      public class Tmp extends Application {
          public static void main(String[] args) {
              Tmp.launch(args);
          }

          @Override
          public void start(Stage stage) {
              ResizableCanvas canvas = new ResizableCanvas();
              StackPane pane = new StackPane(canvas);

              TabPane tabPane = new TabPane();
              tabPane.getTabs().add(createTab("1"));

              SplitPane splitButtonsTabPane = new SplitPane(new TextArea("placeholder"), tabPane);

              Button btnAdd = new Button("+");
              btnAdd.setOnAction(evt -> tabPane.getTabs().add(createTab(String.format("%d", tabPane.getTabs().size() + 1))));

              Button btnRemove = new Button("-");
              btnRemove.disableProperty().bind(Bindings.isEmpty(tabPane.getTabs()));
              btnRemove.setOnAction(evt -> tabPane.getTabs().removeLast());

              VBox panelBelow = new VBox(splitButtonsTabPane, new HBox(btnAdd, btnRemove));

              SplitPane splitAboveBelow = new SplitPane(pane, panelBelow);
              splitAboveBelow.setOrientation(Orientation.VERTICAL);

              Scene scene = new Scene(splitAboveBelow);
              stage.setScene(scene);
              stage.show();

              new RedrawTimer(() -> redraw(canvas)).start();
          }

          private void redraw(Canvas canvas) {
              GraphicsContext gc = canvas.getGraphicsContext2D();
              gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
              gc.setLineWidth(1);
              gc.setStroke(Color.GREEN);
              gc.strokeRect(10, 10, 50, 50);
              gc.setStroke(Color.RED);
              gc.strokeRect(0, 0, canvas.getWidth(), canvas.getHeight());
          }

          private Tab createTab(String name) {
              Tab result = new Tab(name);
              result.setContent(new Label(name));
              return result;
          }


          // https://stackoverflow.com/questions/24533556/how-to-make-canvas-resizable-in-javafx
          private static class ResizableCanvas extends Canvas {
              @Override
              public boolean isResizable() {
                  return true;
              }

              @Override
              public double maxHeight(double width) {
                  return Double.POSITIVE_INFINITY;
              }

              @Override
              public double maxWidth(double height) {
                  return Double.POSITIVE_INFINITY;
              }

              @Override
              public double minWidth(double height) {
                  return 1D;
              }

              @Override
              public double minHeight(double width) {
                  return 1D;
              }

              @Override
              public void resize(double width, double height) {
                  this.setWidth(width);
                  this.setHeight(height);
              }
          }

          private static class RedrawTimer extends AnimationTimer {
              private final Runnable runnable;

              public RedrawTimer(Runnable runnable) {
                  this.runnable = runnable;
              }

              @Override
              public void handle(long now) {
                  runnable.run();
              }
          }
      }
      ---------- END SOURCE ----------

        1. Test.java
          4 kB
          Anupam Dev
        2. screenshot.png
          12 kB
          Anupam Dev
        3. TabPane_Repaint_8353843.java
          4 kB
          Andy Goryachev

            angorya Andy Goryachev
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated: