Skip to content

Instantly share code, notes, and snippets.

@Groostav
Created September 16, 2015 20:53
Show Gist options
  • Save Groostav/fe3a53ada46f80da0922 to your computer and use it in GitHub Desktop.
Save Groostav/fe3a53ada46f80da0922 to your computer and use it in GitHub Desktop.
Attempts to make tab and commit-on-click work
package com;
import com.empowerops.common.exceptions.ExceptionUtilities;
import javafx.beans.value.ChangeListener;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.util.StringConverter;
/**
* Created by Josh on 1/1/2015.
*/
public class OASISTableCell<TTable,TCell> extends TableCell<TTable, TCell> {
protected TextField localTextField;
protected boolean isCancelEditManually = false;
private ChangeListener<TablePosition> focusListener = (e, oldCell, newCell) -> {
updateFocus(newCell);
};
private ChangeListener<TablePosition<TTable, ?>> terminatingListener =
(e, oldPosition, newPosition) -> terminateEdit(newPosition);
public OASISTableCell() {
tableViewProperty().addListener(
(e, oldValue, newValue) -> {
uninstallFocusListener(oldValue);
installFocusListener(newValue);
}
);
tableViewProperty().addListener(
(e, oldTable, newTable) -> {
uninstallTerminatingListener(oldTable);
installTerminatingListener(newTable);
addListenerToTableView(newTable);
}
);
}
protected void updateFocus(TablePosition<TTable, ?> focusedCell) {
setFocused(match(focusedCell));
}
protected boolean match(TablePosition<TTable, ?> pos) {
return pos != null && pos.getRow() == getIndex()
&& pos.getTableColumn() == getTableColumn();
}
@Override
public void startEdit() {
if (!isEmpty()) {
localTextField = new TextField(getString());
localTextField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
addListenerToTextField(localTextField);
setText(null);
setGraphic(localTextField);
localTextField.selectAll();
super.startEdit();
localTextField.requestFocus();
}
}
@Override
public void updateItem(TCell item, boolean empty) {
if (item == getItem())
return;
super.updateItem(item, empty);
if (item == null) {
super.setText(null);
super.setGraphic(null);
} else if (item instanceof Node) {
super.setText(null);
super.setGraphic((Node) item);
} else {
super.setText(item.toString());
super.setGraphic(null);
}
}
@Override
public void cancelEdit() {
// do nothing, but we have write here
}
@Override
protected Skin<?> createDefaultSkin() {
return new OASISTableCellSkin<TTable, TCell>(this);
}
protected void cancelEditByUser() {
super.cancelEdit();
if (null != getItem()) {
ExceptionUtilities.failOnException(() -> {
setText((String) getItem());
localTextField.setText((String)getItem());
});
}
setGraphic(null);
}
protected void terminateEdit(TablePosition<TTable, ?> newPosition) {
if (!isEditing() || !match(newPosition)) {
return;
}
localCommitEdit();
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
protected void localCommitEdit() {
StringConverter<TCell> converter = new CastingConverter<>();
if (localTextField != null) {
TCell edited = converter.fromString(localTextField.getText());
if (! isCancelEditManually ) {
super.commitEdit(edited);
}
isCancelEditManually = false;
}
}
protected void installFocusListener(TableView<TTable> table) {
if (table == null) {
return;
}
TableView.TableViewFocusModel<TTable> model = table.getFocusModel();
if (model != null) {
model.focusedCellProperty().addListener(focusListener);
}
}
protected void uninstallFocusListener(TableView<TTable> table) {
if (table == null) {
return;
}
TableView.TableViewFocusModel<TTable> model = table.getFocusModel();
if (model != null) {
model.focusedCellProperty().removeListener(focusListener);
}
}
protected void installTerminatingListener(TableView<TTable> newTable) {
if (newTable instanceof OASISTableView) {
((OASISTableView<TTable>) newTable).terminatingCellProperty().addListener(terminatingListener);
}
}
protected void uninstallTerminatingListener(TableView<TTable> oldTable) {
if (oldTable instanceof OASISTableView) {
((OASISTableView) oldTable).terminatingCellProperty().removeListener(terminatingListener);
}
}
public static class CastingConverter<TConverted> extends StringConverter<TConverted> {
@Override
public String toString(TConverted object) {
return (String) object;
}
@Override
public TConverted fromString(String string) {
return (TConverted) string;
}
}
protected void addListenerToTableView(TableView<TTable> tableView) {
tableView.setOnKeyReleased(
(KeyEvent event) -> {
if (event.getCode() == KeyCode.ESCAPE) {
isCancelEditManually = true;
cancelEditByUser();
event.consume();
}
if (event.getCode() == KeyCode.TAB) {
findNextLogicalCell();
event.consume();
}
}
);
tableView.addEventFilter(
MouseEvent.MOUSE_CLICKED,
event -> {
if (event.isPopupTrigger()) {
event.consume();
}
}
);
tableView.setOnKeyPressed(
event -> {
if (! event.isControlDown() &&
(event.getCode().isLetterKey() || event.getCode().isDigitKey() || event.getCode() == KeyCode.MINUS)
|| event.getCode() == KeyCode.QUOTE
|| event.getCode() == KeyCode.BACK_QUOTE) {
TablePosition tablePosition = tableView.getFocusModel().getFocusedCell();
tableView.edit(tablePosition.getRow(), tablePosition.getTableColumn());
}
}
);
}
protected void addListenerToTextField(TextField localTextField) {
localTextField.focusedProperty().addListener(
(event, wasFocused, isNowFocused) -> {
if (wasFocused && ! isNowFocused) {
localCommitEdit();
}
}
);
localTextField.setOnKeyReleased(
(KeyEvent keyEvent) -> {
if (keyEvent.getCode() == KeyCode.ENTER
|| keyEvent.getCode() == KeyCode.TAB) {
findNextLogicalCell();
keyEvent.consume();
}
if (keyEvent.getCode() == KeyCode.ESCAPE) {
isCancelEditManually = true;
cancelEditByUser();
keyEvent.consume();
}
}
);
localTextField.addEventFilter(
MouseEvent.MOUSE_CLICKED,
event -> {
if (event.isPopupTrigger()) {
event.consume();
}
}
);
}
@SuppressWarnings("unchecked") //figuring out the cell types from here is impossible.
protected void findNextLogicalCell() {
localCommitEdit();
TableView<TTable> tableView = this.getTableView();
TableView.TableViewFocusModel<TTable> focusModel = tableView.getFocusModel();
TablePosition tp = focusModel.getFocusedCell();
if (tp.getColumn() != tableView.getColumns().size() - 1) {
focusModel.focusRightCell();
}
else {
focusModel.focusBelowCell();
while(focusModel.getFocusedCell().getColumn() != 0) {
focusModel.focusLeftCell();
}
}
TablePosition nextTargetCell = focusModel.getFocusedCell();
getTableView().getSelectionModel().select(nextTargetCell.getRow(), nextTargetCell.getTableColumn());
getTableView().edit(nextTargetCell.getRow(), nextTargetCell.getTableColumn());
}
}
package com;
import com.sun.javafx.scene.control.behavior.TableCellBehavior;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableView;
import javafx.scene.input.MouseButton;
public class OASISTableCellBehavior<S, T> extends TableCellBehavior<S, T>{
public OASISTableCellBehavior(TableCell<S, T> control) {
super(control);
}
protected void tryTerminateEdit() {
TableCell<S, T> cell = getControl();
TableView<S> table = cell.getTableColumn().getTableView();
if (table instanceof OASISTableView) {
((OASISTableView<S>) table).terminateEdit();
}
}
@Override
protected void handleClicks(MouseButton button, int clickCount,
boolean isAlreadySelected) {
tryTerminateEdit();
super.handleClicks(button, clickCount, isAlreadySelected);
}
}
package com;
import com.sun.javafx.scene.control.behavior.TableCellBehavior;
import com.sun.javafx.scene.control.skin.TableCellSkinBase;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
public class OASISTableCellSkin<S,T> extends TableCellSkinBase<TableCell<S,T>, TableCellBehavior<S,T>> {
private final TableCell<S,T> tableCell;
private final TableColumn<S,T> tableColumn;
public OASISTableCellSkin(TableCell<S, T> tableCell) {
super(tableCell, new OASISTableCellBehavior<S,T>(tableCell));
this.tableCell = tableCell;
this.tableColumn = tableCell.getTableColumn();
super.init(tableCell);
}
@Override
protected BooleanProperty columnVisibleProperty() {
return tableColumn.visibleProperty();
}
@Override
protected ReadOnlyDoubleProperty columnWidthProperty() {
return tableColumn.widthProperty();
}
}
package com;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
public class OASISTableColumn<TTable extends TableColumn, TCell extends TableCell>
extends TableColumn<TTable, TCell> {
private StringProperty cellType = new SimpleStringProperty(this, "cellType") {
@Override
protected void invalidated() {
setCellFactory(new CellFactory<>(getValue()));
}
};
public OASISTableColumn() {
}
public OASISTableColumn(String text) {
super(text);
}
public StringProperty cellTypeProperty() {
return cellType;
}
public String getCellType() {
return cellTypeProperty().get();
}
public void setCellType(String value) {
cellTypeProperty().set(value);
}
}
package com;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableView;
public class OASISTableView<S> extends TableView<S> {
public boolean isTerminating = false;
@Override
public void edit(int row, TableColumn<S, ?> column) {
super.edit(row, column);
setTerminatingCell(null);
}
public void terminateEdit() {
if (!isTerminating()) {
return;
}
isTerminating = true;
setTerminatingCell(getEditingCell());
}
public boolean isTerminating() {
return getEditingCell() != null;
}
private ReadOnlyObjectWrapper<TablePosition<S,?>> terminatingCell;
protected void setTerminatingCell(TablePosition<S, ?> terminatingPosition) {
terminatingCellPropertyImpl().set(terminatingPosition);
}
public final ReadOnlyObjectProperty<TablePosition<S,?>> terminatingCellProperty() {
return terminatingCellPropertyImpl().getReadOnlyProperty();
}
private ReadOnlyObjectWrapper<TablePosition<S,?>> terminatingCellPropertyImpl() {
if (terminatingCell == null) {
terminatingCell = new ReadOnlyObjectWrapper<>(this, "terminatingCell");
}
return terminatingCell;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment