fixed inline search
This commit is contained in:
parent
1c65fc987f
commit
15eff68abc
@ -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) {
|
if (!searchModeActive) {
|
||||||
ctrlAltTypeBuffer = "";
|
enterSearchMode();
|
||||||
}
|
}
|
||||||
ctrlAltTypeLastTs = now;
|
|
||||||
ctrlAltTypeBuffer += Character.toLowerCase(c);
|
String currentText = filterTextField.getText();
|
||||||
|
filterTextField.setText(currentText + c);
|
||||||
|
filterTextField.requestFocusInWindow();
|
||||||
|
}
|
||||||
|
|
||||||
String match = findFirstItemStartingWith(ctrlAltTypeBuffer);
|
private void enterSearchMode() {
|
||||||
if (match == null && ctrlAltTypeBuffer.length() > 1) {
|
searchModeActive = true;
|
||||||
ctrlAltTypeBuffer = String.valueOf(Character.toLowerCase(c));
|
filterTextField.setText("");
|
||||||
match = findFirstItemStartingWith(ctrlAltTypeBuffer);
|
filterPanel.setVisible(true);
|
||||||
}
|
revalidate();
|
||||||
if (match != null) {
|
repaint();
|
||||||
selectItemByName(match, true);
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user