Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8341670: [Text,TextFlow] Public API for Text Layout Info #1596

Open
wants to merge 41 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
285db28
draft api
andy-goryachev-oracle Oct 8, 2024
43ac0e1
bounds
andy-goryachev-oracle Oct 8, 2024
21dbe6c
review comments
andy-goryachev-oracle Oct 9, 2024
04c4abb
javadoc
andy-goryachev-oracle Oct 9, 2024
6869c76
clarify
andy-goryachev-oracle Oct 9, 2024
2009a7e
convert to wrapper
andy-goryachev-oracle Oct 9, 2024
6b7b079
Merge remote-tracking branch 'origin/master' into ag.text.layout.api
andy-goryachev-oracle Oct 11, 2024
c2e26d5
text line info
andy-goryachev-oracle Oct 11, 2024
88e9ee1
caret info
andy-goryachev-oracle Oct 14, 2024
17dcdd4
whitespace
andy-goryachev-oracle Oct 14, 2024
fd84f30
tests
andy-goryachev-oracle Oct 14, 2024
466bba7
remove line spacing
andy-goryachev-oracle Oct 14, 2024
5ab4f47
Merge remote-tracking branch 'origin/master' into ag.text.layout.api
andy-goryachev-oracle Oct 14, 2024
eb99008
Merge remote-tracking branch 'origin/master' into ag.text.layout.api
andy-goryachev-oracle Oct 24, 2024
6776d97
review comments
andy-goryachev-oracle Oct 25, 2024
9b1f99a
Merge remote-tracking branch 'origin/master' into ag.text.layout.api
andy-goryachev-oracle Oct 30, 2024
4f4e659
include line spacing
andy-goryachev-oracle Oct 31, 2024
dd34810
Merge remote-tracking branch 'origin/master' into ag.text.layout.api
andy-goryachev-oracle Oct 31, 2024
a716e57
Merge remote-tracking branch 'origin/master' into ag.text.layout.api
andy-goryachev-oracle Nov 1, 2024
75b171f
caret and range
andy-goryachev-oracle Nov 1, 2024
029bbe6
for text
andy-goryachev-oracle Nov 1, 2024
3925c02
Merge remote-tracking branch 'origin/master' into ag.text.layout.api
andy-goryachev-oracle Nov 4, 2024
e139484
Merge remote-tracking branch 'origin/master' into ag.text.layout.api
andy-goryachev-oracle Nov 6, 2024
8a5b904
Merge remote-tracking branch 'origin/master' into ag.text.layout.api
andy-goryachev-oracle Nov 8, 2024
a0e59fb
Merge remote-tracking branch 'origin/master' into ag.text.layout.api
andy-goryachev-oracle Nov 15, 2024
3c2976b
line spacing
andy-goryachev-oracle Nov 15, 2024
5076f36
shorter array
andy-goryachev-oracle Nov 18, 2024
4479f0b
text flow test
andy-goryachev-oracle Nov 19, 2024
c05c790
text layout test
andy-goryachev-oracle Nov 19, 2024
7c86978
note
andy-goryachev-oracle Nov 19, 2024
366ae4b
coordinates
andy-goryachev-oracle Nov 19, 2024
d31eb2b
Merge remote-tracking branch 'origin/master' into ag.text.layout.api
andy-goryachev-oracle Nov 19, 2024
7ea4e3b
Merge remote-tracking branch 'origin/master' into ag.text.layout.api
andy-goryachev-oracle Nov 22, 2024
944bd0d
segments
andy-goryachev-oracle Nov 22, 2024
1dd2b0c
Merge branch 'master' into ag.text.layout.api
andy-goryachev-oracle Dec 4, 2024
2a3a754
Merge remote-tracking branch 'origin/master' into ag.text.layout.api
andy-goryachev-oracle Dec 10, 2024
c1c46ab
Merge remote-tracking branch 'origin/master' into ag.text.layout.api
andy-goryachev-oracle Dec 20, 2024
7d656df
Merge branch 'master' into ag.text.layout.api
andy-goryachev-oracle Jan 16, 2025
0ab5890
25 25
andy-goryachev-oracle Jan 16, 2025
d81f65d
Merge remote-tracking branch 'origin/master' into ag.text.layout.api
andy-goryachev-oracle Feb 5, 2025
d688d74
review comments
andy-goryachev-oracle Feb 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,10 @@

package com.sun.javafx.scene.text;

import javafx.scene.shape.PathElement;
import java.util.Objects;
import com.sun.javafx.geom.BaseBounds;
import com.sun.javafx.geom.Shape;

import java.util.Objects;

