package bluej.utility.javafx;

import javafx.animation.Timeline;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyIntegerProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableBooleanValue;
import javafx.collections.ObservableList;
import javafx.collections.ObservableSet;
import javafx.css.PseudoClass;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.IndexRange;
import javafx.scene.control.Tooltip;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.text.Font;
import javafx.util.Duration;
import bluej.stride.slots.CompletionCalculator;
import bluej.stride.slots.EditableSlot;
import bluej.utility.Utility;
import bluej.utility.javafx.ErrorUnderlineCanvas.UnderlineInfo;
import threadchecker.OnThread;
import threadchecker.Tag;


| Uses ScalableHeightTextField because we need it in ImportDialog | public class AnnotatableTextField { private final ScalableHeightTextField field; private final StackPane pane; private final ErrorUnderlineCanvas errorMarker; private final BooleanProperty fakeCaretShowing = new SimpleBooleanProperty(false); public AnnotatableTextField(String str, ErrorUnderlineCanvas overlay, boolean startHidden) { field = new ScalableHeightTextField(str, startHidden); field.focusedProperty().addListener((a, b, newVal) -> { JavaFXUtil.setPseudoclass("bj-empty", newVal.booleanValue() == false && field.getLength() == 0, field); }); if (overlay != null) { errorMarker = overlay; pane = new StackPane(field); } else { pane = new StackPane(); errorMarker = new ErrorUnderlineCanvas(pane); pane.getChildren().addAll(field, errorMarker.getNode()); } field.setOnMouseMoved(e -> { JavaFXUtil.setPseudoclass("bj-hyperlink", errorMarker.linkFromX(e.getSceneX()) != null, field); }); field.setOnMouseClicked(e -> { Utility.ifNotNull(errorMarker.linkFromX(e.getSceneX()), FXPlatformRunnable::run); }); errorMarker.addExtraRedraw(g -> { if (fakeCaretShowing.get()) { double x = errorMarker.sceneToLocal(field.localToScene(calculateCaretPosition(field.getCaretPosition()), 0)).getX(); Paint p = g.getStroke(); g.setStroke(Color.BLACK); g.strokeLine(x, 0, x, field.getHeight()); g.setStroke(p); } }); JavaFXUtil.addChangeListener(fakeCaretShowing, c -> JavaFXUtil.runNowOrLater(() -> errorMarker.redraw())); JavaFXUtil.addChangeListener(field.caretPositionProperty(), p -> { if (fakeCaretShowing.get()) JavaFXUtil.runNowOrLater(() -> errorMarker.redraw()); }); } public AnnotatableTextField(ErrorUnderlineCanvas overlay) { this("", overlay); } public AnnotatableTextField(String str, ErrorUnderlineCanvas overlay) { this(str, overlay, false); } public Region getNode() { return pane; } public Node getFocusableNode() { return field; } public final StringProperty textProperty() { return field.textProperty(); } public final ReadOnlyDoubleProperty widthProperty() { return field.widthProperty(); } public final ReadOnlyDoubleProperty heightProperty() { return field.heightProperty(); } public final ReadOnlyBooleanProperty focusedProperty() { return field.focusedProperty(); } public void positionCaret(int pos) { field.positionCaret(pos); } public final StringProperty promptTextProperty() { return field.promptTextProperty(); } public void replaceText(int start, int end, String text) { field.replaceText(start, end, text); } public final boolean isFocused() { return field.isFocused(); } public void requestFocus() { field.requestFocus(); } public final int getLength() { return field.getLength(); } public final ReadOnlyIntegerProperty caretPositionProperty() { return field.caretPositionProperty(); } public final DoubleProperty minWidthProperty() { return field.minWidthProperty(); } public final ReadOnlyObjectProperty selectionProperty() { return field.selectionProperty(); } public final ReadOnlyIntegerProperty anchorProperty() { return field.anchorProperty(); } public void end() { field.end(); } public void deselect() { field.deselect(); } public final DoubleProperty prefWidthProperty() { return field.prefWidthProperty(); } public final ObjectProperty fontProperty() { return field.fontProperty(); } public double measureString(String str, boolean includeInsets) { return JavaFXUtil.measureString(field, str, includeInsets, includeInsets); } public final IndexRange getSelection() { return field.getSelection(); } public final ObjectProperty> onKeyPressedProperty() { return field.onKeyPressedProperty(); } public final BooleanProperty editableProperty() { return field.editableProperty(); } public final BooleanProperty disableProperty() { return field.disableProperty(); } public Timeline getGrowToFullHeightTimeline(Duration dur) { return field.getGrowToFullHeightTimeline(dur); } public Timeline getShrinkToNothingTimeline(Duration dur) { return field.getShrinkToNothingTimeline(dur); } @OnThread(Tag.FXPlatform) public boolean executeCompletion(CompletionCalculator cc, int highlighted, int startOfCurWord) { return cc.execute(field, highlighted, startOfCurWord); }
| Calculates the position of the caret when it is before the given index | in the text field, so passing 0 (before first char) will generally return | 0 (far left of field), passing 1 will return X position at end of first char | (before second char) and so on. | | Ideally this would be calculated using model-to-view or font metrics or similar, | but since JavaFX doesn't seem to support these yet, we use the hack of | adjusting the text content of a hidden text field and measuring its width. | | @param beforeIndex Character position, will calculate caret position before (to left) of char | @return The X coordinate (0 being far left) of the caret, our best guess. | protected double calculateCaretPosition(int beforeIndex) { if (beforeIndex == Integer.MAX_VALUE) return field.getWidth(); double paddingLeft = field.getPadding().getLeft(); double borderLeft = 0; if (field.getBorder() != null && field.getBorder().getInsets() != null) borderLeft = field.getBorder().getInsets().getLeft(); int index = Math.min(beforeIndex, field.getText().length()); return paddingLeft + borderLeft + measureString(field.getText().substring(0, index), false) + 1 | fudge factor | } protected double getBaseline() { double height = field.getHeight() - 3 - field.getPadding().getBottom(); if (field.getBorder() != null && field.getBorder().getInsets() != null) height -= field.getBorder().getInsets().getBottom(); return height; } public final ObjectProperty tooltipProperty() { return field.tooltipProperty(); } @OnThread(Tag.FXPlatform) public void clearUnderlines() { errorMarker.clearUnderlines(); field.setCursor(null); } @OnThread(Tag.FXPlatform) public void drawUnderline(UnderlineInfo s, int startPosition, int endPosition, FXPlatformRunnable onClick) { errorMarker.addUnderline(s, startPosition, endPosition, onClick); } protected void addStyleClasses(String... styleClasses) { JavaFXUtil.addStyleClass(field, styleClasses); } protected void setPseudoclass(String pseudoClass, boolean on) { JavaFXUtil.setPseudoclass(pseudoClass, on, field); } @OnThread(Tag.FXPlatform) public void drawErrorMarker(EditableSlot s, int startPos, int endPos, boolean javaPos, FXPlatformConsumer<Boolean> onHover, ObservableBooleanValue visible) { if ((startPos == 0 && endPos == 0) || getLength() == 0) errorMarker.addErrorMarker(s, 0, Integer.MAX_VALUE, false, onHover, visible); else{ errorMarker.addErrorMarker(s, startPos, endPos, javaPos, onHover, visible); } } @OnThread(Tag.FXPlatform) public void clearErrorMarkers(EditableSlot s) { errorMarker.clearErrorMarkers(s); } public void selectAll() { field.selectAll(); } public void cut() { field.cut(); } public void copy() { field.copy(); } public void paste() { field.paste(); } @OnThread(Tag.FXPlatform) public void backspace() { field.deletePreviousChar(); } public void setContextMenu(ContextMenu menu) { field.setContextMenu(menu); } public void injectEvent(KeyEvent e) { field.fireEvent(e.copyFor(null, field)); } public void setFakeCaretShowing(boolean showing) { fakeCaretShowing.set(showing); } public Font getFont() { return field.getFont(); } public double measureString(String text, Font font) { return JavaFXUtil.measureString(field, text, font, true, true); } public boolean hasSelection() { return field.getAnchor() != field.getCaretPosition(); } public ObservableList getStyleClass() { return field.getStyleClass(); } public ObservableSet getPseudoClassStates() { return field.getPseudoClassStates(); } public StringProperty styleProperty() { return field.styleProperty(); } }
top, use, map, class AnnotatableTextField

.   AnnotatableTextField
.   AnnotatableTextField
.   AnnotatableTextField
.   getNode
.   getFocusableNode
.   textProperty
.   widthProperty
.   heightProperty
.   focusedProperty
.   positionCaret
.   promptTextProperty
.   replaceText
.   isFocused
.   requestFocus
.   getLength
.   caretPositionProperty
.   minWidthProperty
.   selectionProperty
.   anchorProperty
.   end
.   deselect
.   prefWidthProperty
.   fontProperty
.   measureString
.   getSelection
.   onKeyPressedProperty
.   editableProperty
.   disableProperty
.   getGrowToFullHeightTimeline
.   getShrinkToNothingTimeline
.   executeCompletion
.   calculateCaretPosition
.   getBaseline
.   tooltipProperty
.   clearUnderlines
.   drawUnderline
.   addStyleClasses
.   setPseudoclass
.   drawErrorMarker
.   clearErrorMarkers
.   selectAll
.   cut
.   copy
.   paste
.   backspace
.   setContextMenu
.   injectEvent
.   setFakeCaretShowing
.   getFont
.   measureString
.   hasSelection
.   getStyleClass
.   getPseudoClassStates
.   styleProperty




463 neLoCode + 11 LoComm