Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions internal/ui/delete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,36 @@ func TestPerformDelete_RemovesCurrentFileAndAdvancesToTheNextOne(t *testing.T) {
settleToast(t, v)
}

// TestPerformDelete_OnLastImageOfMultipleAdvancesWithoutPanicking is a
// regression test: deleting while positioned on the last image of a
// multi-file set left v.index equal to the new (shrunk) length, so the very
// next CurrentFile() call - performDelete's own "did that empty the set?"
// check - indexed v.files out of range and crashed the whole app.
func TestPerformDelete_OnLastImageOfMultipleAdvancesWithoutPanicking(t *testing.T) {
uitest.StubTrashMove(t, func(path string) error { return os.Remove(path) })
v := newTestViewer(t)
a := uitest.TempJPEGURI(t, "a.jpg", 4, 4, color.White)
b := uitest.TempJPEGURI(t, "b.jpg", 4, 4, color.White)
dropAndWait(t, v, a, b)

v.handleKeyEvent(&fyne.KeyEvent{Name: fyne.KeyRight})
waitUntilLoaded(t, v)
if v.index != 1 {
t.Fatalf("setup: index = %d, want 1 (on b.jpg, the last image)", v.index)
}

confirmDelete(t, v)
waitUntilLoaded(t, v)

if len(v.files) != 1 || v.files[0].String() != a.String() {
t.Fatalf("files = %v, want just a.jpg left", v.files)
}
if v.index != 0 {
t.Errorf("index = %d, want 0 (a.jpg took b.jpg's slot)", v.index)
}
settleToast(t, v)
}

// TestPerformDelete_LastFileReturnsToEmptyDropzone covers deleting the only
// remaining file: the app should fall back to the empty-state screen, the
// same place a last decode failure already lands it.
Expand Down
8 changes: 8 additions & 0 deletions internal/ui/viewer.go
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,14 @@ func (v *viewer) RemoveFile(i int) {
v.files = append(v.files[:i], v.files[i+1:]...)
v.imgCache.Remove(target.String())

// Callers always remove the file currently at v.index, so once it's
// gone v.index may point past the new end (e.g. deleting the last
// image) - clamp it back onto the shrunk slice, same as attemptLoad's
// wraparound does for the retry path.
if v.index >= len(v.files) {
v.index = len(v.files) - 1
}

for j, u := range v.unsortedFiles {
if u.String() == target.String() {
v.unsortedFiles = append(v.unsortedFiles[:j], v.unsortedFiles[j+1:]...)
Expand Down
Loading