Skip to content

Commit

Permalink
Fix #32: use undeleted visible cell as anchor; ignore replace updates
Browse files Browse the repository at this point in the history
  • Loading branch information
JordanMartinez committed Oct 3, 2017
1 parent d49214b commit 9d8ba44
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 17 deletions.
27 changes: 26 additions & 1 deletion src/main/java/org/fxmisc/flowless/Navigator.java
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,35 @@ private TargetPosition getCurrentPosition() {
}
}

private CellPositionInfo getFirstCellPositionInfo() {
OptionalInt firstVisible = positioner.getFirstVisibleIndex();
if (firstVisible.isPresent()) {
return getCellPositionInfo(firstVisible.getAsInt());
} else {
return new CellPositionInfo(0, 0, 0);
}
}

private CellPositionInfo getLastCellPositionInfo() {
OptionalInt lastVisible = positioner.getLastVisibleIndex();
if (lastVisible.isPresent()) {
return getCellPositionInfo(lastVisible.getAsInt());
} else {
return new CellPositionInfo(0, 0, 0);
}
}

private CellPositionInfo getCellPositionInfo(int itemIndex) {
C cell = positioner.getVisibleCell(itemIndex);
return new CellPositionInfo(itemIndex, orientation.minY(cell), orientation.maxY(cell));
}

