1

I have an event listener that listens for keyboard events. When i try to enter edit mode by using key event, for some strange reason an incorrect cell enters edit mode.

For example I want to edit a cell. I use keyboard arrows to go to the cell I want to edit i.e. the cell that is focused. By clicking a letter on the keyboard, the focused cell should enter edit mode. When I try to edit the focused cell, the wrong cell enters edit mode.

private final class EditCell extends TableCell<SimpleStringProperty, String> implements GenericTable
{

    public EditCell()
    {
        // Add event listsner. table is a TableView
        table.setOnKeyPressed(keyEvent -> this.handleKeyPressed(keyEvent));
    }

    public void handleKeyPressed(KeyEvent key)
    {
        // Keyboard events
        if (key.getCode().isLetterKey())
        {
            if (!this.isEditing())
            {

                this.edit = true;

                // focus index
                int focusIndex = this.table.getSelectionModel().getFocusedIndex();

                this.changeTableCellFocus(this.table, focusIndex);

                this.startEdit();
            }
        }
    }


    // startEdit() function
    @Override
    public void startEdit()
    {
        if (this.edit)
        {
            LOGGER.info("Start editing on cell index: " + this.getIndex());
            super.startEdit();
            this.createTextField();
            this.setText(null);
            this.setGraphic(this.textField);
            this.textField.selectAll();
            this.textField.requestFocus();

            this.textField.setOnKeyPressed(keyEvent -> this.handleKeyPressed(keyEvent));

            this.textField.focusedProperty()
                    .addListener((observable, oldValue, newValue) -> this.onTextFieldFocusChange(observable,
                            oldValue,
                            newValue));
        }
    }

    // Change focus
    public void changeTableCellFocus(final TableView<?> table, final int focusIndex)
    {
        table.requestFocus();
        table.getSelectionModel().clearAndSelect(focusIndex);
        table.getFocusModel().focus(focusIndex);
    }
}

Before entering edit mode, I change focus to the clicked cell and then call the startEdit() method. I have attempted to debug the issue but with no luck. I have noticed that the focusIndex is different from the current cell index. I'm not sure why the index is different.

fabian
  • 80,457
  • 12
  • 86
  • 114
breaktop
  • 1,899
  • 4
  • 37
  • 58
  • Can you give this code some context? This is in a `TableCell` subclass? – James_D Feb 11 '15 at 14:08
  • Did you try using the selection instead of the focus? I guess table editing is based on selection not on focus.. – eckig Feb 11 '15 at 14:17
  • The changeTableCellFocus(...) function selects the cell + puts focus on the cell. Do you mean, just select the cell and then call startEdit(). I think the issue is that current cell index is different from focus cell index which means that even if you change the selected cell to the focused index cell, you will still have the issue as startEdit() is called in the current cell. – breaktop Feb 11 '15 at 14:26
  • Why is the selected cell index different from the current cell index. – breaktop Feb 11 '15 at 14:44
  • All the cells that are created are calling `table.setOnKeyPressed(...)`. `onKeyPressed` is a property, so each one will override the previous call. Consequently, the `onPressed` handler for the table will call `handleKeyPressed(...)` on the last cell created (which obviously is not necessarily the one focused). – James_D Feb 11 '15 at 14:51
  • How would you go about doing it so that when the keyboard event is triggered, the focused cell enters edit mode. – breaktop Feb 11 '15 at 15:02

1 Answers1

7

The problem with your code is that every cell is calling table.setOnKeyPressed(...) as it is created. This works like any other set method, so the keyPressed handler on the table is just set to the one from the last EditCell that was created. You have no control over actual creation of cells, and this is not necessarily (and unlikely) to be the cell that happens to be focused.

The TableView has enough API for you to be able to manage this directly from the table. In particular

table.getFocusModel().getFocusedCell()

will give you a TablePosition representing the currently focused cell. From that you can retrieve the corresponding row index and TableColumn. Then you just need to call table.edit(int row, TableColumn<...> column); to instruct the appropriate cell to go into editing mode.

Here's a complete example. I didn't make much effort to make the editing "pretty" in terms of selecting text etc in the text field, and you might want to implement edit cancel somehow, but this should get you started.

import java.util.ArrayList;
import java.util.List;

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Scene;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class TableViewEditOnType extends Application {


    @Override
    public void start(Stage primaryStage) {
        TableView<List<StringProperty>> table = new TableView<>();
        table.getSelectionModel().setCellSelectionEnabled(true);
        table.setEditable(true);

        for (int i = 0; i < 10; i++) {
            table.getColumns().add(createColumn(i));

            List<StringProperty> rowData = new ArrayList<>();
            table.getItems().add(rowData);
            for (int j = 0; j < 10 ; j++) {
                rowData.add(new SimpleStringProperty(String.format("Cell [%d, %d]", i, j)));
            }
        }

        table.setOnKeyTyped(event -> {
            TablePosition<List<StringProperty>, String> focusedCell = table.getFocusModel().getFocusedCell();
            if (focusedCell != null) {
                table.getItems().get(focusedCell.getRow()).get(focusedCell.getColumn()).set(event.getCharacter());
                table.edit(focusedCell.getRow(), focusedCell.getTableColumn());
            }
        });

        Scene scene = new Scene(new BorderPane(table), 880, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private TableColumn<List<StringProperty>, String> createColumn(int colIndex) {
        TableColumn<List<StringProperty>, String> col = new TableColumn<>("Column "+colIndex);
        col.setCellValueFactory(cellData -> cellData.getValue().get(colIndex));
        col.setCellFactory(column -> new EditCell());
        return col ;
    }

    private static class EditCell extends TableCell<List<StringProperty>, String> {

        private final TextField textField = new TextField();

        EditCell() {   
            textProperty().bind(itemProperty());
            setGraphic(textField);
            setContentDisplay(ContentDisplay.TEXT_ONLY);

            textField.setOnAction(evt -> commitEdit(textField.getText()));
            textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
                if (! isNowFocused) {
                    commitEdit(textField.getText());
                }
            });
        }

        @Override
        public void startEdit() {
            super.startEdit();
            textField.setText(getItem());
            setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
            textField.requestFocus();          
        }

        @Override
        public void cancelEdit() {
            super.cancelEdit();
            setContentDisplay(ContentDisplay.TEXT_ONLY);
        }

        @Override
        public void commitEdit(String text) {
            super.commitEdit(text);
            setContentDisplay(ContentDisplay.TEXT_ONLY);
        }

    }

    public static void main(String[] args) {
        launch(args);
    }
}
James_D
  • 201,275
  • 16
  • 291
  • 322