I am making a highly interactive TabPane for viewing contact lists in JavaFX 8. For this I have made my own subclass of Tab, EditableTab, which has functionality for changing the name of the tab by double clicking on the name in the overview. When the user clicks the + sign to create a new contact list, I want the program to create a new tab, select it, then focus the name and select all the text - it is natural to name the contact list at once (similar to when you create a new file in windows).
My problem: This seems to be very unstable. Most of the times, it seems some kind of animation/transition problem arises, and the tab name ends up empty. Here is a screenshot of what usually, but not always, happens when the + button is clicked:
And here is what I want:
Here is the code for my EditableTab:
public class EditableTab extends Tab {
private Label lbl;
private TextField txtField;
public EditableTab(String text, Node content) {
super();
setContent(content);
lbl = new Label(text);
txtField = new TextField(text);
txtField.setStyle("-fx-background-color: transparent");
setGraphic(lbl);
setupInteractivity();
}
public TextField getTextField() {
return txtField;
}
private void setupInteractivity() {
lbl.setOnMouseClicked((mouseEvent) -> {
if (mouseEvent.getClickCount() == 2) {
showTextField();
}
});
txtField.setOnAction(event -> setGraphic(lbl));
txtField.focusedProperty().addListener(
(observable, oldValue, newValue) -> {
if (! newValue) {
lbl.setText(txtField.getText());
setGraphic(lbl);
}
});
}
public void showTextField() {
txtField.setPrefWidth(lbl.getWidth());
txtField.setText(lbl.getText());
setGraphic(txtField);
txtField.selectAll();
txtField.requestFocus();
}
}
And here is the code where the functionality is implemented:
private void addNewContactlist() {
Contactlist newList = new Contactlist();
newList.setName("New contact list");
contactlistApp.getContactlistData().add(newList);
ListView<Person> lv = new ListView<Person>(newList.getContacts());
setupListView(lv);
int position = tabPane.getTabs().size() - 1;
EditableTab tab = createEditableTab("New contact list", lv);
tabPane.getTabs().add(position, tab);
tabPane.getSelectionModel().select(tab);
tab.showTextField();
}
I suspect that the problem comes from some animation/transition timings, but that is really just a guess. I tried wrapping the showTextField()
call in a Platform.runLater()
with no luck.
Here is a small test app to replicate the issue:
public class TestApp extends Application {
TabPane tabPane = new TabPane();
@Override
public void start(Stage primaryStage) throws Exception {
Tab addNewContactlistTab = new Tab();
addNewContactlistTab.setClosable(false);
Label lbl = new Label("\u2795");
lbl.setOnMouseClicked(mouseEvent -> {
if (tabPane.getTabs().size() == 1) {
addNewTab();
}
});
addNewContactlistTab.setGraphic(lbl);
tabPane.getTabs().add(addNewContactlistTab);
addNewContactlistTab.selectedProperty().addListener(
(observable, oldValue, newValue) -> {
if (newValue && tabPane.getTabs().size() != 1) {
addNewTab();
}
});
Scene scene = new Scene(tabPane);
primaryStage.setScene(scene);
primaryStage.setWidth(600);
primaryStage.setHeight(400);
primaryStage.show();
}
private void addNewTab() {
int insertionIndex = tabPane.getTabs().size() - 1;
ListView<String> lv = new ListView<String>();
EditableTab tab = new EditableTab("Unnamed", lv);
tabPane.getTabs().add(insertionIndex, tab);
tabPane.getSelectionModel().select(tab);
tab.showTextField();
}
public static void main(String[] args) {
launch();
}
}
Here is my code for the
RenamableTab
class:And here is my code for the
FancyTabPane
:I am having issued with the new tab button when another tab is selected, not sure how you overcame that.