private void itemsChanged(QuasiListChange<?> ch) {
for(QuasiListModification<?> mod: ch) {
targetPosition = targetPosition.transformByChange(
mod.getFrom(), mod.getRemovedSize(), mod.getAddedSize());
mod.getFrom(), mod.getRemovedSize(), mod.getAddedSize(),
getFirstCellPositionInfo(), getLastCellPositionInfo()
);
}
requestLayout(); // TODO: could optimize to only request layout if
// target position changed or cells in the viewport
Expand Down
85 changes: 69 additions & 16 deletions src/main/java/org/fxmisc/flowless/TargetPosition.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@ interface TargetPosition {
static TargetPosition BEGINNING = new StartOffStart(0, 0.0);

/**
* When the list of items, those displayed in the viewport, and those that are not, are modified, transforms
* When the list of items (visible and not visible in the viewport) are modified, transforms
* this change to account for those modifications.
*
* @param pos the cell index where the change begins
* @param removedSize the amount of cells that were removed, starting from {@code pos}
* @param addedSize the amount of cells that were added, starting from {@code pos}
* @param firstVisible information stored to quickly make the first visible cell as the new anchor cell if
* current anchor cell is deleted
* @param lastVisible information stored to quickly make the last visible cell as the new anchor cell if
* current anchor cell is deleted
*/
TargetPosition transformByChange(int pos, int removedSize, int addedSize);

TargetPosition transformByChange(int pos, int removedSize, int addedSize, CellPositionInfo firstVisible, CellPositionInfo lastVisible);

TargetPosition scrollBy(double delta);

Expand Down Expand Up @@ -56,14 +59,22 @@ final class StartOffStart implements TargetPosition {
}

@Override
public TargetPosition transformByChange(
int pos, int removedSize, int addedSize) {
public TargetPosition transformByChange(int pos, int removedSize, int addedSize,
CellPositionInfo firstVisible, CellPositionInfo lastVisible) {
if(itemIndex >= pos + removedSize) {
// change before the target item, just update item index
return new StartOffStart(itemIndex - removedSize + addedSize, offsetFromStart);
} else if(itemIndex >= pos) {
// target item deleted, show the first inserted at the target offset
return new StartOffStart(pos, offsetFromStart);
// target item deleted
if (firstVisible.getItemIndex() < pos) {
return new StartOffStart(firstVisible.getItemIndex(), firstVisible.getStart());
} else if (lastVisible.getItemIndex() >= pos + removedSize) {
return new StartOffStart(lastVisible.getItemIndex() - removedSize + addedSize, lastVisible.getStart());
} else if (removedSize == addedSize) {
return this;
} else {
return new StartOffStart(pos, offsetFromStart);
}
} else {
// change after the target item, target position not affected
return this;
Expand Down Expand Up @@ -113,14 +124,22 @@ final class EndOffEnd implements TargetPosition {
}

@Override
public TargetPosition transformByChange(
int pos, int removedSize, int addedSize) {
public TargetPosition transformByChange(int pos, int removedSize, int addedSize,
CellPositionInfo firstVisible, CellPositionInfo lastVisible) {
if(itemIndex >= pos + removedSize) {
// change before the target item, just update item index
return new EndOffEnd(itemIndex - removedSize + addedSize, offsetFromEnd);
} else if(itemIndex >= pos) {
// target item deleted, show the last inserted at the target offset
return new EndOffEnd(pos + addedSize - 1, offsetFromEnd);
// target item deleted
if (firstVisible.getItemIndex() < pos) {
return new EndOffEnd(firstVisible.getItemIndex(), firstVisible.getEnd());
} else if (lastVisible.getItemIndex() >= pos + removedSize) {
return new EndOffEnd(lastVisible.getItemIndex() - removedSize + addedSize, lastVisible.getEnd());
} else if (removedSize == addedSize) {
return this;
} else {
return new EndOffEnd(pos + addedSize - 1, offsetFromEnd);
}
} else {
// change after the target item, target position not affected
return this;
Expand Down Expand Up @@ -154,19 +173,31 @@ final class MinDistanceTo implements TargetPosition {
this.maxY = maxY;
}

MinDistanceTo(int itemIndex, double startOffset, double endOffset) {
this(itemIndex, Offset.fromStart(startOffset), Offset.fromEnd(endOffset));
}

public MinDistanceTo(int itemIndex) {
this(itemIndex, Offset.fromStart(0.0), Offset.fromEnd(0.0));
this(itemIndex, 0.0, 0.0);
}

@Override
public TargetPosition transformByChange(
int pos, int removedSize, int addedSize) {
public TargetPosition transformByChange(int pos, int removedSize, int addedSize,
CellPositionInfo firstVisible, CellPositionInfo lastVisible) {
if(itemIndex >= pos + removedSize) {
// change before the target item, just update item index
return new MinDistanceTo(itemIndex - removedSize + addedSize, minY, maxY);
} else if(itemIndex >= pos) {
// target item deleted, show the first inserted
return new MinDistanceTo(pos, Offset.fromStart(0.0), Offset.fromEnd(0.0));
// target item deleted
if (firstVisible.getItemIndex() < pos) {
return new MinDistanceTo(firstVisible.getItemIndex(), firstVisible.getStart(), firstVisible.getEnd());
} else if (lastVisible.getItemIndex() >= pos + removedSize) {
return new MinDistanceTo(lastVisible.getItemIndex() - removedSize + addedSize, lastVisible.getStart(), lastVisible.getEnd());
} else if (removedSize == addedSize) {
return this;
} else {
return new MinDistanceTo(pos, Offset.fromStart(0.0), Offset.fromEnd(0.0));
}
} else {
// change after the target item, target position not affected
return this;
Expand Down Expand Up @@ -225,4 +256,26 @@ public boolean isFromEnd() {
public Offset add(double delta) {
return new Offset(offset + delta, fromStart);
}
}

/**
* Stores the information necessary for updating the anchor cell to point to an undeleted visible cell
* in place of the current anchor cell that will be deleted in
* {@link TargetPosition#transformByChange(int, int, int, CellPositionInfo, CellPositionInfo)}.
*/
final class CellPositionInfo {
private final int itemIndex;
final int getItemIndex() { return itemIndex; }

private final double start;
final double getStart() { return start; }

private final double end;
final double getEnd() { return end; }

public CellPositionInfo(int itemIndex, double offsetFromStart, double offsetFromEnd) {
this.itemIndex = itemIndex;
this.start = offsetFromStart;
this.end = offsetFromEnd;
}
}

0 comments on commit 9d8ba44

Please sign in to comment.