Creating a two dimensional VirtualFlow
Hello,
I'm currently using TableView in my project, but I've run into two major limitations that are pushing me to consider reimplementing a custom table using Flowless:
- Performance inefficiencies — similar to those highlighted in Flowless benchmarks.
-
Limited control over scrolling — specifically, no direct access to
TableView's scrolling behavior.
To work around this, I started experimenting with VirtualFlow. However, it seems you can only create either a vertical or a horizontal flow — not both simultaneously. My approach was to create a horizontal VirtualFlow where each column is itself a vertical VirtualFlow. This setup works to some extent, but introduces a new challenge:
- I can add a global horizontal scrollbar.
- I cannot add vertical scrollbars for all columns together.
I tried to bind vertical scrolling bidirectionally, but it seems to causes misalignments in certain cases.
My question is: is there a known way to implement a truly two-dimensional VirtualFlow with independent horizontal and vertical scrollbars? Or any best practices for synchronizing scrolling across nested flows without losing alignment?
Any insights or suggestions would be greatly appreciated!
Thanks, Syméon
Here is some of the code if that helps understanding:
The table
public class CustomTable extends AnchorPane {
public CustomTable(List<List<String>> columns) {
ObservableList<List<String>> observableList = FXCollections.observableList(columns);
// Following is a property to bind all the columns
Var<Double> scrollY = Var.doubleVar(new SimpleDoubleProperty(0.0));
VirtualFlow<List<String>, CustomTableColumn> columnContent = VirtualFlow.createHorizontal(observableList, strings -> {
CustomTableColumn column = new CustomTableColumn(strings);
column.bindScroll(scrollY);
return column;
});
VirtualizedScrollPane<VirtualFlow<List<String>, CustomTableColumn>> scrollPane = new VirtualizedScrollPane<>(columnContent);
AnchorPane.setTopAnchor(scrollPane, 0);
AnchorPane.setRightAnchor(scrollPane, 0);
AnchorPane.setBottomAnchor(scrollPane, 0);
AnchorPane.setLeftAnchor(scrollPane, 0);
this.getChildren().add(scrollPane);
}
}
And the custom column:
public class CustomTableColumn implements Cell<List<String>, AnchorPane> {
private final VirtualFlow<String, CustomCell> columnContent;
private final AnchorPane pane;
public CustomTableColumn(List<String> content) {
pane = new AnchorPane();
ObservableList<String> list = FXCollections.observableList(content);
columnContent = VirtualFlow.createVertical(list, CustomCell::new);
AnchorPane.setTopAnchor(columnContent, 0);
AnchorPane.setRightAnchor(columnContent, 0);
AnchorPane.setBottomAnchor(columnContent, 0);
AnchorPane.setLeftAnchor(columnContent, 0);
pane.getChildren().add(columnContent);
}
public void bindScroll(Var<Double> prop) {
columnContent.estimatedScrollYProperty().bindBidirectional(prop);
}
@Override
public AnchorPane getNode() {
return pane;
}
}