Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 3 additions & 12 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,9 @@ subprojects {
}
}

sourceCompatibility = '1.8'
targetCompatibility = '1.8'

// allow setting source/target compatibility from command line
// for checking Java 9+ compatibility in Travis CI
def checkSourceCompatibility = System.properties["checkSourceCompatibility"]
if (checkSourceCompatibility != null) {
sourceCompatibility = checkSourceCompatibility
targetCompatibility = checkSourceCompatibility
}

compileJava.options.deprecation = true
// Unless Java 9 is used source/target compatibility will be that of the compiling JDK
// With JDK 9 src/main/java compiles with source/target compatibility of Java 8 (eight)
// while src/main/java9 compiles with 9 and multi-release jars are created.

if (it.name.equals("richtextfx")) {
task("getProjectVersion") {
Expand Down
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-bin.zip
distributionSha256Sum=e7cf7d1853dfc30c1c44f571d3919eeeedef002823b66b6a988d27e919686389
distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-bin.zip
distributionSha256Sum=748c33ff8d216736723be4037085b8dc342c6a0f309081acf682c9803e407357
52 changes: 42 additions & 10 deletions richtextfx/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'org.unbroken-dome.gradle-plugins:gradle-testsets-plugin:1.3.2'
}
}

plugins {
id 'org.unbroken-dome.test-sets' version '2.1.1'
}

apply plugin: 'osgi'
apply plugin: 'maven'
apply plugin: 'signing'
apply plugin: 'org.unbroken-dome.test-sets'

group = 'org.fxmisc.richtext'

