FXMisc / Flowless

Efficient VirtualFlow for JavaFX
BSD 2-Clause "Simplified" License
185 stars 38 forks source link

VirtualFlow doesn't resize cells properly when layout bounds changes depending on the Orientation. #72

Closed bwcsemaj closed 4 years ago

bwcsemaj commented 4 years ago
import java.util.Arrays;
import java.util.Random;

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class VirtualFlowTest2 extends Application{

    // Attributes
    private static final Random RANDOM = new Random();

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

    @Override
    public void start(Stage stage) throws Exception{
        StackPane contentPane = new StackPane();
        VirtualFlow<String, Cell<String, ?>> virtualFlow = VirtualFlow
            .createHorizontal(FXCollections.observableArrayList(Arrays.asList("A", "B", "C", "D")), (value) -> {
                Button button = new Button();
                button.prefHeightProperty().bind(contentPane.heightProperty());
                button.prefWidthProperty().bind(contentPane.widthProperty().multiply(.1));
                button.setText(value);
                button.setBackground(new Background(
                    new BackgroundFill(new Color(RANDOM.nextDouble(), RANDOM.nextDouble(), RANDOM.nextDouble(), 1),
                        CornerRadii.EMPTY, Insets.EMPTY)));
                return Cell.wrapNode(button);
            });

        contentPane.getChildren().add(virtualFlow);
        max(virtualFlow);
        stage.setScene(new Scene(contentPane, 500, 500));
        stage.show();
    }

    public static Node[] max(Region... nodes){
        for(Region region : nodes){
            region.setMinSize(0, 0);
            region.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        }
        return nodes;
    }
    // End Methods

}

As you can see I created a Horizontal VirtualFlow with cells 10% of the width of the parent. Now when resizing using only the width of the stage, which is also resizing the parent, you can see that the cells don't resize to 10% of the parent. This is due to SizeTracker lengthFN:

Val<Function<Cell<?, ?>, Double>> lengthFn =
    avoidFalseInvalidations(breadthForCells).map(breadth -> cell -> {
        return orientation.prefLength(cell, breadth);
    });

That static avoid false invalidation method I'm not 100% sure the purpose of it but essentially in this cause eats up the event that would cause the cells to be resized. This is just for the width. When you change the height of the VirtualFlow you can see that the cells height also change with it.

When you delete the call to avoidFalseInvalidations it will now properly resize the cells regarding resizing the stage's width.

Val<Function<Cell<?, ?>, Double>> lengthFn =
    breadthForCells.map(breadth -> cell -> {
        return orientation.prefLength(cell, breadth);
    });

Now say you make a Vertical VirtualFlow. With my same code you would have to make the cell's height take 10% of its parent and max width. Now when changing the height of the stage the VirtualFlow doesn't resize its cells while if you change the width it does. The same fix above works.

Now I know I provided a solution but I don't exactly know what that avoid false invalidation method exact purpose is. Maybe it is trying to prevent like an infinite loop of invalidating I don't know.

Jugen commented 4 years ago

@bwcsemaj thanks for reporting and the diagnostics :1st_place_medal: