dima
dima

Reputation: 75

JavaFX 2 TreeView - How to change default behavior of entering edit mode?

Inspired by the JavaFX tutorial on http://docs.oracle.com/javafx/2/ui_controls/tree-view.htm I am wondering how could I change the behaviour to enter a cell in edit mode. The behaviour I would like to get is

I tried to install a mouse event handler on the TreeView/TreeCell but it seems that the event is already consumed by TreeCellBehavior.

In class TreeCellBehvior there is the following method:

private void simpleSelect(MouseEvent e) {
    TreeView tv = getControl().getTreeView();
    TreeItem treeItem = getControl().getTreeItem();
    int index = getControl().getIndex();
    MultipleSelectionModel sm = tv.getSelectionModel();
    boolean isAlreadySelected = sm.isSelected(index);

    tv.getSelectionModel().clearAndSelect(index);

    // handle editing, which only occurs with the primary mouse button
    if (e.getButton() == MouseButton.PRIMARY) {
        if (e.getClickCount() == 1 && isAlreadySelected) {
            tv.edit(treeItem);
        } else if (e.getClickCount() == 1) {
            // cancel editing
            tv.edit(null);
        } else if (e.getClickCount() == 2/* && ! getControl().isEditable()*/) {
            if (treeItem.isLeaf()) {
                // attempt to edit
                tv.edit(treeItem);
            } else {
                // try to expand/collapse branch tree item
                treeItem.setExpanded(! treeItem.isExpanded());
            }
        }
    }
}

I am not sure if can replace the TreeCellBehavior with my own implementation. Though this method is private I am not sure if this would be the right way to go. Any idea?

Upvotes: 1

Views: 2806

Answers (1)

dima
dima

Reputation: 75

I worked it out by myself. I disable the editable of TreeView by default. For each TreeItem there is a context menu allowing to change the items name. If context menu action is invoked the TreeView is set to editable and TreeView.edit() with the current TreeItem is invoked. Now startEdit() is called behind the scenes and edit mode is active.

However I have got some strange behavior after enter is pressed and commitEdit() is called. This method checks if the cell is still in edit mode (which it is and therefore returns true) causing an internal invocation of cancelEdit()?!?! As a workaround I introduced a commitModeProperty and check in cancelEdit() if it is set.. otherwise the new text value would never be set.

Here is my code:

public class FolderTreeCell extends TreeCell<FolderCellType> {

// workaround for a strange behaviour in commitEdit.. see initTextFieldListener() 
private BooleanProperty commitModeProperty = new SimpleBooleanProperty(false);

public FolderTreeCell() {
    assert Platform.isFxApplicationThread();
}

private ContextMenu createContextMenu() {
    MenuItem menuItem = new MenuItem("Change folder name");

    menuItem.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent evt) {
            getTreeView().setEditable(true);
            getTreeView().edit(getTreeItem());
        }

    });

    return new ContextMenu(menuItem);
}

private void initTextFieldListener() {
    getItem().textFieldProperty().get().setOnKeyReleased(new EventHandler<KeyEvent>() {
        @Override
        public void handle(KeyEvent evt) {
            if (evt.getCode() == KeyCode.ENTER) {
                commitEdit(getItem()); // TODO calls updateItem() when isEditing() is true causing invocation of cancelEdit() ?!?!
            } 
        }

    });
}

@Override
public void commitEdit(FolderCellType newFolderCellType) {
    commitModeProperty.set(true);
    super.commitEdit(newFolderCellType);
    commitModeProperty.set(false);
}

@Override
public void startEdit() {
    super.startEdit();

    setGraphic(getItem().getEditBox());

    if (getItem().textFieldProperty().get().getOnKeyReleased() == null) {
        initTextFieldListener();
    }

    getItem().textFieldProperty().get().selectAll();
    getItem().textFieldProperty().get().requestFocus();
}

@Override
public void cancelEdit() {
    super.cancelEdit();

    getTreeView().setEditable(false);
    if (!commitModeProperty.getValue()) {
        getItem().resetCurrentEntry();
    }

    setGraphic(getItem().getViewBox());
}

@Override
public void updateItem(FolderCellType item, boolean empty) {
    super.updateItem(item, empty);

    if (empty || item == null) {
        setText(null);
        setGraphic(null);
    } else {
        if (isEditing()) {
            setGraphic(item.getEditBox());
        } else {
            setGraphic(item.getViewBox());

            if (getContextMenu() == null) {
                setContextMenu(createContextMenu());
            }
        }
    }

    getTreeView().setEditable(false);
}

}

Upvotes: 2

Related Questions