package bluej.utility.javafx;

import java.util.List;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.css.CssMetaData;
import javafx.css.SimpleStyleableDoubleProperty;
import javafx.css.Styleable;
import javafx.scene.control.IndexRange;
import javafx.scene.control.TextField;
import javafx.scene.input.Clipboard;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.Region;

import threadchecker.OnThread;
import threadchecker.Tag;

public class DelegableScalableTextField<DELEGATE_IDENT> extends ScalableHeightTextField{    
   private final SimpleStyleableDoubleProperty bjMinWidthProperty = new SimpleStyleableDoubleProperty(BJ_MIN_WIDTH_META_DATA);
   
   
| When we let super class handle nextWord, it may call end, but this can produce the wrong result | when we've also overridden end. This flag indicates whether we are currently in the middle of a next-word call: | private boolean inNextWord = false; private final SimpleStyleableDoubleProperty bjMinWidthProperty() { return bjMinWidthProperty; } private static final CssMetaData<DelegableScalableTextField<?>, Number> BJ_MIN_WIDTH_META_DATA = JavaFXUtil.cssSize("-bj-min-width", DelegableScalableTextField::bjMinWidthProperty); private static final List<CssMetaData <? extends Styleable, ? > > cssMetaDataList = JavaFXUtil.extendCss(TextField.getClassCssMetaData()) .add(BJ_MIN_WIDTH_META_DATA) .build(); public static List > getClassCssMetaData() { return cssMetaDataList; } @Override public List> getControlCssMetaData() { return cssMetaDataList; } private final TextFieldDelegate<DELEGATE_IDENT> delegate; private final DELEGATE_IDENT delegateId; @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void insertText(int index, String text) { delegate.insert(delegateId, index, text); } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public boolean deletePreviousChar() { if (delegate.deleteSelection() || delegate.deletePrevious(delegateId, getCaretPosition(), getCaretPosition() == 0)) { return true; } else { return super.deletePreviousChar(); } } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public boolean deleteNextChar() { if (delegate.deleteSelection() || delegate.deleteNext(delegateId, getCaretPosition(), getCaretPosition() == getLength())) { return true; } else { return super.deleteNextChar(); } } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void previousWord() { if (!delegate.previousWord(delegateId, getCaretPosition() == 0)) super.previousWord(); } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void nextWord() { if (!delegate.nextWord(delegateId, getCaretPosition() == getLength())) { inNextWord = true; super.nextWord(); inNextWord = false; } } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void endOfNextWord() { if (!delegate.endOfNextWord(delegateId, getCaretPosition() == getLength())) { super.endOfNextWord(); } } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void backward() { delegate.deselect(); if (getCaretPosition() == 0) { delegate.backwardAtStart(delegateId); } else { super.backward(); } } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void cut() { if (!delegate.cut()) super.cut(); } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void copy() { if (!delegate.copy()) super.copy(); } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void forward() { delegate.deselect(); if (getCaretPosition() == getText().length()) delegate.forwardAtEnd(delegateId); else{ super.forward(); } } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void appendText(String text) { insertText(getText().length(), text); } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void replaceText(IndexRange range, String text) { replaceText(range.getStart(), range.getEnd(), text); } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void replaceText(int start, int end, String text) { insertText(start, text); } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void deleteText(IndexRange range) { super.deleteText(range.getStart(), range.getEnd()); } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void deleteText(int start, int end) { delegate.delete(delegateId, start, end); positionCaret(start); } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void selectBackward() { if (!delegate.selectBackward(delegateId, getCaretPosition())) { super.selectBackward(); } } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void deselect() { delegate.deselect(); super.deselect(); } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void selectForward() { if (!delegate.selectForward(delegateId, getCaretPosition(), getCaretPosition() == getLength())) super.selectForward(); } public DelegableScalableTextField(TextFieldDelegate<DELEGATE_IDENT> delegate, DELEGATE_IDENT ident, String content) { super(content); this.delegate = delegate; this.delegateId = ident; JavaFXUtil.addStyleClass(this, "delegable-scalable-text-field"); setMinWidth(Region.USE_PREF_SIZE); prefWidthProperty().bind(new DoubleBinding() {{ super.bind(textProperty()); super.bind(promptTextProperty()); super.bind(fontProperty()); super.bind(focusedProperty()); super.bind(paddingProperty()); super.bind(bjMinWidthProperty()); } @Override protected double computeValue() { double minWidth; if (getPromptText().isEmpty() && getText().isEmpty()) { return bjMinWidthProperty.get(); } else if (getPromptText().isEmpty() || !getText().isEmpty()) { minWidth = bjMinWidthProperty.get(); } else { minWidth = JavaFXUtil.measureString(DelegableScalableTextField.this, getPromptText()); } return Math.max(minWidth, 2 + JavaFXUtil.measureString(DelegableScalableTextField.this, getText())); } }); setOnMousePressed(e -> { if (e.getButton() == MouseButton.PRIMARY && e.getClickCount() == 1) delegate.clicked(); delegate.moveTo(e.getSceneX(), e.getSceneY(), true); e.consume(); } }); setOnDragDetected(e -> { e.consume(); }); setOnMouseDragged(e -> { if (e.getButton() == MouseButton.PRIMARY) { delegate.selectTo(e.getSceneX(), e.getSceneY()); e.consume(); } }); setOnMouseReleased(e -> { if (e.getButton() == MouseButton.PRIMARY) { delegate.selected(); e.consume(); } }); setOnMouseClicked(e -> { if (e.getButton() == MouseButton.PRIMARY) e.consume(); }); caretPositionProperty().addListener(new ChangeListener<Number>() { @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) { delegate.caretMoved(); } }); addEventHandler(KeyEvent.KEY_PRESSED, e -> { if (e.getCode() == KeyCode.ESCAPE) delegate.escape(); }); } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void selectNextWord() { if (!delegate.selectNextWord(delegateId)) super.selectNextWord(); } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void selectEndOfNextWord() { if (!delegate.selectNextWord(delegateId)) super.selectEndOfNextWord(); } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void selectPreviousWord() { if (!delegate.selectPreviousWord(delegateId)) super.selectPreviousWord(); } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void selectAll() { if (!delegate.selectAll(delegateId)) super.selectAll(); } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void home() { delegate.deselect(); if (!delegate.home(delegateId)) super.home(); } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void end() { delegate.deselect(); if (!delegate.end(delegateId, inNextWord)) super.end(); } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void selectHome() { if (!delegate.selectHome(delegateId, getCaretPosition())) super.selectHome(); } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void selectEnd() { if (!delegate.selectEnd(delegateId, getCaretPosition())) super.selectEnd(); } @Override @OnThread(value = Tag.FXPlatform, ignoreParent = true) public void paste() { Clipboard clipboard = Clipboard.getSystemClipboard(); if (clipboard.hasString()) { delegate.deleteSelection(); insertText(getCaretPosition(), clipboard.getString()); } } public double calculateSceneX(int caretPos) { double borderLeft = 0; if (getBorder() != null && getBorder().getInsets() != null) borderLeft = getBorder().getInsets().getLeft(); double localX = getPadding().getLeft() + borderLeft + JavaFXUtil.measureString(this, getText().substring(0, Math.min(caretPos, getText().length())), true, false); return localToScene(localX, 0).getX(); } }

.   bjMinWidthProperty
.   getClassCssMetaData
.   getControlCssMetaData
.   insertText
.   deletePreviousChar
.   deleteNextChar
.   previousWord
.   nextWord
.   endOfNextWord
.   backward
.   cut
.   copy
.   forward
.   appendText
.   replaceText
.   replaceText
.   deleteText
.   deleteText
.   selectBackward
.   deselect
.   selectForward
.   DelegableScalableTextField
.   computeValue
.   changed
.   selectNextWord
.   selectEndOfNextWord
.   selectPreviousWord
.   selectAll
.   home
.   end
.   selectHome
.   selectEnd
.   paste
.   calculateSceneX




490 neLoCode + 2 LoComm