@Override public void cancelEdit() { //Once the edit has been cancelled we no longer need the editor //so we mark it for cleanup here. Note though that you have to handle //this situation in the focus listener which gets fired at the end //of the editing. editorNode = null; super.cancelEdit(); builder.cancelEdit(); builder.setValue(getValue()); setContentDisplay(ContentDisplay.TEXT_ONLY); }
@Override public void startEdit() { if (isEditable() && checkGroupedColumn()) { super.startEdit(); if (editorNode == null) { createEditorNode(); } else { // set current value if the editor is already created builder.setValue(getValue()); } builder.startEdit(); setGraphic(editorNode); setContentDisplay(ContentDisplay.GRAPHIC_ONLY); } }
@Override public void updateItem(T item, boolean empty) { super.updateItem(item, empty); if (empty) { setText(null); setGraphic(null); } else { if (isEditing() && checkGroupedColumn()) { if (editorNode != null) { builder.setValue(getValue()); } setGraphic(editorNode); setContentDisplay(ContentDisplay.GRAPHIC_ONLY); builder.updateItem(item, empty); } else { Object value = getValue(); if (value instanceof Node) { setGraphic((Node) value); setContentDisplay(ContentDisplay.GRAPHIC_ONLY); } else { setText(value.toString()); setContentDisplay(ContentDisplay.TEXT_ONLY); } } } }
private void createEditorNode() { EventHandler<KeyEvent> keyEventsHandler = t -> { if (t.getCode() == KeyCode.ENTER) { commitHelper(false); } else if (t.getCode() == KeyCode.ESCAPE) { cancelEdit(); } else if (t.getCode() == KeyCode.TAB) { commitHelper(false); editNext(!t.isShiftDown()); } }; ChangeListener<Boolean> focusChangeListener = (observable, oldValue, newValue) -> { //This focus listener fires at the end of cell editing when focus is lost //and when enter is pressed (because that causes the text field to lose focus). //The problem is that if enter is pressed then cancelEdit is called before this //listener runs and therefore the text field has been cleaned up. If the //text field is null we don't commit the edit. This has the useful side effect //of stopping the double commit. if (editorNode != null && !newValue) { commitHelper(true); } }; editorNode = builder.createNode(getValue(), keyEventsHandler, focusChangeListener); }
/** * Any action attempting to commit an edit should call this method rather than commit the edit directly itself. This * method will perform any validation and conversion required on the value. For text values that normally means this * method just commits the edit but for numeric values, for example, it may first parse the given input. <p> The * only situation that needs to be treated specially is when the field is losing focus. If you user hits enter to * commit the cell with bad data we can happily cancel the commit and force them to enter a real value. If they * click away from the cell though we want to give them their old value back. * * @param losingFocus true if the reason for the call was because the field is losing focus. */ protected void commitHelper(boolean losingFocus) { if (editorNode == null) { return; } try { builder.validateValue(); commitEdit((T) builder.getValue()); } catch (Exception ex) { //Most of the time we don't mind if there is a parse exception as it //indicates duff user data but in the case where we are losing focus //it means the user has clicked away with bad data in the cell. In that //situation we want to just cancel the editing and show them the old //value. if (losingFocus) { cancelEdit(); } } }
for (TreeTableColumn<S, ?> column : getTreeTableView().getColumns()) { columns.addAll(getLeaves(column)); int index = getIndex(); int nextIndex = columns.indexOf(getTableColumn()); if (forward) { nextIndex++; if (columns.size() < 2 && index == getIndex()) { return; getTreeTableView().edit(index, nextColumn); getTreeTableView().scrollToColumn(nextColumn);
/** * only allows editing for items that are not grouped * * @return whether the item is grouped or not */ private boolean checkGroupedColumn() { boolean allowEdit = true; if (getTreeTableRow().getTreeItem() != null) { Object rowObject = getTreeTableRow().getTreeItem().getValue(); if (rowObject instanceof RecursiveTreeObject && rowObject.getClass() == RecursiveTreeObject.class) { allowEdit = false; } else { // check grouped columns in the tableview if (getTableColumn() instanceof JFXTreeTableColumn && ((JFXTreeTableColumn) getTableColumn()).isGrouped()) { // make sure that the object is a direct child to a group node if (getTreeTableRow().getTreeItem().getParent() != null && getTreeTableRow().getTreeItem().getParent().getValue().getClass() == RecursiveTreeObject.class) { allowEdit = false; } } } } return allowEdit; }
public final void setDisclosureNode(Node value) { disclosureNodeProperty().set(value); }
@Override protected void layoutChildren(double x, double y, double w, double h) { updateDisclosureNode(); double disclosureWidth = 0; Node disclosureNode = ((JFXTreeTableCell<S, T>) getSkinnable()).getDisclosureNode(); if (disclosureNode.isVisible()) { Pos alignment = getSkinnable().getAlignment(); alignment = alignment == null ? Pos.CENTER_LEFT : alignment; layoutInArea(disclosureNode, x + 8, y, w, h, 0, Insets.EMPTY, false, false, HPos.LEFT, VPos.CENTER); disclosureWidth = disclosureNode.getLayoutBounds().getWidth() + 18; } super.layoutChildren(x + disclosureWidth, y, w - disclosureWidth, h); } }
private List<TreeTableColumn<S, ?>> getLeaves(TreeTableColumn<S, ?> root) { List<TreeTableColumn<S, ?>> columns = new ArrayList<>(); if (root.getColumns().isEmpty()) { //We only want the leaves that are editable. if (root.isEditable()) { columns.add(root); } return columns; } else { for (TreeTableColumn<S, ?> column : root.getColumns()) { columns.addAll(getLeaves(column)); } return columns; } } }
/** * Returns the current disclosure node set in this cell. */ public final Node getDisclosureNode() { if (disclosureNode.get() == null) { final StackPane disclosureNode = new StackPane(); disclosureNode.getStyleClass().setAll("tree-disclosure-node"); disclosureNode.setMouseTransparent(true); final StackPane disclosureNodeArrow = new StackPane(); disclosureNodeArrow.getStyleClass().setAll("arrow"); disclosureNode.getChildren().add(disclosureNodeArrow); setDisclosureNode(disclosureNode); } return disclosureNode.get(); }
ageColumn.setCellFactory((TreeTableColumn<User, String> param) -> new GenericEditableTreeTableCell<>( new TextFieldEditorBuilder())); ageColumn.setOnEditCommit((CellEditEvent<User, String> t) -> t.getTreeTableView() .getValue().age.set(t.getNewValue())); empColumn.setCellFactory((TreeTableColumn<User, String> param) -> new GenericEditableTreeTableCell<>( new TextFieldEditorBuilder())); empColumn.setOnEditCommit((CellEditEvent<User, String> t) -> t.getTreeTableView() .getValue().userName.set(t.getNewValue())); deptColumn.setCellFactory((TreeTableColumn<User, String> param) -> new GenericEditableTreeTableCell<>( new TextFieldEditorBuilder())); deptColumn.setOnEditCommit((CellEditEvent<User, String> t) -> t.getTreeTableView()
@Override public void startEdit() { if (isEditable() && checkGroupedColumn()) { super.startEdit(); if (editorNode == null) { createEditorNode(); } else { // set current value if the editor is already created builder.setValue(getValue()); } builder.startEdit(); setGraphic(editorNode); setContentDisplay(ContentDisplay.GRAPHIC_ONLY); } }
@Override public void cancelEdit() { //Once the edit has been cancelled we no longer need the editor //so we mark it for cleanup here. Note though that you have to handle //this situation in the focus listener which gets fired at the end //of the editing. editorNode = null; super.cancelEdit(); builder.cancelEdit(); builder.setValue(getValue()); setContentDisplay(ContentDisplay.TEXT_ONLY); }
/** * Any action attempting to commit an edit should call this method rather than commit the edit directly itself. This * method will perform any validation and conversion required on the value. For text values that normally means this * method just commits the edit but for numeric values, for example, it may first parse the given input. <p> The * only situation that needs to be treated specially is when the field is losing focus. If you user hits enter to * commit the cell with bad data we can happily cancel the commit and force them to enter a real value. If they * click away from the cell though we want to give them their old value back. * * @param losingFocus true if the reason for the call was because the field is losing focus. */ protected void commitHelper(boolean losingFocus) { if (editorNode == null) { return; } try { builder.validateValue(); commitEdit((T) builder.getValue()); } catch (Exception ex) { //Most of the time we don't mind if there is a parse exception as it //indicates duff user data but in the case where we are losing focus //it means the user has clicked away with bad data in the cell. In that //situation we want to just cancel the editing and show them the old //value. if (losingFocus) { cancelEdit(); } } }
public final void setDisclosureNode(Node value) { disclosureNodeProperty().set(value); }
private void updateDisclosureNode() { Node disclosureNode = ((JFXTreeTableCell<S, T>) getSkinnable()).getDisclosureNode(); if (disclosureNode != null) { TreeItem<S> item = getSkinnable().getTreeTableRow().getTreeItem(); final S value = item == null ? null : item.getValue(); boolean disclosureVisible = value != null && !item.isLeaf() && value instanceof RecursiveTreeObject && ((RecursiveTreeObject) value).getGroupedColumn() == getSkinnable().getTableColumn(); disclosureNode.setVisible(disclosureVisible); if (!disclosureVisible) { getChildren().remove(disclosureNode); } else if (disclosureNode.getParent() == null) { getChildren().add(disclosureNode); disclosureNode.toFront(); } else { disclosureNode.toBack(); } if (disclosureNode.getScene() != null) { disclosureNode.applyCss(); } } }
return new GenericEditableTreeTableCell<>( new TextFieldEditorBuilder()); }); }); lastNameEditableColumn.setCellFactory((TreeTableColumn<Person, String> param) -> { return new GenericEditableTreeTableCell<>( new TextFieldEditorBuilder()); }); }); ageEditableColumn.setCellFactory((TreeTableColumn<Person, Integer> param) -> { return new GenericEditableTreeTableCell<>( new IntegerTextFieldEditorBuilder()); });