public interface TextLayout {

/* Internal flags Flags */
Expand Down Expand Up @@ -79,6 +77,12 @@ public interface TextLayout {

public static final int DEFAULT_TAB_SIZE = 8;

/** Callback to be called for each rectangular shape */
@FunctionalInterface
public static interface GeometryCallback {
public void addRectangle(float left, float top, float right, float bottom);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I understand that you use float here because all the calculations in PrismTextLayout::getRange use floats (from TextRun).

However, the calculations down the line to generate the Rectangle2D mix floats and doubles without any casting (TextUtils::getRange with implicit casts from double to float, PrismLayoutInfo::getGeometry with float and double sums).

Do you think we could use doubles here instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the use of float in this interface is correct, but the code in TextUtils::getRange is bad, and i should feel bad. good catch!

}

public static class Hit {
int charIndex;
int insertionIndex;
Expand Down Expand Up @@ -233,8 +237,36 @@ public String toString() {
*/
public Hit getHitInfo(float x, float y);

public PathElement[] getCaretShape(int offset, boolean isLeading,
float x, float y);
public PathElement[] getRange(int start, int end, int type,
float x, float y);
/**
* Queries the caret geometry and associated information at the specified text position.
* <p>
* The geometry is encoded as a sequence of coordinates using two different formats,
* depending on whether the caret is drawn as a single vertical line or as two separate
* lines (a "split" caret).
* <ul>
* <li>{@code x, ymin, ymax} - corresponds to a single line from (x, ymin) tp (x, ymax)
* <li>{@code x, ymin, y2, x2, ymax} - corresponds to a split caret drawn as two lines, the first line
* drawn from (x,ymin) to (x, y2), the second line drawn from (x2, y2) to (x2, ymax).
* </ul>
*
* @param offset the character offset
* @param leading whether the caret is biased on the leading edge of the character
* @return the caret geometry
*/
public float[] getCaretInf(int offset, boolean leading);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this a typo or is there a reason for naming this method getCaretInf instead of getCaretInfo?


/**
* Queries the range geometry of the range of text within the text layout for one of the three possible types:
* <ul>
* <li>{@link #TYPE_STRIKETHROUGH} - strike-through shape
* <li>{@link #TYPE_TEXT} - text selection shape
* <li>{@link #TYPE_UNDERLINE} - underline shape
* </ul>
*
* @param start the start offset
* @param end the end offset
* @param the type of the geometry
Copy link
Collaborator

Choose a reason for hiding this comment

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

missing typeafter @param

Copy link
Contributor Author

Choose a reason for hiding this comment

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

eagle eye!

* @param client the callback to invoke for each rectangular shape
*/
public void getRange(int start, int end, int type, GeometryCallback client);
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ public interface TextLine {
public GlyphList[] getRuns();

/**
* Returns metrics information about the line as follow:
* Returns metrics information about the line as follows:
*
* bounds().getWidth() - the width of the line.
* The width for the line is sum of all run's width in the line, it is not
* affect by any wrapping width but it will include any changes caused by
* affected by any wrapping width but it will include any changes caused by
* justification.
*
* bounds().getHeight() - the height of the line.
Expand Down Expand Up @@ -73,7 +73,7 @@ public interface TextLine {
public int getStart();

/**
* Returns the line length in character.
* Returns the line length in characters.
*/
public int getLength();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2024, 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 com.sun.javafx.text;

import javafx.scene.text.CaretInfo;

/**
* CaretInfo as reported by the PrismTextLayout.
*/
public final class PrismCaretInfo extends CaretInfo {
private final double[][] lines;

public PrismCaretInfo(double[][] lines) {
this.lines = lines;
}

@Override
public int getLineCount() {
return lines.length;
}

@Override
public double[] getLineAt(int index) {
return lines[index];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* Copyright (c) 2024, 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 com.sun.javafx.text;

import java.util.ArrayList;
import java.util.List;
import javafx.geometry.Rectangle2D;
import javafx.scene.text.CaretInfo;
import javafx.scene.text.LayoutInfo;
import javafx.scene.text.TextLineInfo;
import com.sun.javafx.scene.text.TextLayout;
import com.sun.javafx.scene.text.TextLine;

/**
* Layout information as reported by PrismLayout.
*/
public final class PrismLayoutInfo extends LayoutInfo {
private final TextLayout layout;

public PrismLayoutInfo(TextLayout layout) {
this.layout = layout;
}

@Override
public Rectangle2D getBounds() {
return TextUtils.toRectangle2D(layout.getBounds());
}

@Override
public int getTextLineCount() {
return layout.getLines().length;
}

@Override
public List<TextLineInfo> getTextLines() {
TextLine[] lines = layout.getLines();
int sz = lines.length;
ArrayList<TextLineInfo> rv = new ArrayList<>(sz);
for (int i = 0; i < sz; i++) {
rv.add(TextUtils.toLineInfo(lines[i]));
}
return rv;
}

@Override
public TextLineInfo getTextLine(int index) {
return TextUtils.toLineInfo(layout.getLines()[index]);
}

@Override
public List<Rectangle2D> selectionShape(int start, int end) {
return getGeometry(start, end, TextLayout.TYPE_TEXT);
}

@Override
public List<Rectangle2D> strikeThroughShape(int start, int end) {
return getGeometry(start, end, TextLayout.TYPE_STRIKETHROUGH);
}

@Override
public List<Rectangle2D> underlineShape(int start, int end) {
return getGeometry(start, end, TextLayout.TYPE_UNDERLINE);
}

private List<Rectangle2D> getGeometry(int start, int end, int type) {
ArrayList<Rectangle2D> rv = new ArrayList<>();
// TODO padding/border JDK-8341438?
layout.getRange(start, end, type, (left, top, right, bottom) -> {
if (left < right) {
rv.add(new Rectangle2D(left, top, right - left, bottom - top));
} else {
rv.add(new Rectangle2D(right, top, left - right, bottom - top));
}
});
return rv;
}

private TextLine line(int ix) {
return layout.getLines()[ix];
Copy link
Collaborator

Choose a reason for hiding this comment

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

bound check? In any case, this private method is not used.

}

@Override
public CaretInfo caretInfo(int charIndex, boolean leading) {
float[] c = layout.getCaretInf(charIndex, leading);

// TODO padding/border JDK-8341438?
double[][] lines;
if (c.length == 3) {
// {x, ymin, ymax} - corresponds to a single line from (x, ymin) tp (x, ymax)
lines = new double[][] {
new double[] {
c[0], c[1], c[2]
}
};
} else {
// {x, y, y2, x2, ymax} - corresponds to a split caret drawn as two lines, the first line
// drawn from (x,y) to (x, y2), the second line drawn from (x2, y2) to (x2, ymax).
double y2 = c[2];
lines = new double[][] {
new double[] {
c[0], c[1], y2
},
new double[] {
c[3], y2, c[4]
}
};
}
return new PrismCaretInfo(lines);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.PathElement;
import com.sun.javafx.font.CharToGlyphMapper;
import com.sun.javafx.font.FontResource;
Expand Down Expand Up @@ -310,8 +308,7 @@ public BaseBounds getBounds(TextSpan filter, BaseBounds bounds) {
}

@Override
public PathElement[] getCaretShape(int offset, boolean isLeading,
float x, float y) {
public float[] getCaretInf(int offset, boolean isLeading) {
ensureLayout();
int lineIndex = 0;
int lineCount = getLineCount();
Expand Down Expand Up @@ -382,8 +379,7 @@ public PathElement[] getCaretShape(int offset, boolean isLeading,
if (isMirrored()) {
lineX = getMirroringWidth() - lineX;
}
lineX += x;
lineY += y;

if (splitCaretOffset != -1) {
for (int i = 0; i < runs.length; i++) {
TextRun run = runs[i];
Expand All @@ -401,21 +397,22 @@ public PathElement[] getCaretShape(int offset, boolean isLeading,
if (isMirrored()) {
lineX2 = getMirroringWidth() - lineX2;
}
lineX2 += x;
PathElement[] result = new PathElement[4];
result[0] = new MoveTo(lineX, lineY);
result[1] = new LineTo(lineX, lineY + lineHeight / 2);
result[2] = new MoveTo(lineX2, lineY + lineHeight / 2);
result[3] = new LineTo(lineX2, lineY + lineHeight);
return result;
return new float[] {
lineX,
lineY,
lineY + lineHeight / 2,
lineX2,
lineY + lineHeight
};
}
}
}
}
PathElement[] result = new PathElement[2];
result[0] = new MoveTo(lineX, lineY);
result[1] = new LineTo(lineX, lineY + lineHeight);
return result;
return new float[] {
lineX,
lineY,
lineY + lineHeight
};
}

@Override
Expand Down Expand Up @@ -478,8 +475,7 @@ public Hit getHitInfo(float x, float y) {
}

@Override
public PathElement[] getRange(int start, int end, int type,
float x, float y) {
public void getRange(int start, int end, int type, GeometryCallback client) {
ensureLayout();
int lineCount = getLineCount();
ArrayList<PathElement> result = new ArrayList<>();
Expand Down Expand Up @@ -575,11 +571,7 @@ public PathElement[] getRange(int start, int end, int type,
l = width - l;
r = width - r;
}
result.add(new MoveTo(x + l, y + top));
result.add(new LineTo(x + r, y + top));
result.add(new LineTo(x + r, y + bottom));
result.add(new LineTo(x + l, y + bottom));
result.add(new LineTo(x + l, y + top));
client.addRectangle(l, top, r, bottom);
}
left = runLeft;
right = runRight;
Expand All @@ -592,19 +584,14 @@ public PathElement[] getRange(int start, int end, int type,
l = width - l;
r = width - r;
}
result.add(new MoveTo(x + l, y + top));
result.add(new LineTo(x + r, y + top));
result.add(new LineTo(x + r, y + bottom));
result.add(new LineTo(x + l, y + bottom));
result.add(new LineTo(x + l, y + top));
client.addRectangle(l, top, r, bottom);
}
}
lineX += runWidth;
runIndex++;
}
lineY += lineBounds.getHeight() + spacing;
}
return result.toArray(new PathElement[result.size()]);
}

@Override
Expand Down
Loading