In a quite complex javafx application I faced a possible rendering bug. The last two days I could track it down to the following simple application. The following SSCCE demonstrates that in certain circumstances some javafx components are not correctly rendered. As a result, ComboBox and ListView do not show changed content:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TabPane.TabClosingPolicy;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ComboManagedBug extends Application {
public static void main(String[] args) {
launch(args);
}
public void start(Stage primaryStage) {
// add combo box
ComboBox<String> combo = new ComboBox<>();
combo.setPromptText("Choose a value...");
combo.getItems().setAll("1", "2", "3");
// add list view
ListView<Label> list = new ListView<>();
// add "add" button
Button add = new Button("Add");
add.setOnAction(e -> list.getItems().add(
new Label(combo.getSelectionModel().getSelectedItem())));
// add tab pane
Tab tab1 = new Tab("First", new VBox(combo, add, list));
Tab tab2 = new Tab("Second");
TabPane tabs = new TabPane(tab1, tab2);
tabs.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE); // important!
// add "next" and "cancel" buttons at bottom
Button next = new Button("Next");
Button cancel = new Button("Cancel (Triggers refresh)");
HBox buttons = new HBox(next, cancel);
// install tab listener
tabs.getSelectionModel().selectedItemProperty().addListener((a, b, c) -> {
// intention is to show next button only on first tab
boolean firstTab = c == tab1;
next.setVisible(firstTab);
next.setManaged(firstTab); // important!
});
// show
VBox root = new VBox(new VBox(tabs, buttons)); // important!
Scene scene = new Scene(root, 400, 400);
primaryStage.setTitle("ComboBox/ListView Rendering Bug Demo");
primaryStage.setScene(scene);
primaryStage.show();
}
}
Steps to reproduce:
Note that there are three important reqiurements to reproduce this bug (marked with "important"):
Is this bug already known, and does anybody know a workaround for this? I tried Platform.runLater(cancel.fire()) and similar things but without success.
Thank you for any hints, Peter.
Btw., apart from this, our company uses javafx for some years now. In our experience, it is very reliable and programming javafx is fun. I hope there is a simple solution for our problem :)
As discussed in the comments of the original post, the bug does not occur in JavaFX 8 (Oracle) but in later versions (OpenJFX). I found the following workaround:
public static void fixTabRendering(TabPane tabs) {
if (tabs.getTabClosingPolicy() != TabClosingPolicy.UNAVAILABLE) return;
tabs.setTabClosingPolicy(TabClosingPolicy.SELECTED_TAB);
for (Node node : tabs.lookupAll(".tab-close-button")) {
// hide "close" button to imitate TabClosingPolicy.UNAVAILABLE
node.setStyle("-fx-background-color:transparent;-fx-shape:null;-fx-pref-width:0.001");
}
}
This code should be run after showing the stage (otherwise lookupAll() returns null) as well as after adding tabs to the tab pane. The latter could be achieved via a tab listener:
tabs.getTabs().addListener((Change<?> change) ->
Platform.runLater(() -> fixTabRendering(tabs)));
Platform.runLater() is required because otherwise lookupAll() may not return the nodes of the added tabs.
Maybe this solution can help someone :)