Skip to content

Commit 94a7ee7

Browse files
authored
Mrjar config and source (#804)
Added Java 9 files and modified gradle build to accommodate
1 parent c1f702b commit 94a7ee7

File tree

10 files changed

+426
-25
lines changed

10 files changed

+426
-25
lines changed

build.gradle

+3-12
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,9 @@ subprojects {
1212
}
1313
}
1414

15-
sourceCompatibility = '1.8'
16-
targetCompatibility = '1.8'
17-
18-
// allow setting source/target compatibility from command line
19-
// for checking Java 9+ compatibility in Travis CI
20-
def checkSourceCompatibility = System.properties["checkSourceCompatibility"]
21-
if (checkSourceCompatibility != null) {
22-
sourceCompatibility = checkSourceCompatibility
23-
targetCompatibility = checkSourceCompatibility
24-
}
25-
26-
compileJava.options.deprecation = true
15+
// Unless Java 9 is used source/target compatibility will be that of the compiling JDK
16+
// With JDK 9 src/main/java compiles with source/target compatibility of Java 8 (eight)
17+
// while src/main/java9 compiles with 9 and multi-release jars are created.
2718

2819
if (it.name.equals("richtextfx")) {
2920
task("getProjectVersion") {

gradle/wrapper/gradle-wrapper.properties

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
33
zipStoreBase=GRADLE_USER_HOME
44
zipStorePath=wrapper/dists
5-
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-bin.zip
6-
distributionSha256Sum=e7cf7d1853dfc30c1c44f571d3919eeeedef002823b66b6a988d27e919686389
5+
distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-bin.zip
6+
distributionSha256Sum=748c33ff8d216736723be4037085b8dc342c6a0f309081acf682c9803e407357

richtextfx/build.gradle

+42-10
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ buildscript {
22
repositories {
33
jcenter()
44
}
5-
dependencies {
6-
classpath 'org.unbroken-dome.gradle-plugins:gradle-testsets-plugin:1.3.2'
7-
}
5+
}
6+
7+
plugins {
8+
id 'org.unbroken-dome.test-sets' version '2.1.1'
89
}
910

1011
apply plugin: 'osgi'
1112
apply plugin: 'maven'
1213
apply plugin: 'signing'
13-
apply plugin: 'org.unbroken-dome.test-sets'
1414

1515
group = 'org.fxmisc.richtext'
1616

@@ -20,22 +20,30 @@ testSets {
2020

2121
check.dependsOn integrationTest
2222
integrationTest.mustRunAfter test
23-
if (gradle.gradleVersion.substring(0, 1) >= "4") {
24-
// required for Gradle 4 to see custom integrationTest test suite
25-
integrationTest.testClassesDirs = sourceSets.integrationTest.output.classesDirs
23+
integrationTest.testClassesDirs = sourceSets.integrationTest.output.classesDirs
24+
25+
sourceSets {
26+
java9 {
27+
java {
28+
srcDirs = ['src/main/java9']
29+
}
30+
}
2631
}
32+
2733
dependencies {
2834
compile group: 'org.reactfx', name: 'reactfx', version: '2.0-M5'
2935
compile group: 'org.fxmisc.undo', name: 'undofx', version: '2.1.0'
3036
compile group: 'org.fxmisc.flowless', name: 'flowless', version: '0.6'
3137
compile group: 'org.fxmisc.wellbehaved', name: 'wellbehavedfx', version: '0.3.3'
3238

39+
java9Implementation files(sourceSets.main.output.classesDirs) { builtBy compileJava }
40+
3341
testCompile group: 'junit', name: 'junit', version: '4.12'
3442

3543
integrationTestCompile group: 'junit', name: 'junit', version: '4.12'
3644
integrationTestCompile group: 'com.nitorcreations', name: 'junit-runners', version: '1.2'
3745
integrationTestCompile "org.testfx:testfx-core:4.0.8-alpha"
38-
if (org.gradle.api.JavaVersion.current().isJava9()) {
46+
if ( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
3947
integrationTestCompile "org.testfx:testfx-internal-java9:4.0.8-alpha"
4048
}
4149
integrationTestCompile ("org.testfx:testfx-junit:4.0.8-alpha") {
@@ -44,14 +52,30 @@ dependencies {
4452
integrationTestCompile "org.testfx:openjfx-monocle:8u76-b04"
4553
}
4654

55+
compileJava {
56+
if ( JavaVersion.current() == JavaVersion.VERSION_1_9 ) {
57+
sourceCompatibility = 8
58+
targetCompatibility = 8
59+
}
60+
options.deprecation = true
61+
}
62+
63+
compileJava9Java {
64+
onlyIf { JavaVersion.current() == JavaVersion.VERSION_1_9 }
65+
sourceCompatibility = 9
66+
targetCompatibility = 9
67+
options.deprecation = true
68+
}
4769

4870
jar {
71+
into 'META-INF/versions/9', { from sourceSets.java9.output }
4972
manifest {
5073
attributes(
5174
'Specification-Title': 'RichTextFX',
5275
'Specification-Version': project.specificationVersion,
5376
'Implementation-Title': 'RichTextFX',
54-
'Implementation-Version': project.version)
77+
'Implementation-Version': project.version,
78+
'Multi-Release': 'true')
5579
}
5680
}
5781

@@ -82,6 +106,9 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat
82106
import org.gradle.api.tasks.testing.logging.TestLogEvent
83107

84108
test {
109+
dependsOn jar
110+
// run tests against jar in case it's a multi-release jar
111+
classpath = files(jar.archivePath, classpath) - sourceSets.main.output
85112
testLogging {
86113
// Fancy formatting from http://stackoverflow.com/a/36130467/3634630
87114
// set options for log level LIFECYCLE
@@ -111,6 +138,9 @@ test {
111138
}
112139

113140
integrationTest {
141+
dependsOn jar
142+
// run tests against jar in case it's a multi-release jar
143+
classpath = files(jar.archivePath, classpath) - sourceSets.main.output
114144
testLogging {
115145
// Fancy formatting from http://stackoverflow.com/a/36130467/3634630
116146
// set options for log level LIFECYCLE
@@ -145,13 +175,13 @@ task javadocJar(type: Jar, dependsOn: javadoc) {
145175
}
146176

147177
task sourcesJar(type: Jar) {
178+
into 'java9', { from sourceSets.java9.allSource }
148179
from sourceSets.main.allSource
149180
classifier = 'sources'
150181
}
151182

152183
artifacts {
153184
archives jar
154-
155185
archives javadocJar
156186
archives sourcesJar
157187
}
@@ -218,6 +248,8 @@ uploadArchives.onlyIf { doUploadArchives }
218248

219249
task fatJar(type: Jar, dependsOn: classes) {
220250
appendix = 'fat'
251+
manifest.attributes( 'Multi-Release': 'true' )
252+
into 'META-INF/versions/9', { from sourceSets.java9.output }
221253
from sourceSets.main.output
222254
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
223255
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.fxmisc.richtext;
2+
3+
import org.fxmisc.richtext.JavaFXCompatibility;
4+
import org.junit.Test;
5+
6+
import static org.junit.Assert.assertTrue;
7+
import static org.junit.Assert.assertFalse;
8+
9+
public class MultiReleaseJarTest
10+
{
11+
@Test
12+
public void tests_correct_classes_are_used() {
13+
14+
if ( System.getProperty( "javafx.version" ).split("\\.")[0].equals("8") ) {
15+
assertTrue( JavaFXCompatibility.isJavaEight() );
16+
}
17+
else {
18+
assertFalse( JavaFXCompatibility.isJavaEight() );
19+
}
20+
}
21+
22+
}

richtextfx/src/main/java/org/fxmisc/richtext/JavaFXCompatibility.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
/**
1212
* Uses reflection to make this project's code work on Java 8 and Java 9 in a single jar
1313
*/
14-
class JavaFXCompatibility {
14+
public class JavaFXCompatibility {
1515

1616
private static boolean isJava9orLater;
1717

@@ -26,6 +26,14 @@ class JavaFXCompatibility {
2626
}
2727
}
2828

29+
/**
30+
* There is a Java 9 version of this that returns false in src/main/java9/...
31+
* and is used to check if tests are running against a multi-release jar.
32+
*/
33+
public static boolean isJavaEight() {
34+
return true;
35+
}
36+
2937
/**
3038
* Java 8: javafx.scene.text.Text.impl_selectionFillProperty()
3139
* Java 9+: javafx.scene.text.Text.selectionFillProperty()

richtextfx/src/main/java/org/fxmisc/richtext/TextFlowExt.java

+3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121

2222
/**
2323
* Adds additional API to {@link TextFlow}.
24+
*
25+
* PLEASE NOTE that is has a Java 9+ version in src/main/java9 !!!
26+
*
2427
*/
2528
class TextFlowExt extends TextFlow {
2629

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.fxmisc.richtext;
2+
3+
import javafx.beans.property.ObjectProperty;
4+
import javafx.css.converter.SizeConverter;
5+
import javafx.css.StyleConverter;
6+
import javafx.scene.paint.Paint;
7+
import javafx.scene.text.Text;
8+
9+
/* ************************************************* *
10+
* *
11+
* Also look for and remove deprecated methods !!! *
12+
* *
13+
* ************************************************* */
14+
/**
15+
* Used to use reflection to make this project's code work on Java 8 and Java 9 in a single jar
16+
*/
17+
@Deprecated
18+
public class JavaFXCompatibility {
19+
20+
static public boolean isJavaEight() {
21+
return false;
22+
}
23+
24+
static ObjectProperty<Paint> Text_selectionFillProperty(Text text) {
25+
return text.selectionFillProperty();
26+
}
27+
28+
static StyleConverter<?, Number[]> SizeConverter_SequenceConverter_getInstance() {
29+
return SizeConverter.SequenceConverter.getInstance();
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package org.fxmisc.richtext;
2+
3+
import static org.fxmisc.richtext.model.TwoDimensional.Bias.*;
4+
5+
import java.util.ArrayList;
6+
import java.util.List;
7+
8+
import javafx.geometry.Point2D;
9+
import javafx.geometry.Rectangle2D;
10+
import javafx.scene.control.IndexRange;
11+
import org.fxmisc.richtext.model.TwoLevelNavigator;
12+
13+
import javafx.scene.shape.PathElement;
14+
import javafx.scene.text.HitInfo;
15+
import javafx.scene.text.TextFlow;
16+
import javafx.scene.shape.LineTo;
17+
import javafx.scene.shape.MoveTo;
18+
19+
/**
20+
* Adds additional API to {@link TextFlow}.
21+
*/
22+
class TextFlowExt extends TextFlow {
23+
24+
private TextFlowLayout layout;
25+
26+
private TextFlowLayout textLayout()
27+
{
28+
if ( layout == null ) {
29+
layout = new TextFlowLayout( this, getManagedChildren() );
30+
}
31+
return layout;
32+
}
33+
34+
int getLineCount() {
35+
return textLayout().getLineCount();
36+
}
37+
38+
int getLineStartPosition(int charIdx) {
39+
TwoLevelNavigator navigator = textLayout().getTwoLevelNavigator();
40+
int currentLineIndex = navigator.offsetToPosition(charIdx, Forward).getMajor();
41+
return navigator.position(currentLineIndex, 0).toOffset();
42+
}
43+
44+
int getLineEndPosition(int charIdx) {
45+
TwoLevelNavigator navigator = textLayout().getTwoLevelNavigator();
46+
int currentLineIndex = navigator.offsetToPosition(charIdx, Forward).getMajor();
47+
int minor = (currentLineIndex == getLineCount() - 1) ? 0 : -1;
48+
return navigator.position(currentLineIndex + 1, minor).toOffset();
49+
}
50+
51+
int getLineOfCharacter(int charIdx) {
52+
TwoLevelNavigator navigator = textLayout().getTwoLevelNavigator();
53+
return navigator.offsetToPosition(charIdx, Forward).getMajor();
54+
}
55+
56+
PathElement[] getCaretShape(int charIdx, boolean isLeading) {
57+
return caretShape(charIdx, isLeading);
58+
}
59+
60+
PathElement[] getRangeShape(IndexRange range) {
61+
return getRangeShape(range.getStart(), range.getEnd());
62+
}
63+
64+
PathElement[] getRangeShape(int from, int to) {
65+
return rangeShape(from, to);
66+
}
67+
68+
PathElement[] getUnderlineShape(IndexRange range) {
69+
return getUnderlineShape(range.getStart(), range.getEnd());
70+
}
71+
72+
/**
73+
* @param from The index of the first character.
74+
* @param to The index of the last character.
75+
* @return An array with the PathElement objects which define an
76+
* underline from the first to the last character.
77+
*/
78+
PathElement[] getUnderlineShape(int from, int to) {
79+
// get a Path for the text underline
80+
List<PathElement> result = new ArrayList<>();
81+
82+
PathElement[] shape = rangeShape( from, to );
83+
// The shape is a closed Path for one or more rectangles AROUND the selected text.
84+
// shape: [MoveTo origin, LineTo top R, LineTo bottom R, LineTo bottom L, LineTo origin, *]
85+
86+
// Extract the bottom left and right coordinates for each rectangle to get the underline path.
87+
for ( int ele = 2; ele < shape.length; ele += 5 )
88+
{
89+
LineTo bl = (LineTo) shape[ele+1];
90+
LineTo br = (LineTo) shape[ele];
91+
double y = br.getY() - 2.5;
92+
93+
result.add( new MoveTo( bl.getX(), y ) );
94+
result.add( new LineTo( br.getX(), y ) );
95+
}
96+
97+
return result.toArray(new PathElement[0]);
98+
}
99+
100+
CharacterHit hitLine(double x, int lineIndex) {
101+
return hit(x, textLayout().getLineCenter( lineIndex ));
102+
}
103+
104+
CharacterHit hit(double x, double y) {
105+
TextFlowSpan span = textLayout().getLineSpan( (float) y );
106+
Rectangle2D lineBounds = span.getBounds();
107+
108+
HitInfo hit = hitTest(new Point2D(x, y));
109+
int charIdx = hit.getCharIndex();
110+
boolean leading = hit.isLeading();
111+
112+
if ( ! leading ) {
113+
// If this is a wrapped paragraph and hit character is at end of hit line, make sure that the
114+
// "character hit" stays at the end of the hit line (and not at the beginning of the next line).
115+
leading = (getLineCount() > 1 && charIdx + 1 >= span.getStart() + span.getLength());
116+
}
117+
118+
if(x < lineBounds.getMinX() || x > lineBounds.getMaxX()) {
119+
if(leading) {
120+
return CharacterHit.insertionAt(charIdx);
121+
} else {
122+
return CharacterHit.insertionAt(charIdx + 1);
123+
}
124+
} else {
125+
if(leading) {
126+
return CharacterHit.leadingHalfOf(charIdx);
127+
} else {
128+
return CharacterHit.trailingHalfOf(charIdx);
129+
}
130+
}
131+
}
132+
133+
}

0 commit comments

Comments
 (0)