fixed inline search

This commit is contained in:
Radek Davidek 2026-04-13 17:09:39 +02:00
parent 1c65fc987f
commit 15eff68abc

View File

@ -86,9 +86,10 @@ public class FilePanelTab extends JPanel {
private boolean active = false; private boolean active = false;
private final Map<String, Long> changeTimestamps = new HashMap<>(); private final Map<String, Long> changeTimestamps = new HashMap<>();
private static final long CHANGE_HIGHLIGHT_DURATION = 500; // 0.5 seconds per item private static final long CHANGE_HIGHLIGHT_DURATION = 500; // 0.5 seconds per item
private String ctrlAltTypeBuffer = ""; private final List<FileItem> allItems = new ArrayList<>();
private long ctrlAltTypeLastTs = 0L; private JTextField filterTextField;
private static final long CTRL_ALT_TYPE_RESET_MS = 1200; private JPanel filterPanel;
private boolean searchModeActive = false;
public FilePanelTab(String initialPath) { public FilePanelTab(String initialPath) {
this(initialPath, true); this(initialPath, true);
@ -344,6 +345,39 @@ public class FilePanelTab extends JPanel {
private void initComponents() { private void initComponents() {
setLayout(new BorderLayout()); setLayout(new BorderLayout());
// Filter panel for search
filterPanel = new JPanel(new BorderLayout());
filterTextField = new JTextField();
filterTextField.getDocument().addDocumentListener(new javax.swing.event.DocumentListener() {
@Override
public void insertUpdate(javax.swing.event.DocumentEvent e) { updateFilter(); }
@Override
public void removeUpdate(javax.swing.event.DocumentEvent e) { updateFilter(); }
@Override
public void changedUpdate(javax.swing.event.DocumentEvent e) { updateFilter(); }
});
filterTextField.addKeyListener(new java.awt.event.KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
exitSearchMode();
e.consume();
} else if (e.getKeyCode() == KeyEvent.VK_ENTER) {
FileItem selected = getFocusedItem();
exitSearchMode();
if (selected != null) {
selectItemByName(selected.getName(), true);
}
e.consume();
} else if (e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_DOWN) {
fileTable.dispatchEvent(e);
}
}
});
filterPanel.add(new JLabel(" Search: "), BorderLayout.WEST);
filterPanel.add(filterTextField, BorderLayout.CENTER);
filterPanel.setVisible(false);
// Table showing files // Table showing files
tableModel = new FileTableModel(); tableModel = new FileTableModel();
// Use a custom JTable subclass to intercept mouse events so that mouse-driven // Use a custom JTable subclass to intercept mouse events so that mouse-driven
@ -942,7 +976,11 @@ public class FilePanelTab extends JPanel {
}); });
add(briefSortPanel, BorderLayout.NORTH); add(briefSortPanel, BorderLayout.NORTH);
add(cardPanel, BorderLayout.CENTER);
JPanel centerPanel = new JPanel(new BorderLayout());
centerPanel.add(cardPanel, BorderLayout.CENTER);
centerPanel.add(filterPanel, BorderLayout.SOUTH);
add(centerPanel, BorderLayout.CENTER);
// Status bar // Status bar
statusLabel = new JLabel(" "); statusLabel = new JLabel(" ");
@ -1026,7 +1064,10 @@ public class FilePanelTab extends JPanel {
toggleSelectionAndMoveDown(); toggleSelectionAndMoveDown();
e.consume(); e.consume();
} else if (e.getKeyCode() == java.awt.event.KeyEvent.VK_ESCAPE) { } else if (e.getKeyCode() == java.awt.event.KeyEvent.VK_ESCAPE) {
if (fileTable.isEditing()) { if (searchModeActive) {
exitSearchMode();
e.consume();
} else if (fileTable.isEditing()) {
fileTable.getCellEditor().cancelCellEditing(); fileTable.getCellEditor().cancelCellEditing();
e.consume(); e.consume();
} else { } else {
@ -1077,34 +1118,62 @@ public class FilePanelTab extends JPanel {
private void handleCtrlAltTypeSearch(char c) { private void handleCtrlAltTypeSearch(char c) {
if (inlineRenameActive || fileTable.isEditing()) return; if (inlineRenameActive || fileTable.isEditing()) return;
long now = System.currentTimeMillis();
if (now - ctrlAltTypeLastTs > CTRL_ALT_TYPE_RESET_MS) {
ctrlAltTypeBuffer = "";
}
ctrlAltTypeLastTs = now;
ctrlAltTypeBuffer += Character.toLowerCase(c);
String match = findFirstItemStartingWith(ctrlAltTypeBuffer); if (!searchModeActive) {
if (match == null && ctrlAltTypeBuffer.length() > 1) { enterSearchMode();
ctrlAltTypeBuffer = String.valueOf(Character.toLowerCase(c));
match = findFirstItemStartingWith(ctrlAltTypeBuffer);
} }
if (match != null) {
selectItemByName(match, true); String currentText = filterTextField.getText();
filterTextField.setText(currentText + c);
filterTextField.requestFocusInWindow();
}
private void enterSearchMode() {
searchModeActive = true;
filterTextField.setText("");
filterPanel.setVisible(true);
revalidate();
repaint();
}
private void exitSearchMode() {
searchModeActive = false;
filterPanel.setVisible(false);
filterTextField.setText("");
revalidate();
repaint();
// Restore all items
tableModel.setItems(new ArrayList<>(allItems));
fileTable.requestFocusInWindow();
}
private void updateFilter() {
if (!searchModeActive) return;
String filterText = filterTextField.getText();
List<FileItem> filteredItems = applyFilter(new ArrayList<>(allItems), filterText);
tableModel.setItems(filteredItems);
if (filteredItems.size() > 0) {
fileTable.setRowSelectionInterval(0, 0);
} }
} }
private String findFirstItemStartingWith(String prefix) { private List<FileItem> applyFilter(List<FileItem> items, String filterText) {
if (prefix == null || prefix.isEmpty()) return null; if (filterText == null || filterText.isEmpty()) return items;
String p = prefix.toLowerCase(Locale.ROOT); String lowerFilter = filterText.toLowerCase(Locale.ROOT);
for (FileItem item : tableModel.items) { List<FileItem> result = new ArrayList<>();
if (item == null) continue; for (FileItem item : items) {
String name = item.getName(); // Always keep ".." if it exists
if (name != null && name.toLowerCase(Locale.ROOT).startsWith(p)) { if (item.getName().equals("..")) {
return name; result.add(item);
continue;
}
if (item.getName().toLowerCase(Locale.ROOT).contains(lowerFilter)) {
result.add(item);
} }
} }
return null; return result;
} }
private static char keyCodeToChar(int keyCode, boolean shift) { private static char keyCodeToChar(int keyCode, boolean shift) {
@ -1356,6 +1425,15 @@ public class FilePanelTab extends JPanel {
List<FileItem> items = (preloadedItems != null) ? preloadedItems : createFileItemList(directory); List<FileItem> items = (preloadedItems != null) ? preloadedItems : createFileItemList(directory);
// Save copy of all items for filtering
allItems.clear();
allItems.addAll(items);
// Apply filter if search mode is active
if (searchModeActive) {
items = applyFilter(items, filterTextField.getText());
}
// Only update the model if items have actually changed // Only update the model if items have actually changed
boolean itemsChanged = !isSameContent(items, tableModel.items); boolean itemsChanged = !isSameContent(items, tableModel.items);
if (itemsChanged) { if (itemsChanged) {