setGrid(grid) does not correctly update a SpreadsheetView (allegedly)

Issue #809 new
Rainer
created an issue
  • run the provided code.
  • Click "start"
  • notice how the columns are different width in the first place, moreover the printed width numbers apparently do not match what is on the screen. The table should scroll to the rightmost column but does not
  • Click "start" again
  • notice the numbers now match the visible column width, and because of that the table scrolls to the right as intended, still the columns ARE different width

This is a simplified boiled down example of what I encountered in my application. I think the problem is related to setGrid() in some way.

Tested on 8u144

import java.util.Random;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import org.controlsfx.control.spreadsheet.Grid;
import org.controlsfx.control.spreadsheet.GridBase;
import org.controlsfx.control.spreadsheet.SpreadsheetCell;
import org.controlsfx.control.spreadsheet.SpreadsheetCellType;
import org.controlsfx.control.spreadsheet.SpreadsheetColumn;
import org.controlsfx.control.spreadsheet.SpreadsheetView;

public class SpreadsheetTest extends Application {

    Random rd = new Random();
    final int COL_COUNT = 30, ROW_COUNT = 10;

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        Pane root = new AnchorPane();
        Scene scene = new Scene(root, 700, 400);
        Button btn = new Button("Start");
        btn.setLayoutX(10);
        btn.setLayoutY(10);
        SpreadsheetView spv = new SpreadsheetView();
        spv.setLayoutX(10);
        spv.setLayoutY(50);
        spv.setPrefWidth(650);
        spv.setPrefHeight(300);

        root.getChildren().addAll(btn, spv);
        primaryStage.setScene(scene);
        primaryStage.show();

        btn.setOnAction(event -> {          
            ObservableList <ObservableList <SpreadsheetCell>> rows = FXCollections.observableArrayList();
            for (int r = 0; r < ROW_COUNT; r++) {
                ObservableList <SpreadsheetCell> row = FXCollections.observableArrayList();
                for (int c = 0; c < COL_COUNT; c++) {
                    row.add(SpreadsheetCellType.INTEGER.createCell(r, c, 1, 1, rd.nextInt(100)));
                }
                rows.add(row);
            }
            Grid grid = new GridBase(ROW_COUNT, COL_COUNT);
            grid.setRows(rows);
            spv.setGrid(grid);
            for (SpreadsheetColumn col : spv.getColumns()) System.out.println(col.getWidth());
            spv.scrollToColumnIndex(COL_COUNT - 1);
        });
    }
}

Comments (6)

  1. SamirH

    Hi Rainer,

    Thanks for the feed back.

    I indeed reproduce your issue. When the "setGrid" method is called, it's basically replacing all model data by another model. But we try to keep what is here in order not to recreate all objects.

    So there will be a time where the model data has changed but has not yet been propagated to the whole SpreadsheetView. That's what you're experiencing. Calling the scrollToColumnIndex right after the setGrid is not working. The system wants to scroll to the very last column, but the system only has the previous columns in memory, that's why we are scrolling in the middle on the SpreadsheetView.

    Now I don't have any easy fix for that issue because I cannot control and monitor the pulse of the JavaFX Main Thread and when the whole SpreadsheetView will be redesigned and fully functional.

    But I do have a work-around for you, if you put the last part of your code into a runLater statement :

     Platform.runLater(() -> {
                    for (SpreadsheetColumn col : spv.getColumns()) {
                        System.out.println(col.getWidth());
                    }
                    spv.scrollToColumnIndex(COL_COUNT - 1);
                });
    

    You will get the behavior you expect. By putting the code inside the runLater, you postpone the execution of that code to a later moment, and you are quite certain that the SpreadsheetView will be in a stable state at that moment. I've been using that in my applications, and it's invisible for the end user.

    Let me know if that works for you, I'm open to suggestion and pull request of course.

  2. Rainer reporter

    Hi Samir,

    I never used Platform.runlater() on the Application Thread, but indeed that works, I can work with that. So things that follow setting the grid into the spreadsheet should be done this way, like scrolling the view or setting column widths.

    This makes me think about a strange graphics issue I encountered, where white square blocks appeared around the mouse pointer when moving over a spreadsheet. Maybe you have seen that? I cannot reproduce that situation now but I am sure that tracks down to the same issue. Will try to figure out how I got there for curiousity.

  3. SamirH

    Hi Rainer,

    Sorry for the delay. Well, I personally never experienced what you describe... But some of my colleagues did! We never found out the reason why but we came to the conclusion that it was happening when the memory for the application was low.

    So maybe check your memory, possibly some leaks or what you are retaining. And if you find out that the memory is becoming short and that the GC is running very often, maybe you should see the problem coming where the renderer is having some issues.

    Let me know,

  4. Rainer reporter

    Well I am not really sure about low memory, but maybe you are right. I once got really mad about those graphics issues appearing regularly, and I believe I extracted the code from those times, but I still cannot trigger those white blocks now.

  5. Michael Sausmikat

    Hello Together,

    I had the same problem and I found a workaround. I changed the following lines in the SpreadsheatView class (line 1153):

            if (grid.getRows() != null) {
    //            final ObservableList<ObservableList<SpreadsheetCell>> observableRows = FXCollections
    //                    .observableArrayList(grid.getRows());
    //            cellsView.getItems().clear();
                cellsView.setItems(sortedList);
                computeRowMap();
    

    to

            if ( grid.getRows() != null )
            {
                // final ObservableList<ObservableList<SpreadsheetCell>> observableRows = FXCollections
                // .observableArrayList(grid.getRows());
                cellsView.getItems().clear();
                cellsView.getItems().addAll( sortedList );
                computeRowMap();
    

    I think the underlying table do something special in the getItems().clear() method.

    After that change the rendering is fine after setGrid()

    Maybe this hint will help you.

  6. Log in to comment