Featured Post

Applying Email Validation to a JavaFX TextField Using Binding

This example uses the same controller as in a previous post but adds a use case to support email validation.  A Commons Validator object is ...

Saturday, November 15, 2014

JavaFX Text Manipulation Functions

I recently coded cut-and-paste functionality into an app.  As a habit, I reached for the Commons Lang StringUtils class to manipulate Strings to extract ranges and to re-form replace text.

There's a simpler way which is to use JavaFX's TextField text manipulation methods.  These methods already work with IndexRange objects, so you don't have to juggle start and end positions.

I wrote some code like this to pull text from a JavaFX TextField into the Clipboard for a cut operation.  If anything was selected in the TextField, it is deleted.

UPDATE


A poster recommended the TextInputControl methods cut(), copy(), and paste() rather than the text manipulation methods.  This worked great for my Menu code -- the handlers on the MenuItems were one-liners -- but not for my ToolBar.  I think the difference is in the component focus.  The Button grabs the focus while the MenuItem doesn't.

So, for the cut, copy, and paste operations, use the TextInputControl methods.  If they aren't working, try the examples in this post.

END UPDATE

Cut Operation - StringUtils


After putting the selected text into the Clipboard, a selection range is calculated.  From the range, a first part and last part are calculated.  (The "middle part" is to be deleted.)  First and last are concatenated in the TextField.

  String text = focusedTF.getSelectedText();
ClipboardContent content = new ClipboardContent();
content.putString(text);
systemClipboard.setContent(content);

  IndexRange range = focusedTF.getSelection();
String origText = focusedTF.getText();
String firstPart = StringUtils.substring( origText, 0, range.getStart() );
String lastPart = StringUtils.substring( origTextrange.getEnd(),
        StringUtils.length(origText) );
focusedTF.setText( firstPart + lastPart );
focusedTF.positionCaret( range.getStart() );

This code works, but I've written more code than I need.  Consider this version which uses the deleteText() method on the TextField.

Cut Operation - TextField Methods


With the TextField methods, an IndexRange can be used without unpacking it into starts and ends.

String text = focusedTF.getSelectedText();
ClipboardContent content = new ClipboardContent();
content.putString(text);
systemClipboard.setContent(content);
IndexRange range = focusedTF.getSelection();
focusedTF.deleteText( range );


It's shorter and avoids a trial-and-error cycle if I'm not clear on the whether the range calls from my StringUtils versions are inclusive or exclusive.

Notice also that the caret is taken care of using deleteText, so the positionCaret call goes away.

Paste Operation - StringUtils


There's a similar transformation in my code for the paste operation. This time, I'm using replaceText().  As with the StringUtils version of the cut operation, a range is found and the selected String hacked to be reformed with the clipboardText.  The caret needs to be set.

String clipboardText = systemClipboard.getString();
TextField focusedTF = getFocusedTextField();
IndexRange range = focusedTF.getSelection();
String origText = focusedTF.getText();
int endPos = 0;
String updatedText = "";
String firstPart = StringUtils.substring( origText, 0, range.getStart() );
String lastPart = StringUtils.substring( origTextrange.getEnd(),
       StringUtils.length(origText) );

updatedText = firstPart + clipboardText + lastPart;
ifrange.getStart() == range.getEnd() ) {
endPos = range.getEnd() + StringUtils.length(clipboardText);
else {
endPos = range.getStart() + StringUtils.length(clipboardText);
}
focusedTF.setText( updatedText );
focusedTF.positionCaret( endPos );
}

Paste Operation - TextField Methods


The arguments to replaceText() come from the TextField itself.

String clipboardText = systemClipboard.getString();
TextField focusedTF = getFocusedTextField();
IndexRange range = focusedTF.getSelection();
focusedTF.replaceText( range, clipboardText );

A little String manipulation won't kill your app, but the TextField methods are simpler to work because IndexRanges don't need to be unpacked.  Using the TextField methods also help the controls work consistently by handling the caret position.  In the future, more functionality may be packed into the TextField methods, say an animation.  If you write your own, you'll be stuck with setText()



4 comments:

  1. Hi,

    why aren't you just using the cut/copy/paste methods from the TextInputControl class (the parent of the TextField)? Wouldn't they do the same?

    Regards
    WhoCares

    ReplyDelete
    Replies
    1. Great suggestion! For my Menu-driven Clipboard commands, this worked great: a one-liner for cut/copy/paste. However, I didn't get good results with my ToolBar commands. I'm keeping track of the last selected TF and calling cut() from the Button handler. But this didn't put anything in the Clipboard and didn't remove the text from the TF.

      So, I have both implementations going.

      Delete
  2. You might want to correct the misspelling in the title of this blog post.

    ReplyDelete