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.io.FileReader;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.Deque;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
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 long archiveExtractTime = 0; // Track time of extraction to detect changes
|
||||||
private File archiveReturnDirectory = null;
|
private File archiveReturnDirectory = null;
|
||||||
private Point archiveReturnViewPosition = null;
|
private Point archiveReturnViewPosition = null;
|
||||||
|
private final Deque<ReturnNavigationState> navigationReturnStates = new ArrayDeque<>();
|
||||||
private boolean inlineRenameActive = false;
|
private boolean inlineRenameActive = false;
|
||||||
private boolean refreshDeferredWhileInlineRename = false;
|
private boolean refreshDeferredWhileInlineRename = false;
|
||||||
private boolean deferredRefreshRequestFocus = false;
|
private boolean deferredRefreshRequestFocus = false;
|
||||||
@ -105,6 +108,20 @@ public class FilePanelTab extends JPanel {
|
|||||||
private FtpProfile ftpProfile;
|
private FtpProfile ftpProfile;
|
||||||
private String ftpCurrentPath;
|
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) {
|
public FilePanelTab(String initialPath) {
|
||||||
this(initialPath, true);
|
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) {
|
private void scrollBriefCellToColumnStart(int row, int column) {
|
||||||
if (fileTable == null || viewMode != ViewMode.BRIEF || row < 0 || column < 0
|
if (fileTable == null || viewMode != ViewMode.BRIEF || row < 0 || column < 0
|
||||||
|| column >= fileTable.getColumnModel().getColumnCount()) {
|
|| column >= fileTable.getColumnModel().getColumnCount()) {
|
||||||
@ -1659,6 +1795,8 @@ public class FilePanelTab extends JPanel {
|
|||||||
} else {
|
} else {
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
alignTableItemsToViewportStart();
|
alignTableItemsToViewportStart();
|
||||||
|
fileTable.revalidate();
|
||||||
|
fileTable.repaint();
|
||||||
if (autoSelectFirst && fileTable.getRowCount() > 0) {
|
if (autoSelectFirst && fileTable.getRowCount() > 0) {
|
||||||
int startIndex = 0;
|
int startIndex = 0;
|
||||||
fileTable.setRowSelectionInterval(startIndex, startIndex);
|
fileTable.setRowSelectionInterval(startIndex, startIndex);
|
||||||
@ -2168,6 +2306,7 @@ public class FilePanelTab extends JPanel {
|
|||||||
if (item.getName().equals("..")) {
|
if (item.getName().equals("..")) {
|
||||||
navigateUp();
|
navigateUp();
|
||||||
} else if (FileOperations.canOpenAsArchive(item.getFile())) {
|
} else if (FileOperations.canOpenAsArchive(item.getFile())) {
|
||||||
|
ReturnNavigationState returnState = rememberReturnStateForItem(item, selectedRow, viewMode == ViewMode.BRIEF ? briefCurrentColumn : 0);
|
||||||
rememberArchiveReturnState(item.getFile());
|
rememberArchiveReturnState(item.getFile());
|
||||||
final File archiveFile = item.getFile();
|
final File archiveFile = item.getFile();
|
||||||
|
|
||||||
@ -2195,11 +2334,13 @@ public class FilePanelTab extends JPanel {
|
|||||||
loadDirectory(temp.toFile());
|
loadDirectory(temp.toFile());
|
||||||
},
|
},
|
||||||
error -> {
|
error -> {
|
||||||
|
discardReturnState(returnState);
|
||||||
clearArchiveReturnState();
|
clearArchiveReturnState();
|
||||||
JOptionPane.showMessageDialog(FilePanelTab.this, error, "Archive Error", JOptionPane.ERROR_MESSAGE);
|
JOptionPane.showMessageDialog(FilePanelTab.this, error, "Archive Error", JOptionPane.ERROR_MESSAGE);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else if (item.isDirectory()) {
|
} else if (item.isDirectory()) {
|
||||||
|
rememberReturnStateForItem(item, selectedRow, viewMode == ViewMode.BRIEF ? briefCurrentColumn : 0);
|
||||||
loadDirectory(item.getFile());
|
loadDirectory(item.getFile());
|
||||||
} else if (item.getFile() != null && item.getFile().isFile()) {
|
} else if (item.getFile() != null && item.getFile().isFile()) {
|
||||||
openFileNative(item.getFile());
|
openFileNative(item.getFile());
|
||||||
@ -2715,6 +2856,7 @@ public class FilePanelTab extends JPanel {
|
|||||||
if (item.getName().equals("..")) {
|
if (item.getName().equals("..")) {
|
||||||
navigateUp();
|
navigateUp();
|
||||||
} else if (FileOperations.canOpenAsArchive(item.getFile())) {
|
} else if (FileOperations.canOpenAsArchive(item.getFile())) {
|
||||||
|
ReturnNavigationState returnState = rememberReturnStateForItem(item, row, viewMode == ViewMode.BRIEF ? col : 0);
|
||||||
rememberArchiveReturnState(item.getFile());
|
rememberArchiveReturnState(item.getFile());
|
||||||
final File archiveFile = item.getFile();
|
final File archiveFile = item.getFile();
|
||||||
|
|
||||||
@ -2732,11 +2874,13 @@ public class FilePanelTab extends JPanel {
|
|||||||
loadDirectory(temp.toFile(), true, true);
|
loadDirectory(temp.toFile(), true, true);
|
||||||
},
|
},
|
||||||
error -> {
|
error -> {
|
||||||
|
discardReturnState(returnState);
|
||||||
clearArchiveReturnState();
|
clearArchiveReturnState();
|
||||||
JOptionPane.showMessageDialog(FilePanelTab.this, error, "Archive Error", JOptionPane.ERROR_MESSAGE);
|
JOptionPane.showMessageDialog(FilePanelTab.this, error, "Archive Error", JOptionPane.ERROR_MESSAGE);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else if (item.isDirectory()) {
|
} else if (item.isDirectory()) {
|
||||||
|
rememberReturnStateForItem(item, row, viewMode == ViewMode.BRIEF ? col : 0);
|
||||||
loadDirectory(item.getFile());
|
loadDirectory(item.getFile());
|
||||||
} else if (item.getFile() != null && item.getFile().isFile()) {
|
} else if (item.getFile() != null && item.getFile().isFile()) {
|
||||||
openFileNative(item.getFile());
|
openFileNative(item.getFile());
|
||||||
@ -3143,6 +3287,7 @@ public class FilePanelTab extends JPanel {
|
|||||||
File parent = currentArchiveSourceFile.getParentFile();
|
File parent = currentArchiveSourceFile.getParentFile();
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
String archiveName = currentArchiveSourceFile.getName();
|
String archiveName = currentArchiveSourceFile.getName();
|
||||||
|
ReturnNavigationState returnState = consumeReturnState(parent, archiveName);
|
||||||
|
|
||||||
// Save any changes made to the archive before leaving (only if modified)
|
// Save any changes made to the archive before leaving (only if modified)
|
||||||
if (FileOperations.supportsArchiveRewrite(currentArchiveSourceFile)) {
|
if (FileOperations.supportsArchiveRewrite(currentArchiveSourceFile)) {
|
||||||
@ -3159,13 +3304,9 @@ public class FilePanelTab extends JPanel {
|
|||||||
deleteTempDirRecursively(currentArchiveTempDir);
|
deleteTempDirRecursively(currentArchiveTempDir);
|
||||||
clearOpenedArchiveSession();
|
clearOpenedArchiveSession();
|
||||||
loadDirectory(parent, false, true, () -> {
|
loadDirectory(parent, false, true, () -> {
|
||||||
// Restore original viewport position and keep focus on archive item.
|
restoreReturnState(returnState, true);
|
||||||
// In BRIEF mode some selection listeners may still run later, so apply once more on EDT tail.
|
|
||||||
restoreArchiveReturnPosition();
|
|
||||||
selectItemByName(archiveName, true, false);
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
restoreArchiveReturnPosition();
|
restoreReturnState(returnState, true);
|
||||||
selectItemByName(archiveName, true, false);
|
|
||||||
clearArchiveReturnState();
|
clearArchiveReturnState();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -3178,10 +3319,18 @@ public class FilePanelTab extends JPanel {
|
|||||||
File parent = currentDirectory.getParentFile();
|
File parent = currentDirectory.getParentFile();
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
String previousDirName = currentDirectory.getName();
|
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
|
if (returnState != null) {
|
||||||
SwingUtilities.invokeLater(() -> selectItemByName(previousDirName));
|
SwingUtilities.invokeLater(() -> restoreReturnState(returnState, true));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user