Expand All @@ -20,22 +20,30 @@ testSets {

check.dependsOn integrationTest
integrationTest.mustRunAfter test
if (gradle.gradleVersion.substring(0, 1) >= "4") {
// required for Gradle 4 to see custom integrationTest test suite
integrationTest.testClassesDirs = sourceSets.integrationTest.output.classesDirs
integrationTest.testClassesDirs = sourceSets.integrationTest.output.classesDirs

sourceSets {
java9 {
java {
srcDirs = ['src/main/java9']
}
}
}

dependencies {
compile group: 'org.reactfx', name: 'reactfx', version: '2.0-M5'
compile group: 'org.fxmisc.undo', name: 'undofx', version: '2.1.0'
compile group: 'org.fxmisc.flowless', name: 'flowless', version: '0.6'
compile group: 'org.fxmisc.wellbehaved', name: 'wellbehavedfx', version: '0.3.3'

java9Implementation files(sourceSets.main.output.classesDirs) { builtBy compileJava }

testCompile group: 'junit', name: 'junit', version: '4.12'

integrationTestCompile group: 'junit', name: 'junit', version: '4.12'
integrationTestCompile group: 'com.nitorcreations', name: 'junit-runners', version: '1.2'
integrationTestCompile "org.testfx:testfx-core:4.0.8-alpha"
if (org.gradle.api.JavaVersion.current().isJava9()) {
if ( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
integrationTestCompile "org.testfx:testfx-internal-java9:4.0.8-alpha"
}
integrationTestCompile ("org.testfx:testfx-junit:4.0.8-alpha") {
Expand All @@ -44,14 +52,30 @@ dependencies {
integrationTestCompile "org.testfx:openjfx-monocle:8u76-b04"
}

compileJava {
if ( JavaVersion.current() == JavaVersion.VERSION_1_9 ) {
sourceCompatibility = 8
targetCompatibility = 8
}
options.deprecation = true
}

compileJava9Java {
onlyIf { JavaVersion.current() == JavaVersion.VERSION_1_9 }
sourceCompatibility = 9
targetCompatibility = 9
options.deprecation = true
}

jar {
into 'META-INF/versions/9', { from sourceSets.java9.output }
manifest {
attributes(
'Specification-Title': 'RichTextFX',
'Specification-Version': project.specificationVersion,
'Implementation-Title': 'RichTextFX',
'Implementation-Version': project.version)
'Implementation-Version': project.version,
'Multi-Release': 'true')
}
}

Expand Down Expand Up @@ -82,6 +106,9 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent

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

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

task sourcesJar(type: Jar) {
into 'java9', { from sourceSets.java9.allSource }
from sourceSets.main.allSource
classifier = 'sources'
}

artifacts {
archives jar

archives javadocJar
archives sourcesJar
}
Expand Down Expand Up @@ -218,6 +248,8 @@ uploadArchives.onlyIf { doUploadArchives }

task fatJar(type: Jar, dependsOn: classes) {
appendix = 'fat'
manifest.attributes( 'Multi-Release': 'true' )
into 'META-INF/versions/9', { from sourceSets.java9.output }
from sourceSets.main.output
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.fxmisc.richtext;

import org.fxmisc.richtext.JavaFXCompatibility;
import org.junit.Test;

import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;

public class MultiReleaseJarTest
{
@Test
public void tests_correct_classes_are_used() {

if ( System.getProperty( "javafx.version" ).split("\\.")[0].equals("8") ) {
assertTrue( JavaFXCompatibility.isJavaEight() );
}
else {
assertFalse( JavaFXCompatibility.isJavaEight() );
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
/**
* Uses reflection to make this project's code work on Java 8 and Java 9 in a single jar
*/
class JavaFXCompatibility {
public class JavaFXCompatibility {

private static boolean isJava9orLater;

Expand All @@ -26,6 +26,14 @@ class JavaFXCompatibility {
}
}

/**
* There is a Java 9 version of this that returns false in src/main/java9/...
* and is used to check if tests are running against a multi-release jar.
*/
public static boolean isJavaEight() {
return true;
}

/**
* Java 8: javafx.scene.text.Text.impl_selectionFillProperty()
* Java 9+: javafx.scene.text.Text.selectionFillProperty()
Expand Down
3 changes: 3 additions & 0 deletions richtextfx/src/main/java/org/fxmisc/richtext/TextFlowExt.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@

/**
* Adds additional API to {@link TextFlow}.
*
* PLEASE NOTE that is has a Java 9+ version in src/main/java9 !!!
*
*/
class TextFlowExt extends TextFlow {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.fxmisc.richtext;

import javafx.beans.property.ObjectProperty;
import javafx.css.converter.SizeConverter;
import javafx.css.StyleConverter;
import javafx.scene.paint.Paint;
import javafx.scene.text.Text;

/* ************************************************* *
* *
* Also look for and remove deprecated methods !!! *
* *
* ************************************************* */
/**
* Used to use reflection to make this project's code work on Java 8 and Java 9 in a single jar
*/
@Deprecated
public class JavaFXCompatibility {

static public boolean isJavaEight() {
return false;
}

static ObjectProperty<Paint> Text_selectionFillProperty(Text text) {
return text.selectionFillProperty();
}

static StyleConverter<?, Number[]> SizeConverter_SequenceConverter_getInstance() {
return SizeConverter.SequenceConverter.getInstance();
}
}
133 changes: 133 additions & 0 deletions richtextfx/src/main/java9/org/fxmisc/richtext/TextFlowExt.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package org.fxmisc.richtext;

import static org.fxmisc.richtext.model.TwoDimensional.Bias.*;

import java.util.ArrayList;
import java.util.List;

import javafx.geometry.Point2D;
import javafx.geometry.Rectangle2D;
import javafx.scene.control.IndexRange;
import org.fxmisc.richtext.model.TwoLevelNavigator;

import javafx.scene.shape.PathElement;
import javafx.scene.text.HitInfo;
import javafx.scene.text.TextFlow;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;

/**
* Adds additional API to {@link TextFlow}.
*/
class TextFlowExt extends TextFlow {

private TextFlowLayout layout;

private TextFlowLayout textLayout()
{
if ( layout == null ) {
layout = new TextFlowLayout( this, getManagedChildren() );
}
return layout;
}

int getLineCount() {
return textLayout().getLineCount();
}

int getLineStartPosition(int charIdx) {
TwoLevelNavigator navigator = textLayout().getTwoLevelNavigator();
int currentLineIndex = navigator.offsetToPosition(charIdx, Forward).getMajor();
return navigator.position(currentLineIndex, 0).toOffset();
}

int getLineEndPosition(int charIdx) {
TwoLevelNavigator navigator = textLayout().getTwoLevelNavigator();
int currentLineIndex = navigator.offsetToPosition(charIdx, Forward).getMajor();
int minor = (currentLineIndex == getLineCount() - 1) ? 0 : -1;
return navigator.position(currentLineIndex + 1, minor).toOffset();
}

int getLineOfCharacter(int charIdx) {
TwoLevelNavigator navigator = textLayout().getTwoLevelNavigator();
return navigator.offsetToPosition(charIdx, Forward).getMajor();
}

PathElement[] getCaretShape(int charIdx, boolean isLeading) {
return caretShape(charIdx, isLeading);
}

PathElement[] getRangeShape(IndexRange range) {
return getRangeShape(range.getStart(), range.getEnd());
}

PathElement[] getRangeShape(int from, int to) {
return rangeShape(from, to);
}

PathElement[] getUnderlineShape(IndexRange range) {
return getUnderlineShape(range.getStart(), range.getEnd());
}

/**
* @param from The index of the first character.
* @param to The index of the last character.
* @return An array with the PathElement objects which define an
* underline from the first to the last character.
*/
PathElement[] getUnderlineShape(int from, int to) {
// get a Path for the text underline
List<PathElement> result = new ArrayList<>();

PathElement[] shape = rangeShape( from, to );
// The shape is a closed Path for one or more rectangles AROUND the selected text.
// shape: [MoveTo origin, LineTo top R, LineTo bottom R, LineTo bottom L, LineTo origin, *]

// Extract the bottom left and right coordinates for each rectangle to get the underline path.
for ( int ele = 2; ele < shape.length; ele += 5 )
{
LineTo bl = (LineTo) shape[ele+1];
LineTo br = (LineTo) shape[ele];
double y = br.getY() - 2.5;

result.add( new MoveTo( bl.getX(), y ) );
result.add( new LineTo( br.getX(), y ) );
}

return result.toArray(new PathElement[0]);
}

CharacterHit hitLine(double x, int lineIndex) {
return hit(x, textLayout().getLineCenter( lineIndex ));
}

CharacterHit hit(double x, double y) {
TextFlowSpan span = textLayout().getLineSpan( (float) y );
Rectangle2D lineBounds = span.getBounds();

HitInfo hit = hitTest(new Point2D(x, y));
int charIdx = hit.getCharIndex();
boolean leading = hit.isLeading();

if ( ! leading ) {
// If this is a wrapped paragraph and hit character is at end of hit line, make sure that the
// "character hit" stays at the end of the hit line (and not at the beginning of the next line).
leading = (getLineCount() > 1 && charIdx + 1 >= span.getStart() + span.getLength());
}

if(x < lineBounds.getMinX() || x > lineBounds.getMaxX()) {
if(leading) {
return CharacterHit.insertionAt(charIdx);
} else {
return CharacterHit.insertionAt(charIdx + 1);
}
} else {
if(leading) {
return CharacterHit.leadingHalfOf(charIdx);
} else {
return CharacterHit.trailingHalfOf(charIdx);
}
}
}

}
Loading