Skip to content

Commit

Permalink
Reworked the TextView to provide a more fine-grained approach to sele…
Browse files Browse the repository at this point in the history
…cting text.
  • Loading branch information
dlemmermann committed Aug 19, 2024
1 parent 90194ef commit 16d3e29
Show file tree
Hide file tree
Showing 4 changed files with 332 additions and 315 deletions.
14 changes: 6 additions & 8 deletions gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/TextViewApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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());

Expand All @@ -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");
Expand Down
224 changes: 201 additions & 23 deletions gemsfx/src/main/java/com/dlsc/gemsfx/TextView.java
Original file line number Diff line number Diff line change
@@ -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;

/**
Expand All @@ -32,28 +48,14 @@ public class TextView extends Control {
public TextView() {
getStyleClass().add("text-view");

setFocusTraversable(false);

getProperties().addListener((MapChangeListener<Object, Object>) 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");
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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<Paint> highlightFill = new StyleableObjectProperty<>(Color.DODGERBLUE) {

@Override
public Object getBean() {
return this;
}

@Override
public String getName() {
return "highlightFill";
}

@Override
public CssMetaData<TextView, Paint> 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<Paint> highlightFillProperty() {
return highlightFill;
}

// highlight stroke

/**
* The fill to use for the text when highlighted.
*/
private final ObjectProperty<Paint> highlightStroke = new StyleableObjectProperty<>(Color.TRANSPARENT) {

@Override
public Object getBean() {
return this;
}

@Override
public String getName() {
return "highlightStroke";
}

@Override
public CssMetaData<TextView, Paint> 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<Paint> highlightStrokeProperty() {
return highlightStroke;
}

// highlight text fill

/**
* The fill {@code Paint} used for the foreground of selected text.
*/
private final ObjectProperty<Paint> highlightTextFill = new StyleableObjectProperty<>(Color.WHITE) {

@Override
public Object getBean() {
return this;
}

@Override
public String getName() {
return "highlightTextFill";
}

@Override
public CssMetaData<TextView, Paint> 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<Paint> highlightTextFillProperty() {
return highlightTextFill;
}

private static class StyleableProperties {

private static final CssMetaData<TextView, Paint> 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<Paint> getStyleableProperty(TextView n) {
return (StyleableProperty<Paint>) n.highlightTextFill;
}
};

private static final CssMetaData<TextView, Paint> HIGHLIGHT_FILL = new CssMetaData<>(
"-fx-highlight-fill", PaintConverter.getInstance(), Color.OLIVE
) {

@Override
public boolean isSettable(TextView c) {
return c.highlightFill.isBound();
}

@Override
public StyleableProperty<Paint> getStyleableProperty(TextView c) {
return (StyleableProperty<Paint>) c.highlightFillProperty();
}
};

private static final CssMetaData<TextView, Paint> HIGHLIGHT_STROKE = new CssMetaData<>(
"-fx-highlight-stroke", PaintConverter.getInstance(), Color.RED
) {

@Override
public boolean isSettable(TextView c) {
return !c.highlightStroke.isBound();
}

@Override
public StyleableProperty<Paint> getStyleableProperty(TextView c) {
return (StyleableProperty<Paint>) c.highlightStroke;
}
};

private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;

static {
final List<CssMetaData<? extends Styleable, ?>> 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<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
return StyleableProperties.STYLEABLES;
}
}
Loading

0 comments on commit 16d3e29

Please sign in to comment.