diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/CaretNode.java b/richtextfx/src/main/java/org/fxmisc/richtext/CaretNode.java index 94c2bf21..bcf10026 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/CaretNode.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/CaretNode.java @@ -216,7 +216,7 @@ public CaretNode(String name, GenericStyledArea area, SuspendableNo dep } private void handleContentChange(List list) { - int newPosition = new CaretPositionChange().apply(getPosition(), list); + int newPosition = new CaretPositionChange(getPosition()).apply(list); if (newPosition != getPosition()) { moveTo(newPosition); } diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/CaretPositionChange.java b/richtextfx/src/main/java/org/fxmisc/richtext/CaretPositionChange.java index b6da7714..ff968b87 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/CaretPositionChange.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/CaretPositionChange.java @@ -5,40 +5,48 @@ import java.util.List; class CaretPositionChange { + private int position; + + /** @param position the current caret position */ + public CaretPositionChange(int position) { + this.position = position; + } + /** * Goes through the list of change and update the caret position accordingly - * @param position the current caret position - * @param list the list of changes * @return the new caret position */ - public int apply(int position, List list) { - for (PlainTextChange plainTextChange : list) { - int netLength = plainTextChange.getNetLength(); - if (netLength != 0) { - int indexOfChange = plainTextChange.getPosition(); - // in case of a replacement: "hello there" -> "hi." - int endOfChange = indexOfChange + Math.abs(netLength); - - /* - "->" means add (positive) netLength to position - "<-" means add (negative) netLength to position - "x" means don't update position + public int apply(List changes) { + changes.forEach(this::applyFor); + return position; + } - "+c" means caret was included in the deleted portion of content - "-c" means caret was not included in the deleted portion of content - Before/At/After means indexOfChange "<" / "==" / ">" position + private void applyFor(PlainTextChange plainTextChange) { + int netLength = plainTextChange.getNetLength(); + if (netLength != 0) { + int changeStart = plainTextChange.getPosition(); + int changeEnd = changeStart + Math.abs(netLength); + position = applyChange(position, changeStart, changeEnd, netLength); + } + } - | Before +c | Before -c | At | After - -------+---------------+-----------+----+------ - Add | N/A | -> | -> | x - Delete | indexOfChange | <- | x | x - */ - if (indexOfChange == position && netLength > 0) { - position = position + netLength; - } else if (indexOfChange < position) { - position = position < endOfChange ? indexOfChange : position + netLength; - } - } + /** + *
    + *
  • If the position is after the interval of the change, the caret moves left/right if it's a net removal/addition.
  • + *
  • If it's within the change interval, the caret move back at the start of the change.
  • + *
  • If it's equal to the start position, the caret is moved at the end of the changed (it cannot be moved backwards + * if it's a removal of text, hence capping to 0).
  • + *
+ */ + private static int applyChange(int position, int changeStart, int changeEnd, int netLength) { + if(position >= changeEnd) { + position += netLength; + } + else if(position > changeStart) { + position = changeStart; + } + else if(position == changeStart) { + position += Math.max(0, netLength); } return position; } diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/SelectionChange.java b/richtextfx/src/main/java/org/fxmisc/richtext/SelectionChange.java index da354952..bd804929 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/SelectionChange.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/SelectionChange.java @@ -4,7 +4,10 @@ import java.util.List; +// TODO SMA can it be that CaretPositionChange is the same as this one but for start == end ? class SelectionChange { + private int start, end; + // TODO -> to be replaced by state of this class start(), end() public static class Range { private final int start, end; @@ -23,45 +26,37 @@ public int end() { } } - public Range apply(List list, int selectStart, int selectEnd) { - for (PlainTextChange plainTextChange : list) { - int changeLength = plainTextChange.getNetLength(); - int indexOfChange = plainTextChange.getPosition(); - // in case of a replacement: "hello there" -> "hi." - int endOfChange = indexOfChange + Math.abs(changeLength); - - /* - "->" means add (positive) netLength to position - "<-" means add (negative) netLength to position - "x" means don't update position + public SelectionChange(int start, int end) { + this.start = start; + this.end = end; + } - "start / end" means what should be done in each case for each anchor if they differ + public Range applyFor(List changes) { + changes.forEach(this::applyFor); + return new Range(start, end); + } - "+a" means one of the anchors was included in the deleted portion of content - "-a" means one of the anchors was not included in the deleted portion of content - Before/At/After means indexOfChange "<" / "==" / ">" position + private void applyFor(PlainTextChange plainTextChange) { + int netLength = plainTextChange.getNetLength(); + int changeStart = plainTextChange.getPosition(); + int changeEnd = changeStart + Math.abs(netLength); + if (start == changeStart) { + start += Math.max(netLength, 0); + } + else { + start = applyChange(start, changeStart, changeEnd, netLength); + } + end = applyChange(end, changeStart, changeEnd, netLength); + start = Math.min(start, end); + } - | Before +a | Before -a | At | After - -------+---------------+-----------+--------+------ - Add | N/A | -> | -> / x | x - Delete | indexOfChange | <- | x | x - */ - if (indexOfChange == selectStart && changeLength > 0) { - selectStart = selectStart + changeLength; - } else if (indexOfChange < selectStart) { - selectStart = selectStart < endOfChange - ? indexOfChange - : selectStart + changeLength; - } - if (indexOfChange < selectEnd) { - selectEnd = selectEnd < endOfChange - ? indexOfChange - : selectEnd + changeLength; - } - if (selectStart > selectEnd) { - selectStart = selectEnd; - } + private static int applyChange(int position, int changeStart, int changeEnd, int netLength) { + if(position >= changeEnd) { + position += netLength; + } + else if(position > changeStart) { + position = changeStart; } - return new Range(selectStart, selectEnd); + return position; } } diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/SelectionImpl.java b/richtextfx/src/main/java/org/fxmisc/richtext/SelectionImpl.java index 40e0859f..e44eee83 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/SelectionImpl.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/SelectionImpl.java @@ -238,7 +238,7 @@ public SelectionImpl(String name, GenericStyledArea area, int startP } private void handleChange(List list) { - SelectionChange.Range newSelection = new SelectionChange().apply(list, getStartPosition(), getEndPosition()); + SelectionChange.Range newSelection = new SelectionChange(getStartPosition(), getEndPosition()).applyFor(list); selectRange(newSelection.start(), newSelection.end()); } diff --git a/richtextfx/src/test/java/org/fxmisc/richtext/CaretPositionChangeTest.java b/richtextfx/src/test/java/org/fxmisc/richtext/CaretPositionChangeTest.java index 25d618ff..5a029919 100644 --- a/richtextfx/src/test/java/org/fxmisc/richtext/CaretPositionChangeTest.java +++ b/richtextfx/src/test/java/org/fxmisc/richtext/CaretPositionChangeTest.java @@ -17,7 +17,7 @@ public class CaretPositionChangeTest { private void checkChange(int before, int after, PlainTextChange... changes) { List changeList = Arrays.stream(changes).collect(Collectors.toList()); - assertEquals(after, new CaretPositionChange().apply(before, changeList)); + assertEquals(after, new CaretPositionChange(before).apply(changeList)); } @ParameterizedTest diff --git a/richtextfx/src/test/java/org/fxmisc/richtext/SelectionChangeTest.java b/richtextfx/src/test/java/org/fxmisc/richtext/SelectionChangeTest.java index fef39706..bc0ffbe5 100644 --- a/richtextfx/src/test/java/org/fxmisc/richtext/SelectionChangeTest.java +++ b/richtextfx/src/test/java/org/fxmisc/richtext/SelectionChangeTest.java @@ -12,7 +12,7 @@ public class SelectionChangeTest { private void checkChange(int startBefore, int endBefore, int startAfter, int endAfter, PlainTextChange... changes) { List changeList = Arrays.stream(changes).collect(Collectors.toList()); - SelectionChange.Range range = new SelectionChange().apply(changeList, startBefore, endBefore); + SelectionChange.Range range = new SelectionChange(startBefore, endBefore).applyFor(changeList); assertEquals(startAfter, range.start(), "Start does not match"); assertEquals(endAfter, range.end(), "End does not match"); }