I'm trying to create a classical side-by-side difference viewer with 2 InlineCssTextArea on the same horizontal alignment. I want line wrapping in text areas to avoid horizontal scrolling. I'm only interested in non-editable text areas, so no subject on handling modifications.
I've implemented the main feature (displaying the original text with red background on deleted text, the new text with green background on added text), but I'm struggling to synchronize the scrolling between the 2 areas. I don't see how to achieve a clean synchronization with the current API, but I may be missing a few things...
Here are the problems I encounter :
Dual way synchronization
I want that whatever text area is scrolled, the other one follows nicely.
For the moment, I have implemented a listener on each estimatedScrollYProperty() that scrolls the other one.
Problem is that I can't distinguish events coming from an actual user action on the text area, or from the other text area being scrolled.
Is there a way to distinguish the events?
final ObservableValue<Double> oldScrollY = this.oldContentArea.estimatedScrollYProperty();
oldScrollY.addListener((_, _, _) -> syncScroll(oldContentArea, newContentArea, true));
final ObservableValue<Double> newScrollY = this.newContentArea.estimatedScrollYProperty();
newScrollY.addListener((_, _, _) -> syncScroll(newContentArea, oldContentArea, false));
Determining what is actually displayed on the text area
For the moment, I'm using lastVisibleParToAllParIndex() and firstVisibleParToAllParIndex(), but with line wrapping it's really inaccurate to know what is actually displayed (in a paragraph takes 5 lines, I don't know how many lines are actually displayed).
Is there a way to know what is the text actually displayed on the text area?
final int lastSourcePar = sourceArea.lastVisibleParToAllParIndex();
final int lastTargetPosition = computeTargetPosition(sourceArea, lastSourcePar, newer);
final int firstSourcePar = sourceArea.firstVisibleParToAllParIndex();
final int firstTargetPosition = computeTargetPosition(sourceArea, firstSourcePar, newer);
Scrolling to the computed line
With the restriction on the precision of what is actually displayed, I can still compute what is the matching part in the other text area. I'm currently using the methods selectRange() and requestFollowCaret() to scroll the text area, but they're not working as I need:
requestFollowCaret() seems to do nothing is the range is already visible : I'd like to be able to force the move to display the beginning of the range at the top of the text area
- requestFollowCaret() seems to favor the end of the range if the range is too big to fit on screen : I'd like the other way for my use case. So I can't select a huge range to force the beginning at the top of the displayed area.
requestFollowCaret() seems to be working asynchronously, so I can't use hacks like calling selectRange() with a big range, requestFollowCaret() to force the end of the big range on screen (beginning not visible), selectRange() with only the beginning, requestFollowCaret() to force the beginning on screen (at top since it wasn't visible).
- If I do
selectRange() for only the beginning, the synchronization only works once every two page downs. At the first page down, the paragraph that was last visible becomes the first visible paragraph, so it's still visible in the other text area, so nothing happens with requestFollowCaret()
- If I do
selectRange() with an actual range, text is displayed as selected which is not what I want.
Is there another way to scroll the text area to a given point without using the selection and caret mechanisms?
For the moment, I've implemented an in-between solution, which is not perfect
targetArea.moveTo((firstTargetPosition + lastTargetPosition) / 2);
targetArea.requestFollowCaret();
I'm trying to create a classical side-by-side difference viewer with 2
InlineCssTextAreaon the same horizontal alignment. I want line wrapping in text areas to avoid horizontal scrolling. I'm only interested in non-editable text areas, so no subject on handling modifications.I've implemented the main feature (displaying the original text with red background on deleted text, the new text with green background on added text), but I'm struggling to synchronize the scrolling between the 2 areas. I don't see how to achieve a clean synchronization with the current API, but I may be missing a few things...
Here are the problems I encounter :
Dual way synchronization
I want that whatever text area is scrolled, the other one follows nicely.
For the moment, I have implemented a listener on each
estimatedScrollYProperty()that scrolls the other one.Problem is that I can't distinguish events coming from an actual user action on the text area, or from the other text area being scrolled.
Is there a way to distinguish the events?
Determining what is actually displayed on the text area
For the moment, I'm using
lastVisibleParToAllParIndex()andfirstVisibleParToAllParIndex(), but with line wrapping it's really inaccurate to know what is actually displayed (in a paragraph takes 5 lines, I don't know how many lines are actually displayed).Is there a way to know what is the text actually displayed on the text area?
Scrolling to the computed line
With the restriction on the precision of what is actually displayed, I can still compute what is the matching part in the other text area. I'm currently using the methods
selectRange()andrequestFollowCaret()to scroll the text area, but they're not working as I need:requestFollowCaret()seems to do nothing is the range is already visible : I'd like to be able to force the move to display the beginning of the range at the top of the text arearequestFollowCaret()seems to be working asynchronously, so I can't use hacks like callingselectRange()with a big range,requestFollowCaret()to force the end of the big range on screen (beginning not visible),selectRange()with only the beginning,requestFollowCaret()to force the beginning on screen (at top since it wasn't visible).selectRange()for only the beginning, the synchronization only works once every two page downs. At the first page down, the paragraph that was last visible becomes the first visible paragraph, so it's still visible in the other text area, so nothing happens withrequestFollowCaret()selectRange()with an actual range, text is displayed as selected which is not what I want.Is there another way to scroll the text area to a given point without using the selection and caret mechanisms?
For the moment, I've implemented an in-between solution, which is not perfect