diff --git a/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/TextViewApp.java b/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/TextViewApp.java index f2da02f0..0cc1f294 100644 --- a/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/TextViewApp.java +++ b/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/TextViewApp.java @@ -11,13 +11,14 @@ import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; +import javafx.scene.paint.Color; import javafx.stage.Stage; public class TextViewApp extends Application { @Override public void start(Stage stage) { - TextView textView = new TextView("Lorem ipsum dolor sit amet consectetur adipiscing elit nunc hendrerit purus, nisi dapibus primis nibh volutpat fringilla ad nisl urna posuere, cubilia sagittis egestas pharetra sociis montes nullam netus erat. Fusce mauris condimentum neque morbi nunc ligula pretium vehicula nulla, platea dictum mus sapien pulvinar eget porta mi praesent, orci hac dignissim suscipit imperdiet sem per a. Mauris pellentesque dui vitae velit netus venenatis diam felis urna ultrices, potenti pretium sociosqu eros dictumst dis aenean nibh cursus, leo sagittis integer nullam malesuada aliquet et metus vulputate. Interdum facilisis congue ac proin libero mus ullamcorper mauris leo imperdiet eleifend porta, posuere dignissim erat tincidunt vehicula habitant taciti porttitor scelerisque laoreet neque. Habitant etiam cubilia tempor inceptos ad aptent est et varius, vitae imperdiet phasellus feugiat class purus curabitur ullamcorper maecenas, venenatis mollis fusce cras leo eros metus proin. Fusce aenean sociosqu dis habitant mi sapien inceptos, orci lacinia nisi nascetur convallis at erat sociis, purus integer arcu feugiat sollicitudin libero. Lorem ipsum dolor sit amet consectetur adipiscing elit nunc hendrerit purus, nisi dapibus primis nibh volutpat fringilla ad nisl urna posuere. Lorem ipsum dolor sit amet consectetur adipiscing elit nunc hendrerit purus, nisi dapibus primis nibh volutpat fringilla ad nisl urna posuere. Lorem ipsum dolor sit amet consectetur adipiscing elit nunc hendrerit purus, nisi dapibus primis nibh volutpat fringilla ad nisl urna posuere."); + TextView textView = new TextView("Lorem ipsum dolor sit amet consectetur adipiscing elit nunc hendrerit purus, nisi dapibus primis nibh volutpat fringilla ad nisl urna posuere, cubilia sagittis egestas pharetra sociis montes nullam netus erat. Fusce mauris condimentum neque morbi nunc ligula pretium vehicula nulla, platea dictum mus sapien pulvinar eget porta mi praesent, orci hac dignissim suscipit imperdiet sem per a. Mauris pellentesque dui vitae velit netus venenatis diam felis urna ultrices, potenti pretium sociosqu eros dictumst dis aenean nibh cursus, leo sagittis integer nullam malesuada aliquet et metus vulputate. Interdum facilisis congue ac proin libero mus ullamcorper mauris leo imperdiet eleifend porta, posuere dignissim erat tincidunt vehicula habitant taciti porttitor scelerisque laoreet neque. Habitant etiam cubilia tempor inceptos ad aptent est et varius, vitae imperdiet phasellus feugiat class purus curabitur ullamcorper maecenas, venenatis mollis fusce cras leo eros metus proin. Fusce aenean sociosqu dis habitant mi sapien inceptos, orci lacinia nisi nascetur convallis at erat sociis, purus integer arcu feugiat sollicitudin libero. Lorem ipsum dolor sit amet consectetur adipiscing elit nunc hendrerit purus, nisi dapibus primis nibh volutpat fringilla ad nisl urna posuere. Lorem ipsum dolor sit amet consectetur adipiscing elit nunc hendrerit purus, nisi dapibus primis nibh volutpat fringilla ad nisl urna posuere. Lorem ipsum dolor sit amet consectetur adipiscing elit nunc hendrerit purus, nisi dapibus primis nibh volutpat fringilla ad nisl urna posuere. A"); textView.setPrefWidth(400); HBox.setHgrow(textView, Priority.ALWAYS); @@ -30,9 +31,6 @@ public void start(Stage stage) { selectedText.setWrapText(true); selectedText.textProperty().bind(textView.selectedTextProperty()); - Button clear = new Button("Clear Selection"); - clear.setOnAction(evt -> textView.clearSelection()); - Button copyAll = new Button("Copy entire text to clipboard"); copyAll.setOnAction(evt -> textView.copyAll()); @@ -56,14 +54,14 @@ public void start(Stage stage) { controls.add(new Label("Selected Text"), 0, 1); controls.add(selectedText, 1, 1); - - controls.add(clear, 1, 2); - controls.add(copyAll, 1, 3); + controls.add(copyAll, 1, 2); HBox hBox = new HBox(10, textView, controls); hBox.setPadding(new Insets(20)); - stage.setScene(new Scene(hBox)); + Scene scene = new Scene(hBox); + scene.focusOwnerProperty().addListener(it -> System.out.println("focus owner: " + scene.getFocusOwner())); + stage.setScene(scene); stage.centerOnScreen(); stage.sizeToScene(); stage.setTitle("Custom Label"); diff --git a/gemsfx/src/main/java/com/dlsc/gemsfx/TextView.java b/gemsfx/src/main/java/com/dlsc/gemsfx/TextView.java index 88f0b62c..0334feaf 100644 --- a/gemsfx/src/main/java/com/dlsc/gemsfx/TextView.java +++ b/gemsfx/src/main/java/com/dlsc/gemsfx/TextView.java @@ -1,21 +1,37 @@ package com.dlsc.gemsfx; import com.dlsc.gemsfx.skins.TextViewSkin; +import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyStringProperty; import javafx.beans.property.ReadOnlyStringWrapper; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; +import javafx.beans.value.WritableValue; import javafx.collections.MapChangeListener; +import javafx.css.CssMetaData; +import javafx.css.Styleable; +import javafx.css.StyleableObjectProperty; +import javafx.css.StyleableProperty; +import javafx.css.converter.PaintConverter; import javafx.geometry.Orientation; import javafx.scene.control.ContextMenu; import javafx.scene.control.Control; import javafx.scene.control.MenuItem; import javafx.scene.control.Skin; +import javafx.scene.control.SkinBase; +import javafx.scene.control.TextInputControl; +import javafx.scene.control.skin.TextInputControlSkin; import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyEvent; +import javafx.scene.paint.Color; +import javafx.scene.paint.Paint; +import javafx.scene.text.TextFlow; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Objects; /** @@ -32,28 +48,14 @@ public class TextView extends Control { public TextView() { getStyleClass().add("text-view"); + setFocusTraversable(false); + getProperties().addListener((MapChangeListener) change -> { if (change.getKey().equals("selected.text")) { selectedText.set((String) change.getValueAdded()); } }); - addEventHandler(KeyEvent.KEY_PRESSED, evt -> { - if (KeyCodeCombination.keyCombination("shortcut+c").match(evt)) { - copySelection(); - } - }); - - focusWithinProperty().addListener(it -> { - ContextMenu contextMenu = getContextMenu(); - if (contextMenu != null && contextMenu.isShowing()) { - return; - } - if (!isFocusWithin()) { - clearSelection(); - } - }); - setOnContextMenuRequested(evt -> { if (getContextMenu() == null) { MenuItem copySelectionItem = new MenuItem("Copy Selection"); @@ -111,19 +113,13 @@ public void copyAll() { } private void doCopy(String text) { + System.out.println("copying selection to clipboard: " + text); Clipboard clipboard = Clipboard.getSystemClipboard(); ClipboardContent content = new ClipboardContent(); content.putString(text); clipboard.setContent(content); } - /** - * Removes the current selection. - */ - public final void clearSelection() { - selectedText.set(null); - } - // text private final StringProperty text = new SimpleStringProperty(this, "text"); @@ -161,4 +157,186 @@ public final String getSelectedText() { public final ReadOnlyStringProperty selectedTextProperty() { return selectedText.getReadOnlyProperty(); } + + // highlight fill + + /** + * The fill to use for the text when highlighted. + */ + private final ObjectProperty highlightFill = new StyleableObjectProperty<>(Color.DODGERBLUE) { + + @Override + public Object getBean() { + return this; + } + + @Override + public String getName() { + return "highlightFill"; + } + + @Override + public CssMetaData getCssMetaData() { + return StyleableProperties.HIGHLIGHT_FILL; + } + }; + + /** + * The fill {@code Paint} used for the background of selected text. + * + * @param value the highlight fill + */ + public final void setHighlightFill(Paint value) { + highlightFill.set(value); + } + + public final Paint getHighlightFill() { + return highlightFill.get(); + } + + public final ObjectProperty highlightFillProperty() { + return highlightFill; + } + + // highlight stroke + + /** + * The fill to use for the text when highlighted. + */ + private final ObjectProperty highlightStroke = new StyleableObjectProperty<>(Color.TRANSPARENT) { + + @Override + public Object getBean() { + return this; + } + + @Override + public String getName() { + return "highlightStroke"; + } + + @Override + public CssMetaData getCssMetaData() { + return StyleableProperties.HIGHLIGHT_STROKE; + } + }; + + /** + * The fill {@code Paint} used for the background of selected text. + * + * @param value the highlight fill + */ + public final void setHighlightStroke(Paint value) { + highlightStroke.set(value); + } + + public final Paint getHighlightStroke() { + return highlightStroke.get(); + } + + public final ObjectProperty highlightStrokeProperty() { + return highlightStroke; + } + + // highlight text fill + + /** + * The fill {@code Paint} used for the foreground of selected text. + */ + private final ObjectProperty highlightTextFill = new StyleableObjectProperty<>(Color.WHITE) { + + @Override + public Object getBean() { + return this; + } + + @Override + public String getName() { + return "highlightTextFill"; + } + + @Override + public CssMetaData getCssMetaData() { + return StyleableProperties.HIGHLIGHT_TEXT_FILL; + } + }; + + /** + * The fill {@code Paint} used for the foreground of selected text. + * + * @param value the highlight text fill + */ + public final void setHighlightTextFill(Paint value) { + highlightTextFill.set(value); + } + + public final Paint getHighlightTextFill() { + return highlightTextFill.get(); + } + + public final ObjectProperty highlightTextFillProperty() { + return highlightTextFill; + } + + private static class StyleableProperties { + + private static final CssMetaData HIGHLIGHT_TEXT_FILL = new CssMetaData<>( + "-fx-highlight-text-fill", PaintConverter.getInstance(), Color.RED) { + + @Override + public boolean isSettable(TextView n) { + return !n.highlightTextFill.isBound(); + } + + @Override + @SuppressWarnings("unchecked") + public StyleableProperty getStyleableProperty(TextView n) { + return (StyleableProperty) n.highlightTextFill; + } + }; + + private static final CssMetaData HIGHLIGHT_FILL = new CssMetaData<>( + "-fx-highlight-fill", PaintConverter.getInstance(), Color.OLIVE + ) { + + @Override + public boolean isSettable(TextView c) { + return c.highlightFill.isBound(); + } + + @Override + public StyleableProperty getStyleableProperty(TextView c) { + return (StyleableProperty) c.highlightFillProperty(); + } + }; + + private static final CssMetaData HIGHLIGHT_STROKE = new CssMetaData<>( + "-fx-highlight-stroke", PaintConverter.getInstance(), Color.RED + ) { + + @Override + public boolean isSettable(TextView c) { + return !c.highlightStroke.isBound(); + } + + @Override + public StyleableProperty getStyleableProperty(TextView c) { + return (StyleableProperty) c.highlightStroke; + } + }; + + private static final List> STYLEABLES; + + static { + final List> styleables = new ArrayList<>(TextFlow.getClassCssMetaData()); + styleables.add(HIGHLIGHT_FILL); + styleables.add(HIGHLIGHT_STROKE); + styleables.add(HIGHLIGHT_TEXT_FILL); + STYLEABLES = Collections.unmodifiableList(styleables); + } + } + + public static List> getClassCssMetaData() { + return StyleableProperties.STYLEABLES; + } } \ No newline at end of file diff --git a/gemsfx/src/main/java/com/dlsc/gemsfx/skins/TextViewSkin.java b/gemsfx/src/main/java/com/dlsc/gemsfx/skins/TextViewSkin.java index 50cd9423..3fc16c32 100644 --- a/gemsfx/src/main/java/com/dlsc/gemsfx/skins/TextViewSkin.java +++ b/gemsfx/src/main/java/com/dlsc/gemsfx/skins/TextViewSkin.java @@ -1,326 +1,185 @@ package com.dlsc.gemsfx.skins; import com.dlsc.gemsfx.TextView; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; -import javafx.css.PseudoClass; -import javafx.event.EventHandler; -import javafx.geometry.Rectangle2D; +import javafx.geometry.Point2D; +import javafx.scene.Cursor; +import javafx.scene.Node; +import javafx.scene.control.ContextMenu; import javafx.scene.control.SkinBase; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.Pane; +import javafx.scene.input.KeyCodeCombination; +import javafx.scene.input.KeyEvent; +import javafx.scene.input.MouseButton; import javafx.scene.layout.Region; +import javafx.scene.shape.Path; +import javafx.scene.shape.PathElement; +import javafx.scene.text.HitInfo; import javafx.scene.text.Text; import javafx.scene.text.TextFlow; +import org.apache.commons.lang3.StringUtils; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; - -/** - * A custom label Skin that allows you to select a text to be copied to the clipboard - */ public class TextViewSkin extends SkinBase { - private static final PseudoClass SELECTED = PseudoClass.getPseudoClass("selected"); - - /** - * Panel that will be used as for rendering the actual text of the view. - */ - private final TextFlow textsContainer = new TextFlow(); - - /** - * Lays out the selection nodes. - */ - private final Pane selectionContainer = new Pane(); - - /** - * Arrangement of the words contained in the text - */ - private final List texts = new ArrayList<>(); + private final SelectableText selectableText = new SelectableText(); - /** - * Set of indices of the words that have been selected - */ - private final Set selectedIndices = new TreeSet<>(); - - /** - * String property used to automatically change the text of the TextView - */ - private final StringProperty selectedText = new SimpleStringProperty(); - - /** - * Instances a new Custom Label Skin, value TextView - */ public TextViewSkin(TextView control) { super(control); - textsContainer.getStyleClass().add("text-container"); - selectionContainer.getStyleClass().add("selection-container"); - - selectedText.addListener(obs -> control.getProperties().put("selected.text", selectedText.get())); - - SelectionHandler selectionHandler = new SelectionHandler(); - control.setOnMouseDragged(selectionHandler); - control.setOnMousePressed(selectionHandler); - control.setOnMouseReleased(selectionHandler); - control.textProperty().addListener(obs -> buildView(control.getText())); - control.selectedTextProperty().addListener(obs -> { - if (control.getSelectedText() == null) { - clearSelection(); + control.addEventHandler(KeyEvent.KEY_PRESSED, evt -> { + if (KeyCodeCombination.keyCombination("shortcut+c").match(evt)) { + control.copySelection(); + } else if (KeyCodeCombination.keyCombination("shortcut+a").match(evt)) { + selectableText.selectAll(); + } else if (KeyCodeCombination.keyCombination("backspace").match(evt)) { + selectableText.removeSelection("shortcut"); } }); - buildView(control.getText()); - - /* - * Defines the overlays of the main panels - */ - selectionContainer.toBack(); - textsContainer.toFront(); + getChildren().setAll(selectableText); - control.widthProperty().addListener((obs, oldV, newV) -> buildSelection()); - control.heightProperty().addListener((obs, oldV, newV) -> buildSelection()); + control.focusedProperty().addListener(it -> { + ContextMenu contextMenu = control.getContextMenu(); + if (contextMenu != null && contextMenu.isShowing()) { + return; + } - getChildren().addAll(selectionContainer, textsContainer); + if (!control.isFocused()) { + selectableText.removeSelection("focus lost"); + } + }); } @Override protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) { - return textsContainer.prefHeight(width - leftInset - rightInset); + return selectableText.prefHeight(width - leftInset - rightInset); } - /** - * Separates a text into words and indexes - */ - private void buildView(String text) { - texts.clear(); - - if (text != null && !text.isEmpty()) { - - StringBuilder spaces = new StringBuilder(); - StringBuilder word = new StringBuilder(); - StringBuilder special = new StringBuilder(); - - for (int i = 0; i < text.length(); i++) { - char character = text.charAt(i); - if (isSpaceCharacter(character)) { - spaces.append(character); - if (!word.isEmpty()) { - addText(word); - } else if (!special.isEmpty()) { - addText(special); - } - } else if (isSpecialCharacter(character)) { - special.append(character); - if (!word.isEmpty()) { - addText(word); - } else if (!spaces.isEmpty()) { - addText(spaces); - } - } else if (isLineBreakCharacter(character)) { - if (!word.isEmpty()) { - addText(word); - } else if (!spaces.isEmpty()) { - addText(spaces); - } else if (!special.isEmpty()) { - addText(special); - } - StringBuilder line = new StringBuilder(); - line.append(character); - addText(line); - } else { - word.append(character); - if (!spaces.isEmpty()) { - addText(spaces); - } else if (!special.isEmpty()) { - addText(special); - } - } - } - if (!word.isEmpty()) { - addText(word); - } else if (!special.isEmpty()) { - addText(special); - } else if (!spaces.isEmpty()) { - addText(spaces); - } - } + private final class SelectableText extends TextFlow { - textsContainer.getChildren().setAll(texts); - clearSelection(); - } + private final Path wrappingPath = new Path(); - private void addText(StringBuilder text) { - Text textNode = new Text(); - textNode.setText(text.toString()); - textNode.getStyleClass().add("text"); - texts.add(textNode); - text.setLength(0); - } + private int mouseDragStartPos = -1; + private int selectionStartPos = -1; + private int selectionEndPos = -1; + private final Text text = new Text(); - /** - * Iterate over the indices to separate them into regions, which overlap the text - */ - private void buildSelection() { - StringBuilder selection = new StringBuilder(); - Map> rectangles = new HashMap<>(); - List regions = new ArrayList<>(); + public SelectableText() { + super(); - texts.forEach(t -> t.pseudoClassStateChanged(SELECTED, false)); + setCursor(Cursor.TEXT); + setPrefWidth(Region.USE_PREF_SIZE); - for (int index : selectedIndices) { - if (index < 0 || index > texts.size()) { - continue; - } + text.textProperty().bind(getSkinnable().textProperty()); + text.getStyleClass().add("text"); - Text text = texts.get(index); - text.pseudoClassStateChanged(SELECTED, true); - selection.append(text.getText()); + setText(text); - double x = text.getLayoutX(); - double y = text.getLayoutY(); - double w = text.getBoundsInLocal().getWidth(); - double h = text.getBoundsInLocal().getHeight(); + wrappingPath.setManaged(false); + wrappingPath.fillProperty().bind(getSkinnable().highlightFillProperty()); + wrappingPath.strokeProperty().bind(getSkinnable().highlightStrokeProperty()); - String txt = text.getText(); - char[] chars = txt.toCharArray(); + getStyleClass().add("selectable-text"); + initListeners(); + } - if (isLineBreakCharacter(chars[0])) { - w = 5; + public void selectAll() { + String t = text.getText(); + if (StringUtils.isNotBlank(t)) { + selectionStartPos = 0; + selectionEndPos = t.length(); + performSelection(); } - - List temp = rectangles.computeIfAbsent(y, ay -> new ArrayList<>()); - temp.add(new Rectangle2D(x, y, w, h)); } - for (Double y : rectangles.keySet()) { - List temp = rectangles.get(y); - - double x = Double.MAX_VALUE; - double height = 0; - double width = 0; + private void initListeners() { + setOnMousePressed(e -> { + getSkinnable().requestFocus(); - for (Rectangle2D r : temp) { - if (r.getMinX() < x) { - x = r.getMinX(); + if (!e.isPrimaryButtonDown() || e.isPopupTrigger()) { + return; } - if (r.getHeight() > height) { - height = r.getHeight(); - } + removeSelection("mouse pressed"); - width += r.getWidth(); - } + HitInfo hit = hitTest(new Point2D(e.getX(), e.getY())); - Region region = new Region(); - region.setLayoutY(y); - region.setLayoutX(x); - region.setPrefWidth(width); - region.setPrefHeight(height); - region.getStyleClass().add("selection"); + if (e.isPrimaryButtonDown() && e.getClickCount() == 2) { + // TODO: Double-click selection + return; + } else { + mouseDragStartPos = hit.getCharIndex(); + } + }); - regions.add(region); - } + setOnMouseDragged(e -> { + if (e.isStillSincePress() || !e.isPrimaryButtonDown()) { + return; + } + HitInfo hit = hitTest(new Point2D(e.getX(), e.getY())); - selectedText.set(selection.isEmpty() ? null : selection.toString()); - selectionContainer.getChildren().setAll(regions); - } + selectionStartPos = Math.min(mouseDragStartPos, hit.getCharIndex()); + selectionEndPos = Math.max(mouseDragStartPos, hit.getCharIndex()); - private void clearSelection() { - selectedIndices.clear(); - buildSelection(); - } + performSelection(); + }); - private static boolean isSpaceCharacter(char character) { - return character == ' '; - } + setOnMouseReleased(e -> { + if (!e.getButton().equals(MouseButton.PRIMARY) || e.isPopupTrigger()) { + return; + } + getSkinnable().getProperties().put("selected.text", getSelectedTextAsString()); + mouseDragStartPos = -1; + }); - private static boolean isSpecialCharacter(char character) { - return character == '.' || character == ',' || character == ';' || character == ':'; - } + widthProperty().addListener((obs, old, val) -> removeSelection("width changed")); + heightProperty().addListener((obs, old, val) -> removeSelection("height changed")); + } - private static boolean isLineBreakCharacter(char character) { - return character == '\n'; - } + private void performSelection() { + text.setSelectionStart(selectionStartPos); + text.setSelectionEnd(selectionEndPos); - /** - * Anonymous class to control mouse events - */ - private class SelectionHandler implements EventHandler { - - private Integer firstIndex; - - @Override - public void handle(MouseEvent evt) { - if (evt.getEventType() == MouseEvent.MOUSE_DRAGGED) { - handleMouseDragged(evt); - } else if (evt.getEventType() == MouseEvent.MOUSE_PRESSED) { - handleMousePressed(evt); - } else if (evt.getEventType() == MouseEvent.MOUSE_RELEASED) { - handleMouseReleased(evt); - } + PathElement[] selectionRange = rangeShape(selectionStartPos, selectionEndPos); + wrappingPath.getElements().setAll(selectionRange); + getSkinnable().getProperties().put("selected.text", getSelectedTextAsString()); } - private void handleMouseDragged(MouseEvent evt) { - if (!evt.isPrimaryButtonDown() || evt.isPopupTrigger()) { - return; - } - if (evt.getPickResult().getIntersectedNode() instanceof Text text) { - addSelectedIndex(texts.indexOf(text)); + public void setText(Text text) { + if (text != null) { + text.setSelectionFill(getSkinnable().getHighlightTextFill()); } + getChildren().setAll(wrappingPath); + getChildren().addAll(text); } - private void handleMousePressed(MouseEvent evt) { - if (!evt.isPrimaryButtonDown() || evt.isPopupTrigger()) { - return; - } - getSkinnable().requestFocus(); - - if (evt.getPickResult().getIntersectedNode() instanceof Text text) { - int index = texts.indexOf(text); - if (index >= 0) { - firstIndex = index; - } - - } - - selectedIndices.clear(); - buildSelection(); + public void clear() { + getChildren().setAll(wrappingPath); } - private void handleMouseReleased(MouseEvent evt) { - if (!evt.isPrimaryButtonDown() || evt.isPopupTrigger()) { - return; - } - firstIndex = null; + public String getSelectedTextAsString() { + StringBuilder content = getTextFlowContentAsString(); + return selectionStartPos >= 0 && selectionEndPos > selectionStartPos + ? content.substring(selectionStartPos, selectionEndPos) + : null; } - /** - * From an initial and final index, creates a route of the intermediate indexes to avoid skipping indexes - */ - private void addSelectedIndex(int index) { - if (index >= 0 && index < texts.size() && firstIndex != null) { - selectedIndices.clear(); - selectedIndices.add(index); - - if (firstIndex > index) { - for (int i = firstIndex; i > index; i--) { - selectedIndices.add(i); - } - } - else { - for (int i = firstIndex; i < index; i++) { - selectedIndices.add(i); - } + private StringBuilder getTextFlowContentAsString() { + StringBuilder sb = new StringBuilder(); + for (Node node : getChildren()) { + if (node instanceof Text t) { + sb.append(t.getText()); } - - buildSelection(); } + return sb; + } + + private void removeSelection(String reason) { + getSkinnable().getProperties().put("selected.text", null); + selectionStartPos = -1; + selectionEndPos = -1; + text.setSelectionStart(selectionStartPos); + text.setSelectionEnd(selectionEndPos); + wrappingPath.getElements().clear(); } } } \ No newline at end of file diff --git a/gemsfx/src/main/resources/com/dlsc/gemsfx/text-view.css b/gemsfx/src/main/resources/com/dlsc/gemsfx/text-view.css index 7d3f73ac..79921938 100644 --- a/gemsfx/src/main/resources/com/dlsc/gemsfx/text-view.css +++ b/gemsfx/src/main/resources/com/dlsc/gemsfx/text-view.css @@ -1,23 +1,5 @@ .text-view { - -} - -.text-view > .text-container { - -} - -.text-view > .text-container > .text { -} - -.text-view > .text-container > .text:selected { - -fx-background: -fx-selection-bar; - -fx-fill: -fx-selection-bar-text; -} - -.text-view > .selection-container { - -} - -.text-view > .selection-container > .selection { - -fx-background-color: -fx-selection-bar; -} + -fx-highlight-text-fill: white; + -fx-highlight-fill: red; + -fx-highlight-stroke: orange; +} \ No newline at end of file