Skip to content

Commit aaee089

Browse files
authored
Fix scrollbar jump (#918)
* Improved visibleParToAllParIndex, Reverted fix for #795 * Improved method allParToVisibleParIndex * Added exception trap and test for #788
1 parent e404747 commit aaee089

File tree

4 files changed

+53
-80
lines changed

4 files changed

+53
-80
lines changed

richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/ParagraphIndexMappingTests.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,21 @@ public void all_par_to_visible_par_index_is_correct() {
3030
assertEquals(Optional.of(area.getVisibleParagraphs().size() - 1), area.allParToVisibleParIndex(LAST_PAR_INDEX));
3131
}
3232

33+
@Test
34+
public void all_par_to_visible_par_index_after_replace() {
35+
interact(() -> {
36+
area.clear();
37+
area.replaceText( "123\nabc" );
38+
});
39+
40+
interact(() -> area.replaceText( "123\nxyz" ));
41+
42+
interact(() -> {
43+
assertEquals(Optional.of(1), area.allParToVisibleParIndex(1));
44+
assertEquals(Optional.of(0), area.allParToVisibleParIndex(0));
45+
});
46+
}
47+
3348
@Test
3449
public void visible_par_to_all_par_index_is_correct() {
3550
interact(() -> area.showParagraphAtTop(0));

richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/ReplaceTextTests.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,37 @@
11
package org.fxmisc.richtext.api;
22

33
import org.fxmisc.richtext.InlineCssTextAreaAppTest;
4+
import static org.junit.Assert.assertEquals;
45
import org.junit.Test;
56

67
public class ReplaceTextTests extends InlineCssTextAreaAppTest {
78

9+
@Test
10+
public void replaceText_does_not_cause_index_out_of_bounds_exception()
11+
{
12+
interact( () ->
13+
{
14+
String test = "abc\n";
15+
area.replaceText( test );
16+
area.insertText( 0, test +"def\n" );
17+
18+
// An IndexOutOfBoundsException occurs when trimming MaterializedListModification
19+
// in GenericEditableStyledDocumentBase.ParagraphList.observeInputs()
20+
area.replaceText( test );
21+
assertEquals( test, area.getText() );
22+
23+
area.clear();
24+
area.replaceText( test );
25+
area.appendText( test +"def" );
26+
27+
// An IndexOutOfBoundsException occurs when trimming MaterializedListModification
28+
// in GenericEditableStyledDocumentBase.ParagraphList.observeInputs()
29+
area.replaceText( test +"def" );
30+
assertEquals( test +"def", area.getText() );
31+
});
32+
33+
}
34+
835
@Test
936
public void deselect_before_replaceText_does_not_cause_index_out_of_bounds_exception()
1037
{

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

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -864,10 +864,15 @@ public final Optional<Integer> allParToVisibleParIndex(int allParIndex) {
864864
getParagraphs().size() - 1, allParIndex)
865865
);
866866
}
867-
Paragraph<PS, SEG, S> p = getParagraph(allParIndex);
868-
for (int index = 0; index < getVisibleParagraphs().size(); index++) {
869-
if (getVisibleParagraphs().get(index) == p) {
870-
return Optional.of(index);
867+
List<Cell<Paragraph<PS, SEG, S>, ParagraphBox<PS, SEG, S>>> visibleList = virtualFlow.visibleCells();
868+
int firstVisibleParIndex = visibleList.get( 0 ).getNode().getIndex();
869+
int targetIndex = allParIndex - firstVisibleParIndex;
870+
871+
if ( allParIndex >= firstVisibleParIndex && targetIndex < visibleList.size() )
872+
{
873+
if ( visibleList.get( targetIndex ).getNode().getIndex() == allParIndex )
874+
{
875+
return Optional.of( targetIndex );
871876
}
872877
}
873878
return Optional.empty();
@@ -884,13 +889,7 @@ public final int visibleParToAllParIndex(int visibleParIndex) {
884889
getVisibleParagraphs().size() - 1, visibleParIndex)
885890
);
886891
}
887-
Paragraph<PS, SEG, S> visibleP = getVisibleParagraphs().get(visibleParIndex);
888-
for (int index = 0; index < getParagraphs().size(); index++) {
889-
if (getParagraph(index) == visibleP) {
890-
return index;
891-
}
892-
}
893-
throw new AssertionError("Unreachable code");
892+
return virtualFlow.visibleCells().get( visibleParIndex ).getNode().getIndex();
894893
}
895894

896895
@Override

richtextfx/src/main/java/org/fxmisc/richtext/model/GenericEditableStyledDocumentBase.java

Lines changed: 1 addition & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ protected Subscription observeInputs() {
4646
return parChangesList.subscribe(list -> {
4747
ListChangeAccumulator<Paragraph<PS, SEG, S>> accumulator = new ListChangeAccumulator<>();
4848
for (MaterializedListModification<Paragraph<PS, SEG, S>> mod : list) {
49-
mod = trim(mod);
49+
try { mod = mod.trim(); } catch ( IndexOutOfBoundsException EX ) {}
5050

5151
// add the quasiListModification itself, not as a quasiListChange, in case some overlap
5252
accumulator.add(QuasiListModification.create(mod.getFrom(), mod.getRemoved(), mod.getAddedSize()));
@@ -221,72 +221,4 @@ private void updateMulti(
221221
});
222222
}
223223

224-
/**
225-
* Copy of org.reactfx.collection.MaterializedListModification.trim()
226-
* that uses reference comparison instead of equals().
227-
*/
228-
private MaterializedListModification<Paragraph<PS, SEG, S>> trim(MaterializedListModification<Paragraph<PS, SEG, S>> mod) {
229-
return commonPrefixSuffixLengths(mod.getRemoved(), mod.getAdded()).map((pref, suff) -> {
230-
if(pref == 0 && suff == 0) {
231-
return mod;
232-
} else {
233-
return MaterializedListModification.create(
234-
mod.getFrom() + pref,
235-
mod.getRemoved().subList(pref, mod.getRemovedSize() - suff),
236-
mod.getAdded().subList(pref, mod.getAddedSize() - suff));
237-
}
238-
});
239-
}
240-
241-
/**
242-
* Copy of org.reactfx.util.Lists.commonPrefixSuffixLengths()
243-
* that uses reference comparison instead of equals().
244-
*/
245-
private static Tuple2<Integer, Integer> commonPrefixSuffixLengths(List<?> l1, List<?> l2) {
246-
int n1 = l1.size();
247-
int n2 = l2.size();
248-
249-
if(n1 == 0 || n2 == 0) {
250-
return Tuples.t(0, 0);
251-
}
252-
253-
int pref = commonPrefixLength(l1, l2);
254-
if(pref == n1 || pref == n2) {
255-
return Tuples.t(pref, 0);
256-
}
257-
258-
int suff = commonSuffixLength(l1, l2);
259-
260-
return Tuples.t(pref, suff);
261-
}
262-
263-
/**
264-
* Copy of org.reactfx.util.Lists.commonPrefixLength()
265-
* that uses reference comparison instead of equals().
266-
*/
267-
private static int commonPrefixLength(List<?> l, List<?> m) {
268-
ListIterator<?> i = l.listIterator();
269-
ListIterator<?> j = m.listIterator();
270-
while(i.hasNext() && j.hasNext()) {
271-
if(i.next() != j.next()) {
272-
return i.nextIndex() - 1;
273-
}
274-
}
275-
return i.nextIndex();
276-
}
277-
278-
/**
279-
* Copy of org.reactfx.util.Lists.commonSuffixLength()
280-
* that uses reference comparison instead of equals().
281-
*/
282-
private static int commonSuffixLength(List<?> l, List<?> m) {
283-
ListIterator<?> i = l.listIterator(l.size());
284-
ListIterator<?> j = m.listIterator(m.size());
285-
while(i.hasPrevious() && j.hasPrevious()) {
286-
if(i.previous() != j.previous()) {
287-
return l.size() - i.nextIndex() - 1;
288-
}
289-
}
290-
return l.size() - i.nextIndex();
291-
}
292224
}

0 commit comments

Comments
 (0)