better scrolling

This commit is contained in:
Radek Davidek 2026-02-10 19:26:47 +01:00
parent abd2ba15b8
commit 8279f1bc3d

View File

@ -725,9 +725,23 @@ public class FilePanelTab extends JPanel {
JScrollBar hBar = sp.getHorizontalScrollBar(); JScrollBar hBar = sp.getHorizontalScrollBar();
if (hBar != null && hBar.isVisible()) { if (hBar != null && hBar.isVisible()) {
int rotation = e.getWheelRotation(); int rotation = e.getWheelRotation();
// Scroll by a larger amount for significantly faster movement int colWidth = fileTable.getColumnModel().getColumn(0).getPreferredWidth();
int amount = rotation * hBar.getUnitIncrement() * 10; if (colWidth > 0) {
hBar.setValue(hBar.getValue() + amount); // Calculate current value and round to nearest column to ensure snapping
int currentVal = hBar.getValue();
int currentCol = (int) Math.round((double) currentVal / colWidth);
// Scroll by 2 columns per notch for speed but keep it aligned
int nextCol = currentCol + (rotation * 2);
int newVal = nextCol * colWidth;
// Bounds checking
int maxVal = hBar.getMaximum() - hBar.getVisibleAmount();
if (newVal < 0) newVal = 0;
if (newVal > maxVal) newVal = maxVal;
hBar.setValue(newVal);
}
} }
} }
e.consume(); e.consume();
@ -2706,33 +2720,9 @@ public class FilePanelTab extends JPanel {
return icon; return icon;
} }
private void updateColumnWidths() { private int calculateCurrentColumnWidth() {
if (viewMode == ViewMode.FULL) { if (tableModel.items == null || tableModel.items.isEmpty()) return 0;
if (fileTable.getColumnModel().getColumnCount() == 3) {
fileTable.getColumnModel().getColumn(0).setPreferredWidth(300);
fileTable.getColumnModel().getColumn(1).setPreferredWidth(100);
fileTable.getColumnModel().getColumn(2).setPreferredWidth(150);
}
} else if (viewMode == ViewMode.BRIEF) {
int columnCount = tableModel.getColumnCount();
if (columnCount == 0) {
return;
}
// If ColumnModel has not yet adapted to the new structure (e.g. after fireTableStructureChanged()),
// wait and try to set renderers later - otherwise new columns won't get a renderer and icons won't
// show up in FULL mode.
if (fileTable.getColumnModel().getColumnCount() != columnCount) {
SwingUtilities.invokeLater(() -> updateColumnRenderers());
return;
}
// Turn off auto-resize so preferred widths are honored and horizontal scrolling appears
fileTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
// Compute the preferred column width based on the longest displayed
// name in the current directory (include brackets for directories)
// plus the widest icon width and some padding.
java.awt.FontMetrics fm = fileTable.getFontMetrics(fileTable.getFont()); java.awt.FontMetrics fm = fileTable.getFontMetrics(fileTable.getFont());
int maxTextWidth = 0; int maxTextWidth = 0;
int maxIconWidth = 0; int maxIconWidth = 0;
@ -2770,11 +2760,59 @@ public class FilePanelTab extends JPanel {
// Add icon width and padding (left/right + gap between icon and text) // Add icon width and padding (left/right + gap between icon and text)
int padding = 36; // reasonable extra space int padding = 36; // reasonable extra space
int columnWidth = maxTextWidth + maxIconWidth + padding; return maxTextWidth + maxIconWidth + padding;
}
private void updateColumnWidths() {
if (viewMode == ViewMode.FULL) {
if (fileTable.getColumnModel().getColumnCount() == 3) {
fileTable.getColumnModel().getColumn(0).setPreferredWidth(300);
fileTable.getColumnModel().getColumn(1).setPreferredWidth(100);
fileTable.getColumnModel().getColumn(2).setPreferredWidth(150);
}
} else if (viewMode == ViewMode.BRIEF) {
int columnCount = tableModel.getColumnCount();
if (columnCount == 0) {
return;
}
// If ColumnModel has not yet adapted to the new structure (e.g. after fireTableStructureChanged()),
// wait and try to set renderers later - otherwise new columns won't get a renderer and icons won't
// show up in FULL mode.
if (fileTable.getColumnModel().getColumnCount() != columnCount) {
SwingUtilities.invokeLater(() -> updateColumnRenderers());
return;
}
// Turn off auto-resize so preferred widths are honored and horizontal scrolling appears
fileTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
int columnWidth = calculateCurrentColumnWidth();
int viewportWidth = fileTable.getParent().getWidth();
for (int i = 0; i < columnCount; i++) { for (int i = 0; i < columnCount; i++) {
fileTable.getColumnModel().getColumn(i).setPreferredWidth(columnWidth); fileTable.getColumnModel().getColumn(i).setPreferredWidth(columnWidth);
} }
// Adjust the very last column to make the total table width such that
// the maximum scroll position is a multiple of columnWidth.
if (tableModel.extraColumns > 0 && columnWidth > 0 && viewportWidth > 0) {
int actualDataWidth = tableModel.briefColumns * columnWidth;
int k = (int) Math.ceil((double) (actualDataWidth - viewportWidth) / columnWidth);
int targetTotalWidth = k * columnWidth + viewportWidth;
int widthOfOthers = (columnCount - 1) * columnWidth;
int lastColWidth = targetTotalWidth - widthOfOthers;
if (lastColWidth > 0) {
fileTable.getColumnModel().getColumn(columnCount - 1).setPreferredWidth(lastColWidth);
}
}
// Ensure horizontal scrollbar snaps to column widths
JScrollPane sp = (JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, fileTable);
if (sp != null) {
sp.getHorizontalScrollBar().setUnitIncrement(columnWidth);
}
} }
} }
@ -3191,6 +3229,7 @@ public class FilePanelTab extends JPanel {
private List<FileItem> items = new ArrayList<>(); private List<FileItem> items = new ArrayList<>();
private String[] columnNames = {"Name", "Size", "Date"}; private String[] columnNames = {"Name", "Size", "Date"};
public int briefColumns = 1; public int briefColumns = 1;
public int extraColumns = 0;
public int briefRowsPerColumn = 10; public int briefRowsPerColumn = 10;
public void setItems(List<FileItem> items) { public void setItems(List<FileItem> items) {
@ -3211,6 +3250,7 @@ public class FilePanelTab extends JPanel {
public void calculateBriefLayout() { public void calculateBriefLayout() {
if (items.isEmpty()) { if (items.isEmpty()) {
briefColumns = 1; briefColumns = 1;
extraColumns = 0;
briefRowsPerColumn = 1; briefRowsPerColumn = 1;
return; return;
} }
@ -3221,11 +3261,36 @@ public class FilePanelTab extends JPanel {
if (availableHeight <= 0 || rowHeight <= 0) { if (availableHeight <= 0 || rowHeight <= 0) {
briefRowsPerColumn = Math.max(1, items.size()); briefRowsPerColumn = Math.max(1, items.size());
briefColumns = 1; briefColumns = 1;
extraColumns = 0;
return; return;
} }
briefRowsPerColumn = Math.max(1, availableHeight / rowHeight); briefRowsPerColumn = Math.max(1, availableHeight / rowHeight);
briefColumns = (int) Math.ceil((double) items.size() / briefRowsPerColumn); briefColumns = (int) Math.ceil((double) items.size() / briefRowsPerColumn);
// Calculate extra columns to allow snapping correctly at the end.
// We want the scrolling to stop precisely when the last column is fully visible
// on the right, but also ensure the left-most visible column is snapped to the left edge.
int colWidth = calculateCurrentColumnWidth();
int viewportWidth = fileTable.getParent().getWidth();
if (colWidth > 0 && viewportWidth > 0 && briefColumns > 0) {
int actualWidth = briefColumns * colWidth;
if (actualWidth > viewportWidth) {
// Smallest k such that (k * colWidth + viewportWidth) >= actualWidth
int k = (int) Math.ceil((double) (actualWidth - viewportWidth) / colWidth);
int neededWidth = k * colWidth + viewportWidth;
// How many extra columns of 'colWidth' would it take?
// We'll adjust the last one's width in updateColumnWidths
extraColumns = (int) Math.ceil((double) (neededWidth - actualWidth) / colWidth);
if (extraColumns == 0 && neededWidth > actualWidth) {
extraColumns = 1; // Need at least one to hold the fractional part
}
} else {
extraColumns = 0;
}
} else {
extraColumns = 0;
}
} }
public FileItem getItemFromBriefLayout(int row, int column) { public FileItem getItemFromBriefLayout(int row, int column) {
@ -3233,6 +3298,8 @@ public class FilePanelTab extends JPanel {
return getItem(row); return getItem(row);
} }
if (column >= briefColumns) return null;
int index = column * briefRowsPerColumn + row; int index = column * briefRowsPerColumn + row;
if (index >= 0 && index < items.size()) { if (index >= 0 && index < items.size()) {
return items.get(index); return items.get(index);
@ -3251,7 +3318,7 @@ public class FilePanelTab extends JPanel {
@Override @Override
public int getColumnCount() { public int getColumnCount() {
if (viewMode == ViewMode.BRIEF) { if (viewMode == ViewMode.BRIEF) {
return briefColumns; return briefColumns + extraColumns;
} }
return 3; return 3;
} }