-
Notifications
You must be signed in to change notification settings - Fork 237
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Question: How to scroll two CodeAreas simultaneously? #1136
Comments
Here's my current attempt, using RichTextFX 0.10.9. First, I've defined a simple class, public class CodeViewer extends StackPane {
private final CodeArea codeArea;
private final VirtualizedScrollPane<CodeArea> scrollPane;
public CodeViewer() {
super();
this.codeArea = new CodeArea();
this.scrollPane = new VirtualizedScrollPane<>(codeArea);
getChildren().add(scrollPane);
codeArea.setParagraphGraphicFactory(LineNumberFactory.get(codeArea));
codeArea.setEditable(false);
}
public VirtualizedScrollPane<?> scrollPane() {
return scrollPane;
}
public void setContent(String code) {
codeArea.replaceText(0, 0, code);
}
} Then, I bind them together like this: CodeViewer left = ...;
CodeViewer right = ...;
left.scrollPane().estimatedScrollXProperty()
.bindBidirectional(right.scrollPane().estimatedScrollXProperty());
left.scrollPane().estimatedScrollYProperty()
.bindBidirectional(right.scrollPane().estimatedScrollYProperty());
left.scrollPane().setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); This almost does what's wanted. I can drag the vertical scrollbar and the two panes scroll together; but sometimes when I let go of the thumb the bidirectional binding causes a logic loop and it starts jittering between two positions uncontrollable, or slowly scrolling to the bottom uncontrollably. |
Yeah, the problem is that the scroll x/y property is an estimate and is continuously being updated/recalculated as new cells/lines come into and leave the view port. When bound bidirectionally this creates an unstable value changing environment that easily gets messed up. So we need to resort to some trickery to try and stabilize the situation. Here's my initial attempt using your CodeViewer above: Var<Double> leftScrollY = left.scrollPane().estimatedScrollYProperty();
Var<Double> rightScrollY = right.scrollPane().estimatedScrollYProperty();
boolean[] isBusy = new boolean[2];
final int LEFT = 0, RIGHT = 1;
leftScrollY.addListener( (ob,ov,nv) ->
{
isBusy[LEFT] = true;
if ( ! isBusy[RIGHT] ) rightScrollY.setValue( nv );
isBusy[LEFT] = false;
});
rightScrollY.addListener( (ob,ov,nv) ->
{
isBusy[RIGHT] = true;
if ( ! isBusy[LEFT] ) leftScrollY.setValue( nv );
isBusy[RIGHT] = false;
}); Hopefully this works for you .... |
That appears to do it! Thanks very much! I'll get back to you if I run into any surprises. |
So, the above still seems to be working, but while debugging something else I ran into some behavior I didn't expect. I've instrumented the above listeners as follows: leftScrollY.addListener((ob,ov,nv) -> {
isBusy[LEFT] = true;
if (!isBusy[RIGHT] && rightScrollY.getValue() != nv.doubleValue()) {
System.out.println("leftScrollY: scrolling right=" +
rightScrollY.getValue() + " to " + nv);
rightScrollY.setValue( nv );
System.out.println("leftScrollY: done");
}
isBusy[LEFT] = false;
});
rightScrollY.addListener((ob,ov,nv) -> {
isBusy[RIGHT] = true;
if (!isBusy[LEFT] && leftScrollY.getValue() != nv.doubleValue()) {
System.out.println("rightScrollY: scrolling left=" +
leftScrollY.getValue() + " to " + nv);
leftScrollY.setValue( nv );
System.out.println("rightScrollY: done:");
}
isBusy[RIGHT] = false;
}); When the user navigates from one change in the diff view to the next, I scroll the paired CodeAreas by calling
The number of calls back and forth varies; I've counted as many as 9. Also, the order of the messages indicates that Or maybe Dunno. This looks like a problem waiting to happen, so I wanted to pass it along. |
And in fact, calling
|
Maybe if I disabled the leftScrollY/rightScrollY listeners before calling showParagraphAtTop for both CodeAreas, and then re-enabled them via Platform.runLater() (and so, after everything had settled)? |
Aha! The showParagraphAtTop issue was my fault. My code has been setting a special style on the paragraphGraphic for the selected lines to indicate the selection; and the prototype has been doing this by calling recreateParagraphGraphic for every paragraph in the CodeArea. I disabled that, and no more scrolling problem. |
Whoops! That helped; but it didn't solve the problem. Programmatically scrolling still causes occasional problems. I've tried disabling the leftScrollY/rightScrollY listeners while calling showParagraphTop (in several different ways), but it doesn't get the job done. The problem seems to be that setting the property, e.g.,
It appears that the call to Is there a way to just set the scroll value and make it stick immediately? Failing that, is there a way to use |
Sorry for the delayed response, I now have time again to take another look at this ..... In order to see if it's the ScrollPane that's at fault could you please try and test what happens if you put both the Also make the following change: Var<Double> leftScrollY = left.estimatedScrollYProperty(); // remove: ~scrollPane()~
Var<Double> rightScrollY = right.estimatedScrollYProperty(); // remove: ~scrollPane()~ Thanks |
So I made this change, and yes, I'm still seeing bizarre behavior. Sometimes it does the right thing, sometimes it does something slightly wrong, and sometimes it is just way off. |
Thanks, I'll work on it again on Friday and hopefully figure something out. |
Thanks! I persist in thinking that there are some timing-related things going on: that when I click (and how often) might also be having an effect. But for the experiment this morning I made sure to trigger the programmatic scroll slowly and deliberately, so as to make sure that the timing of my clicks wasn't part of the problem. |
Thank you! |
I'm wanting to implement a two-column diff viewer based on RichTextFX's CodeArea; and to do that I need two CodeAreas side-by-side that are scrolled simultaneously. More specifically:
Is this possible with the current version? What's the cleanest way to do it?
The text was updated successfully, but these errors were encountered: