title: How to commit when clicking outside an editable TableView cell in JavaFX?
body: <p>I have a table cell factory responsible for creating an editable cell in a JavaFX TableView.</p>
  
  <p>I'm trying to implement some added functionality to the tableview so that when the user clicks outside the editable cell a commit is made (the edited text is saved, and not discarded as per the default tableview behavior.)</p>
  
  <p>I added an <code>textField.focusedProperty()</code> event handler, where I commit the text from the text field. However, when one clicks outside the current cell <code>cancelEdit()</code> gets called and calling <code>commitEdit(textField.getText());</code> has no effect. </p>
  
  <p>I have come to realize that once <code>cancelEdit()</code> is called the <code>TableCell.isEditing()</code> returns false and so the commit will never happen.</p>
  
  <p><strong>How can I make so that when the user clicks outside the editable cell the text is committed?</strong></p>
  
  <p><em>After committing an <code>setOnEditCommit()</code> event handler will take care of the validation and database logic. I haven't included it here since it will most likely complicate things even further.</em></p>
  
  <pre><code>// EditingCell - for editing capability in a TableCell
  public static class EditingCell extends TableCell&lt;Person, String&gt; {
  private TextField textField;
  
  public EditingCell() {
  }
  
  @Override public void startEdit() {
      super.startEdit();
  
      if (textField == null) {
          createTextField();
      }
      setText(null);
      setGraphic(textField);
      textField.selectAll();
  }
  
  @Override public void cancelEdit() {
      super.cancelEdit();
      setText((String) getItem());
      setGraphic(null);
  }
  
  @Override public void updateItem(String item, boolean empty) {
      super.updateItem(item, empty);
      if (empty) {
          setText(null);
          setGraphic(null);
      } else {
          if (isEditing()) {
              if (textField != null) {
                  textField.setText(getString());
              }
              setText(null);
              setGraphic(textField);
          } else {
              setText(getString());
              setGraphic(null);
          }
      }
  }
  
  private void createTextField() {
      textField = new TextField(getString());
      textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
      textField.setOnKeyReleased(new EventHandler&lt;KeyEvent&gt;() {                
          @Override public void handle(KeyEvent t) {
              if (t.getCode() == KeyCode.ENTER) {
                  commitEdit(textField.getText());
              } else if (t.getCode() == KeyCode.ESCAPE) {
                  cancelEdit();
              }
          }
      });
  
      textField.focusedProperty().addListener(new ChangeListener&lt;Boolean&gt;() {
           @Override
           public void changed(ObservableValue&lt;? extends Boolean&gt; observable, Boolean oldValue, Boolean newValue) {
               if (!newValue) {
                      commitEdit(textField.getText());
               }
           }
      });
  }
  
  private String getString() {
      return getItem() == null ? "" : getItem().toString();
  }
  } 
  </code></pre>