Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
8302511: HitInfo.toString() throws IllegalArgumentException
Reviewed-by: kpk, aghaisas
  • Loading branch information
Andy Goryachev committed Jun 23, 2023
1 parent 152a43e commit d2683b9
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 68 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 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
Expand All @@ -25,78 +25,56 @@

package javafx.scene.text;

import java.text.BreakIterator;

/**
* Represents the hit information in a Text node.
*
* @since 9
*/
public class HitInfo {

private int charIndex;
private boolean leading;
private int insertionIndex;
private String text;
private final int charIndex;
private final boolean leading;
private final int insertionIndex;

/**
* Create a HitInfo object representing a text index and forward bias.
*
* @param charIndex the character index.
* @param leading whether the hit is on the leading edge of the character. If it is false, it represents the trailing edge.
*/
HitInfo(int charIndex, int insertionIndex, boolean leading, String text) {
HitInfo(int charIndex, int insertionIndex, boolean leading) {
this.charIndex = charIndex;
this.leading = leading;
this.insertionIndex = insertionIndex;
this.text = text;
}

/**
* The index of the character which this hit information refers to.
* @return the index of the character which this hit information refers to
*/
public int getCharIndex() { return charIndex; }
public int getCharIndex() {
return charIndex;
}

/**
* Indicates whether the hit is on the leading edge of the character.
* If it is false, it represents the trailing edge.
* @return if true the hit is on the leading edge of the character, otherwise
* the trailing edge
*/
public boolean isLeading() { return leading; }

private static BreakIterator charIterator = BreakIterator.getCharacterInstance();
public boolean isLeading() {
return leading;
}

/**
* Returns the index of the insertion position.
* @return the index of the insertion position
*/
public int getInsertionIndex() {
if (insertionIndex == -1) {
insertionIndex = charIndex;
if (!leading) {
if (text != null) {
// Skip complex character clusters / ligatures.
int next;
synchronized(charIterator) {
charIterator.setText(text);
next = charIterator.following(insertionIndex);
}
if (next == BreakIterator.DONE) {
insertionIndex += 1;
} else {
insertionIndex = next;
}
} else {
insertionIndex += 1;
}
}
}
return insertionIndex;
}

@Override public String toString() {
return "charIndex: " + charIndex + ", isLeading: " + leading + ", insertionIndex: " + getInsertionIndex();
@Override
public String toString() {
return "charIndex: " + charIndex + ", isLeading: " + leading + ", insertionIndex: " + insertionIndex;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 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
Expand Down Expand Up @@ -1022,9 +1022,8 @@ public final HitInfo hitTest(Point2D point) {
TextLayout layout = getTextLayout();
double x = point.getX() - getX();
double y = point.getY() - getY() + getYRendering();
TextLayout.Hit layoutHit = layout.getHitInfo((float)x, (float)y);
return new HitInfo(layoutHit.getCharIndex(), layoutHit.getInsertionIndex(),
layoutHit.isLeading(), getText());
TextLayout.Hit h = layout.getHitInfo((float)x, (float)y);
return new HitInfo(h.getCharIndex(), h.getInsertionIndex(), h.isLeading());
}

private PathElement[] getRange(int start, int end, int type) {
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 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
Expand Down Expand Up @@ -197,11 +197,10 @@ private void checkOrientation() {
public final HitInfo hitTest(javafx.geometry.Point2D point) {
if (point != null) {
TextLayout layout = getTextLayout();
double x = point.getX()/* - getX()*/;
double y = point.getY()/* - getY()/* + getYRendering()*/;
TextLayout.Hit layoutHit = layout.getHitInfo((float)x, (float)y);
return new HitInfo(layoutHit.getCharIndex(), layoutHit.getInsertionIndex(),
layoutHit.isLeading(), null/*getText()*/);
double x = point.getX();
double y = point.getY();
TextLayout.Hit h = layout.getHitInfo((float)x, (float)y);
return new HitInfo(h.getCharIndex(), h.getInsertionIndex(), h.isLeading());
} else {
return null;
}
Expand Down
Expand Up @@ -25,32 +25,27 @@

package test.robot.javafx.scene;

import java.util.Random;
import java.util.concurrent.CountDownLatch;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.robot.Robot;
import javafx.scene.text.Font;
import javafx.scene.text.HitInfo;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import com.sun.javafx.PlatformUtil;
import com.sun.javafx.tk.Toolkit;

import test.util.Util;

/*
Expand Down Expand Up @@ -104,6 +99,7 @@

public class TextFlowSurrogatePairInsertionIndexTest {
static CountDownLatch startupLatch = new CountDownLatch(1);
static Random random;
static Robot robot;
static TextFlow textFlow;
static Text text;
Expand Down Expand Up @@ -132,7 +128,7 @@ private void mouseClick(double x, double y) {
});
}

private void moveMouseOverTextFlow(int x, int y) throws Exception {
private void moveMouseOverTextFlow(double x, double y) throws Exception {
mouseClick(textFlow.getLayoutX() + x,
textFlow.getLayoutY() + y);
}
Expand Down Expand Up @@ -207,15 +203,15 @@ public void testTextFlowInsertionIndexUsingMultipleEmojis() throws Exception {
Util.waitForIdle(scene);

int textLength = text.getText().length();
int index = 0;
double x = 0.0;
while (charIndex < textLength - 2) {
moveMouseOverTextFlow(index, Y_OFFSET);
moveMouseOverTextFlow(x, Y_OFFSET);
if (isLeading) {
Assert.assertEquals(charIndex, insertionIndex);
} else {
Assert.assertEquals(charIndex, insertionIndex - 2);
}
index += 5;
x += step();
}
}

Expand All @@ -225,17 +221,17 @@ public void testTextFlowInsertionIndexUsingTextAndEmojis() throws Exception {
Util.waitForIdle(scene);

int textLength = text.getText().length();
int index = 0;
double x = 0.0;
while (charIndex < textLength - 2) {
moveMouseOverTextFlow(index, Y_OFFSET);
moveMouseOverTextFlow(x, Y_OFFSET);
if (isLeading) {
Assert.assertEquals(charIndex, insertionIndex);
} else if (!isLeading && charIndex < 5) {
Assert.assertEquals(charIndex, insertionIndex - 1);
} else {
Assert.assertEquals(charIndex, insertionIndex - 2);
}
index += 5;
x += step();
}
}

Expand All @@ -246,17 +242,17 @@ public void testTextFlowInsertionIndexUsingEmbeddedTextNodes() throws Exception

int textLength = text.getText().length();
textLength += emoji.getText().length();
int index = 0;
double x = 0.0;
while (charIndex < textLength - 2) {
moveMouseOverTextFlow(index, Y_OFFSET);
moveMouseOverTextFlow(x, Y_OFFSET);
if (isLeading) {
Assert.assertEquals(charIndex, insertionIndex);
} else if (isSurrogatePair) {
Assert.assertEquals(charIndex, insertionIndex - 2);
} else {
Assert.assertEquals(charIndex, insertionIndex - 1);
}
index += 5;
x += step();
}
}

Expand All @@ -265,15 +261,15 @@ public void testTextFlowInsertionIndexWhenMouseMovedOutsideText() throws Excepti
addTextAndEmojis();
Util.waitForIdle(scene);

int index = 0;
while (index < (HEIGHT - Y_OFFSET)) {
moveMouseOverTextFlow(X_LEADING_OFFSET, (Y_OFFSET + index));
double x = 0.0;
while (x < (HEIGHT - Y_OFFSET)) {
moveMouseOverTextFlow(X_LEADING_OFFSET, (Y_OFFSET + x));
if (isLeading) {
Assert.assertEquals(charIndex, insertionIndex);
} else {
Assert.assertEquals(charIndex, insertionIndex - 1);
}
index += 5;
x += step();
}
}

Expand All @@ -283,7 +279,7 @@ public void testTextFlowInsertionIndexUsingWrappedText() throws Exception {
Util.waitForIdle(scene);

for (int y = 0; y < 2; y++) {
for (int x = 0; x < (WIDTH - X_LEADING_OFFSET); x += 5) {
for (double x = 0.0; x < (WIDTH - X_LEADING_OFFSET); x += step()) {
moveMouseOverTextFlow(x, (Y_OFFSET + (Y_OFFSET * (y * 2))));
if (isLeading) {
Assert.assertEquals(charIndex, insertionIndex);
Expand Down Expand Up @@ -311,6 +307,14 @@ private void handleMouseEvent(MouseEvent event) {
char c = testString.charAt(charIndex);
isSurrogatePair = Character.isSurrogate(c);
}

Assert.assertTrue(insertionIndex >= 0);
String s = hitInfo.toString();
Assert.assertTrue(s != null);
}

private double step() {
return 1.0 + random.nextDouble() * 8.0;
}

@After
Expand All @@ -329,6 +333,11 @@ public void setupUI() {

@BeforeClass
public static void initFX() {
long seed = new Random().nextLong();
// if any test fails, we can use the seed found in the log to reproduce exact sequence of events
System.out.println("seed=" + seed);
random = new Random(seed);

Util.launch(startupLatch, TestApp.class);
}

Expand Down

1 comment on commit d2683b9

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

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

Please sign in to comment.