fixed panel scrolling
This commit is contained in:
parent
caac494639
commit
7f708c3337
@ -28,8 +28,10 @@ import java.io.InputStreamReader;
|
||||
import java.io.FileReader;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Deque;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
@ -90,6 +92,7 @@ public class FilePanelTab extends JPanel {
|
||||
private long archiveExtractTime = 0; // Track time of extraction to detect changes
|
||||
private File archiveReturnDirectory = null;
|
||||
private Point archiveReturnViewPosition = null;
|
||||
private final Deque<ReturnNavigationState> navigationReturnStates = new ArrayDeque<>();
|
||||
private boolean inlineRenameActive = false;
|
||||
private boolean refreshDeferredWhileInlineRename = false;
|
||||
private boolean deferredRefreshRequestFocus = false;
|
||||
@ -105,6 +108,20 @@ public class FilePanelTab extends JPanel {
|
||||
private FtpProfile ftpProfile;
|
||||
private String ftpCurrentPath;
|
||||
|
||||
private static final class ReturnNavigationState {
|
||||
private final File parentDirectory;
|
||||
private final String itemName;
|
||||
private final Point itemOffsetInView;
|
||||
private final Point fallbackViewPosition;
|
||||
|
||||
private ReturnNavigationState(File parentDirectory, String itemName, Point itemOffsetInView, Point fallbackViewPosition) {
|
||||
this.parentDirectory = parentDirectory;
|
||||
this.itemName = itemName;
|
||||
this.itemOffsetInView = itemOffsetInView;
|
||||
this.fallbackViewPosition = fallbackViewPosition;
|
||||
}
|
||||
}
|
||||
|
||||
public FilePanelTab(String initialPath) {
|
||||
this(initialPath, true);
|
||||
}
|
||||
@ -1520,6 +1537,125 @@ public class FilePanelTab extends JPanel {
|
||||
}
|
||||
}
|
||||
|
||||
private ReturnNavigationState rememberReturnStateForItem(FileItem item, int row, int column) {
|
||||
if (item == null || item.getName() == null || currentDirectory == null || fileTable == null || row < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Rectangle cell = fileTable.getCellRect(row, Math.max(0, column), true);
|
||||
Rectangle visible = fileTable.getVisibleRect();
|
||||
Point offset = new Point(cell.x - visible.x, cell.y - visible.y);
|
||||
Point fallback = visible.getLocation();
|
||||
|
||||
ReturnNavigationState state = new ReturnNavigationState(
|
||||
currentDirectory,
|
||||
item.getName(),
|
||||
offset,
|
||||
fallback
|
||||
);
|
||||
navigationReturnStates.push(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
private void discardReturnState(ReturnNavigationState state) {
|
||||
if (state == null) {
|
||||
return;
|
||||
}
|
||||
navigationReturnStates.removeFirstOccurrence(state);
|
||||
}
|
||||
|
||||
private ReturnNavigationState consumeReturnState(File parentDirectory, String itemName) {
|
||||
if (parentDirectory == null || itemName == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
java.util.Iterator<ReturnNavigationState> it = navigationReturnStates.iterator();
|
||||
while (it.hasNext()) {
|
||||
ReturnNavigationState state = it.next();
|
||||
if (parentDirectory.equals(state.parentDirectory) && itemName.equals(state.itemName)) {
|
||||
it.remove();
|
||||
return state;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void restoreReturnState(ReturnNavigationState state, boolean requestFocus) {
|
||||
if (state == null || fileTable == null || currentDirectory == null) {
|
||||
return;
|
||||
}
|
||||
if (!currentDirectory.equals(state.parentDirectory)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < tableModel.items.size(); i++) {
|
||||
FileItem item = tableModel.items.get(i);
|
||||
if (item == null || !state.itemName.equalsIgnoreCase(item.getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (viewMode == ViewMode.BRIEF) {
|
||||
int column = i / tableModel.briefRowsPerColumn;
|
||||
int row = i % tableModel.briefRowsPerColumn;
|
||||
selectBriefItemWithoutAutoScroll(row, column);
|
||||
restoreViewportForCell(row, column, state);
|
||||
final int finalRow = row;
|
||||
final int finalColumn = column;
|
||||
SwingUtilities.invokeLater(() -> restoreViewportForCell(finalRow, finalColumn, state));
|
||||
} else {
|
||||
selectFullRowWithoutAutoScroll(i);
|
||||
restoreViewportForCell(i, 0, state);
|
||||
final int finalRow = i;
|
||||
SwingUtilities.invokeLater(() -> restoreViewportForCell(finalRow, 0, state));
|
||||
}
|
||||
|
||||
fileTable.repaint();
|
||||
if (requestFocus) {
|
||||
fileTable.requestFocusInWindow();
|
||||
}
|
||||
updateStatus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void restoreViewportForCell(int row, int column, ReturnNavigationState state) {
|
||||
if (fileTable == null || state == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Rectangle cell = fileTable.getCellRect(row, Math.max(0, column), true);
|
||||
Container parent = fileTable.getParent();
|
||||
if (parent instanceof JViewport viewport) {
|
||||
if (state.fallbackViewPosition != null) {
|
||||
viewport.setViewPosition(clampViewPosition(viewport, new Point(state.fallbackViewPosition)));
|
||||
}
|
||||
|
||||
Rectangle visibleAfterFallback = viewport.getViewRect();
|
||||
if (visibleAfterFallback.intersects(cell)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Point target = (state.itemOffsetInView != null)
|
||||
? new Point(cell.x - state.itemOffsetInView.x, cell.y - state.itemOffsetInView.y)
|
||||
: new Point(state.fallbackViewPosition != null ? state.fallbackViewPosition : new Point());
|
||||
viewport.setViewPosition(clampViewPosition(viewport, target));
|
||||
} else if (state.fallbackViewPosition != null) {
|
||||
fileTable.scrollRectToVisible(new Rectangle(state.fallbackViewPosition.x, state.fallbackViewPosition.y, 1, 1));
|
||||
}
|
||||
}
|
||||
|
||||
private Point clampViewPosition(JViewport viewport, Point target) {
|
||||
Dimension pref = fileTable.getPreferredSize();
|
||||
int contentW = Math.max(fileTable.getWidth(), pref != null ? pref.width : 0);
|
||||
int contentH = Math.max(fileTable.getHeight(), pref != null ? pref.height : 0);
|
||||
int maxX = Math.max(0, contentW - viewport.getWidth());
|
||||
int maxY = Math.max(0, contentH - viewport.getHeight());
|
||||
return new Point(
|
||||
Math.max(0, Math.min(target.x, maxX)),
|
||||
Math.max(0, Math.min(target.y, maxY))
|
||||
);
|
||||
}
|
||||
|
||||
private void scrollBriefCellToColumnStart(int row, int column) {
|
||||
if (fileTable == null || viewMode != ViewMode.BRIEF || row < 0 || column < 0
|
||||
|| column >= fileTable.getColumnModel().getColumnCount()) {
|
||||
@ -1659,6 +1795,8 @@ public class FilePanelTab extends JPanel {
|
||||
} else {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
alignTableItemsToViewportStart();
|
||||
fileTable.revalidate();
|
||||
fileTable.repaint();
|
||||
if (autoSelectFirst && fileTable.getRowCount() > 0) {
|
||||
int startIndex = 0;
|
||||
fileTable.setRowSelectionInterval(startIndex, startIndex);
|
||||
@ -2168,6 +2306,7 @@ public class FilePanelTab extends JPanel {
|
||||
if (item.getName().equals("..")) {
|
||||
navigateUp();
|
||||
} else if (FileOperations.canOpenAsArchive(item.getFile())) {
|
||||
ReturnNavigationState returnState = rememberReturnStateForItem(item, selectedRow, viewMode == ViewMode.BRIEF ? briefCurrentColumn : 0);
|
||||
rememberArchiveReturnState(item.getFile());
|
||||
final File archiveFile = item.getFile();
|
||||
|
||||
@ -2195,11 +2334,13 @@ public class FilePanelTab extends JPanel {
|
||||
loadDirectory(temp.toFile());
|
||||
},
|
||||
error -> {
|
||||
discardReturnState(returnState);
|
||||
clearArchiveReturnState();
|
||||
JOptionPane.showMessageDialog(FilePanelTab.this, error, "Archive Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
);
|
||||
} else if (item.isDirectory()) {
|
||||
rememberReturnStateForItem(item, selectedRow, viewMode == ViewMode.BRIEF ? briefCurrentColumn : 0);
|
||||
loadDirectory(item.getFile());
|
||||
} else if (item.getFile() != null && item.getFile().isFile()) {
|
||||
openFileNative(item.getFile());
|
||||
@ -2715,6 +2856,7 @@ public class FilePanelTab extends JPanel {
|
||||
if (item.getName().equals("..")) {
|
||||
navigateUp();
|
||||
} else if (FileOperations.canOpenAsArchive(item.getFile())) {
|
||||
ReturnNavigationState returnState = rememberReturnStateForItem(item, row, viewMode == ViewMode.BRIEF ? col : 0);
|
||||
rememberArchiveReturnState(item.getFile());
|
||||
final File archiveFile = item.getFile();
|
||||
|
||||
@ -2732,11 +2874,13 @@ public class FilePanelTab extends JPanel {
|
||||
loadDirectory(temp.toFile(), true, true);
|
||||
},
|
||||
error -> {
|
||||
discardReturnState(returnState);
|
||||
clearArchiveReturnState();
|
||||
JOptionPane.showMessageDialog(FilePanelTab.this, error, "Archive Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
);
|
||||
} else if (item.isDirectory()) {
|
||||
rememberReturnStateForItem(item, row, viewMode == ViewMode.BRIEF ? col : 0);
|
||||
loadDirectory(item.getFile());
|
||||
} else if (item.getFile() != null && item.getFile().isFile()) {
|
||||
openFileNative(item.getFile());
|
||||
@ -3143,6 +3287,7 @@ public class FilePanelTab extends JPanel {
|
||||
File parent = currentArchiveSourceFile.getParentFile();
|
||||
if (parent != null) {
|
||||
String archiveName = currentArchiveSourceFile.getName();
|
||||
ReturnNavigationState returnState = consumeReturnState(parent, archiveName);
|
||||
|
||||
// Save any changes made to the archive before leaving (only if modified)
|
||||
if (FileOperations.supportsArchiveRewrite(currentArchiveSourceFile)) {
|
||||
@ -3159,13 +3304,9 @@ public class FilePanelTab extends JPanel {
|
||||
deleteTempDirRecursively(currentArchiveTempDir);
|
||||
clearOpenedArchiveSession();
|
||||
loadDirectory(parent, false, true, () -> {
|
||||
// Restore original viewport position and keep focus on archive item.
|
||||
// In BRIEF mode some selection listeners may still run later, so apply once more on EDT tail.
|
||||
restoreArchiveReturnPosition();
|
||||
selectItemByName(archiveName, true, false);
|
||||
restoreReturnState(returnState, true);
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
restoreArchiveReturnPosition();
|
||||
selectItemByName(archiveName, true, false);
|
||||
restoreReturnState(returnState, true);
|
||||
clearArchiveReturnState();
|
||||
});
|
||||
});
|
||||
@ -3178,10 +3319,18 @@ public class FilePanelTab extends JPanel {
|
||||
File parent = currentDirectory.getParentFile();
|
||||
if (parent != null) {
|
||||
String previousDirName = currentDirectory.getName();
|
||||
loadDirectory(parent, false);
|
||||
ReturnNavigationState returnState = consumeReturnState(parent, previousDirName);
|
||||
loadDirectory(parent, false, true, () -> {
|
||||
if (returnState != null) {
|
||||
restoreReturnState(returnState, true);
|
||||
} else {
|
||||
selectItemByName(previousDirName);
|
||||
}
|
||||
});
|
||||
|
||||
// Select the directory we just left
|
||||
SwingUtilities.invokeLater(() -> selectItemByName(previousDirName));
|
||||
if (returnState != null) {
|
||||
SwingUtilities.invokeLater(() -> restoreReturnState(returnState, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user