FXMisc / RichTextFX

Rich-text area for JavaFX
BSD 2-Clause "Simplified" License
1.21k stars 236 forks source link

Getting exception when trying to get paragraph bounds when caret moves #939

Closed garybentley closed 4 years ago

garybentley commented 4 years ago

This is a strange one, please bear with me!

I have a custom paragraph graphic that makes use of area.getCharacterBoundsOnScreen and area.getParagraphBoundsOnScreen to determine where nodes within the graphic should be placed.

This code works well enough but if I have a node on the same line as the caret and I press return and the caret would leave the visible area then I get the following exception:

java.lang.IllegalArgumentException: This ParagraphText is not the parent of the given shape (CaretNode(name=main-caret position=324 paragraphIndex=1 columnPosition=0 Path[elements=[MoveTo[x=0.0, y=0.0], LineTo[x=0.0, y=37.0]], fill=null, fillRule=NON_ZERO, stroke=0x000000ff, strokeWidth=1.0])):
Expected: ParagraphText@663439511(paragraph=null)
Actual:   null
        at org.fxmisc.richtext.ParagraphText.checkWithinParagraph(ParagraphText.java:312)
        at org.fxmisc.richtext.ParagraphText.getCaretBounds(ParagraphText.java:251)
        at org.fxmisc.richtext.ParagraphBox.getCaretBounds(ParagraphBox.java:204)
        at org.fxmisc.richtext.GenericStyledArea.followCaret(GenericStyledArea.java:1698)
        at org.fxmisc.richtext.GenericStyledArea.lambda$layoutChildren$46(GenericStyledArea.java:1458)
        at org.reactfx.Suspendable.suspendWhile(Suspendable.java:49)
        at org.fxmisc.richtext.GenericStyledArea.layoutChildren(GenericStyledArea.java:1450)
        at javafx.graphics/javafx.scene.Parent.layout(Parent.java:1206)

The image below should give a rough idea of what's going on.

caret-exception

Once I press return and thus the caret would move below the bottom of the scrollpane viewport I get the exception above. This doesn't happen if I type normally and the line wraps at the edge of the area. Nor does it happen if there is space in the scrollpane viewport to go to the next line. It only happens when I press return and the caret is at the bottom of the viewport.

I can add my position lookup code if you like, it's a bit of a mess at the moment but it essentially just makes use of a combination of getCharacterBoundsOnScreen and getParagraphBoundsOnScreen depending upon where the position is in relation to the text.

I suspect the exception is being caused by the getXXX calls triggering a layout but I don't know how to prevent it happening because the calls are already being made in a layoutChildren call. It's notable here that this exception isn't being thrown from my code and none of my code is calling requestFollowCaret.

Jugen commented 4 years ago

Just a wild idea .... can you try and override requestFollowCaret()with the following and see if it helps:

@Override
public void requestFollowCaret() {
    Platform.runLater( () -> super.requestFollowCaret() );
}
Jugen commented 4 years ago

Hi Gary, could you please try this PR/patch and let me know if it works or not. Thanks.

garybentley commented 4 years ago

Do you have any plans to update the build files to support modules, i.e. post JavaFX 8 releases? I'm currently using Java 12/JavaFX 12 and the richtextfx build breaks until I modify the build.gradle file to tell the build where the module files are.

garybentley commented 4 years ago

For the patch, yes that seems to fix the problem, thanks :)

Jugen commented 4 years ago

Great :)  With regards to the gradle build file, I do have plans - but my gradle "foo" is weak :)

I would appreciate it very much, if you don't mind showing me how you've done it and then I can use that to maybe make appropriate changes this side.

garybentley commented 4 years ago

Yes I'm not great with gradle either, that's the trouble with build tools you don't use them enough to be get good at them.

Anyway, I modified file:

richtextfx/build.gradle

in the compileJava task I've added:

options.compilerArgs =
[
   '--add-exports',
   'javafx.graphics/com.sun.javafx.iio=ALL-UNNAMED',
   '--add-exports',
   'javafx.graphics/com.sun.javafx.iio.common=ALL-UNNAMED',
   '--add-exports',
   'javafx.graphics/com.sun.javafx.geom=ALL-UNNAMED',
   '--add-exports',
   'javafx.graphics/com.sun.javafx.scene.text=ALL-UNNAMED',
   '--add-exports',
   'javafx.graphics/com.sun.javafx.tk=ALL-UNNAMED',
       '--add-modules=java.base,javafx.controls,javafx.swing,javafx.media,javafx.web,java.instrument,jdk.attach',
       '--module-path',
       '[path to modules]'
   ]

I'm not sure if all of those exports are needed for RichTextFX (I assume they are because I added them :). The [path to modules] is just the location of where the javafx sdk library modules are, in my case this is: D:/reference/java/javafx/javafx-sdk-12/lib. I'm sure there is a gradle options file where the path could be defined but I don't have time to find out how to do that.

I've also modified the compileJava9Java task as well with the same compileArgs, but I'm not sure if that's needed for javafx 12+. The changes will only work for ./gradlew jar. Using ./gradlew fatJar will cause it to break so some other tasks probably need changing as well but again I just don't have time and only need the normal jar files.

Jugen commented 4 years ago

Thanks for this :)  I'll try it out and hopefully get something working ......

Siedlerchr commented 4 years ago

@garybentley Regarding modularization for javafx, it's no longer necessary to have a javafx sdk locally., we use the following in our build.gradle

plugins:
    id 'org.openjfx.javafxplugin' version '0.0.9'

javafx {
    version = "14"
    modules = [ 'javafx.controls', 'javafx.fxml', 'javafx.web', 'javafx.swing' ]
}

I also had problems building it locally. And for newer gradle versions, the osgi plugin is no longer supported, you need id 'biz.aQute.bnd.builder' version '5.1.2'

garybentley commented 4 years ago

@Siedlerchr thanks for that, it's good to know.