Skip to content

Commit

Permalink
8307363: TextFlow.underlineShape()
Browse files Browse the repository at this point in the history
Reviewed-by: prr, kcr
  • Loading branch information
Andy Goryachev committed May 11, 2023
1 parent d8da8d8 commit 604fc26
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 11 deletions.
Expand Up @@ -231,6 +231,18 @@ public final PathElement[] rangeShape(int start, int end) {
return getRange(start, end, TextLayout.TYPE_TEXT);
}

/**
* Returns the shape for the underline in local coordinates.
*
* @param start the beginning character index for the range
* @param end the end character index (non-inclusive) for the range
* @return an array of {@code PathElement} which can be used to create a {@code Shape}
* @since 21
*/
public final PathElement[] underlineShape(int start, int end) {
return getRange(start, end, TextLayout.TYPE_UNDERLINE);
}

@Override
public boolean usesMirroring() {
return false;
Expand Down
Expand Up @@ -58,11 +58,13 @@ public class TextFlowPage extends TestPaneBase {
protected final FontSelector fontSelector;
protected final CheckBox showChars;
protected final CheckBox showCaretPath;
protected final CheckBox underline;
protected final TextFlow control;
protected final Label pickResult;
protected final Label hitInfo;
protected final Label hitInfo2;
protected final Path caretPath;
protected final Path underlinePath;
private static final String INLINE = "$INLINE";
private static final String RICH_TEXT = "$RICH";

Expand All @@ -83,6 +85,12 @@ public TextFlowPage() {
caretPath.setStroke(Color.RED);
caretPath.setManaged(false);

underlinePath = new Path();
underlinePath.setStrokeWidth(1);
underlinePath.setStroke(Color.GREEN);
underlinePath.setFill(Color.YELLOW);
underlinePath.setManaged(false);

textSelector = TextSelector.fromPairs(
"textSelector",
(t) -> updateControl(),
Expand All @@ -104,7 +112,13 @@ public TextFlowPage() {
showCaretPath = new CheckBox("show caret path");
showCaretPath.setId("showCaretPath");
showCaretPath.selectedProperty().addListener((p) -> {
updateControl();
updateCaret();
});

underline = new CheckBox("underline shape");
underline.setId("underline");
underline.selectedProperty().addListener((p) -> {
updateUnderline();
});

OptionPane p = new OptionPane();
Expand All @@ -116,6 +130,7 @@ public TextFlowPage() {
p.option(fontSelector.sizeNode());
p.option(showChars);
p.option(showCaretPath);
p.option(underline);
p.option(new Separator(Orientation.HORIZONTAL));
p.label("Pick Result:");
p.option(pickResult);
Expand All @@ -142,16 +157,12 @@ protected void updateControl() {
control.getChildren().add(g);
}

if (showCaretPath.isSelected()) {
caretPath.getElements().clear();
control.getChildren().add(caretPath);
caretPath.getElements().clear();
underlinePath.getElements().clear();
control.getChildren().addAll(underlinePath, caretPath);

int len = computeTextLength(control);
for (int i = 0; i < len; i++) {
PathElement[] es = control.caretShape(i, true);
caretPath.getElements().addAll(es);
}
}
updateCaret();
updateUnderline();
}

/** TextFlow.getTextLength() */
Expand All @@ -160,8 +171,10 @@ private static int computeTextLength(TextFlow f) {
for (Node n: f.getChildrenUnmodifiable()) {
if (n instanceof Text t) {
len += t.getText().length();
} else {
// treat any other nodes as having length 1
len++;
}
// embedded nodes do not have an associated text
}
return len;
}
Expand Down Expand Up @@ -221,4 +234,26 @@ protected void handleMouseEvent(MouseEvent ev) {
HitInfo h = control.hitTest(p);
hitInfo.setText(String.valueOf(h));
}

protected void updateUnderline() {
if (underline.isSelected()) {
int len = computeTextLength(control);
PathElement[] es = control.underlineShape(0, len);
underlinePath.getElements().addAll(es);
} else {
underlinePath.getElements().clear();
}
}

protected void updateCaret() {
if (showCaretPath.isSelected()) {
int len = computeTextLength(control);
for (int i = 0; i < len; i++) {
PathElement[] es = control.caretShape(i, true);
caretPath.getElements().addAll(es);
}
} else {
caretPath.getElements().clear();
}
}
}
@@ -0,0 +1,84 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package test.javafx.scene.text;

import java.util.concurrent.CountDownLatch;
import javafx.geometry.Bounds;
import javafx.scene.shape.Path;
import javafx.scene.shape.PathElement;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import test.util.Util;

/**
* Tests TextFlow Node
*/
public class TextFlowNodeTest {
@BeforeClass
public static void initFX() {
CountDownLatch startupLatch = new CountDownLatch(1);
Util.startup(startupLatch, startupLatch::countDown);
}

@Test
public void testUnderlineShape() {
Text t1 = new Text("one ");
t1.setFont(new Font("Monospaced Regular", 16));
Text t2 = new Text("two.");
TextFlow f = new TextFlow(t1, t2);

// underline 0,0 must be empty
PathElement[] p = f.underlineShape(0, 0);
Assert.assertNotNull(p);
Assert.assertEquals(0, p.length);

// underline 1,0 .. 1,len must increase monotonically
int len = t1.getText().length() + t2.getText().length();
double w = 0.0;
for (int i = 1; i < len; i++) {
p = f.underlineShape(0, i);
Assert.assertNotNull(p);

// width must increase
Bounds b = new Path(p).getBoundsInLocal();
Assert.assertTrue(b.getWidth() > w);
w = b.getWidth();

// test height greater than zero
Assert.assertTrue(b.getHeight() > 0.0);
}

// 0,1000 same as 0,len
Bounds b1 = new Path(f.underlineShape(0, len)).getBoundsInLocal();
Bounds b2 = new Path(f.underlineShape(0, 1000)).getBoundsInLocal();
Assert.assertEquals(b1, b2);
Assert.assertTrue(b1.getHeight() > 0.0);
}
}

3 comments on commit 604fc26

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevinrushforth
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/tag 21+17

@openjdk
Copy link

@openjdk openjdk bot commented on 604fc26 May 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevinrushforth The tag 21+17 was successfully created.

Please sign in to comment.