From 15eff68abc7ee7f7ea7293c36d5a281baf187cbf Mon Sep 17 00:00:00 2001 From: Radek Davidek Date: Mon, 13 Apr 2026 17:09:39 +0200 Subject: [PATCH] fixed inline search --- .../cz/kamma/kfmanager/ui/FilePanelTab.java | 130 ++++++++++++++---- 1 file changed, 104 insertions(+), 26 deletions(-) diff --git a/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java b/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java index f69ee99..8320aea 100644 --- a/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java +++ b/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java @@ -86,9 +86,10 @@ public class FilePanelTab extends JPanel { private boolean active = false; private final Map changeTimestamps = new HashMap<>(); private static final long CHANGE_HIGHLIGHT_DURATION = 500; // 0.5 seconds per item - private String ctrlAltTypeBuffer = ""; - private long ctrlAltTypeLastTs = 0L; - private static final long CTRL_ALT_TYPE_RESET_MS = 1200; + private final List allItems = new ArrayList<>(); + private JTextField filterTextField; + private JPanel filterPanel; + private boolean searchModeActive = false; public FilePanelTab(String initialPath) { this(initialPath, true); @@ -344,6 +345,39 @@ public class FilePanelTab extends JPanel { private void initComponents() { 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 tableModel = new FileTableModel(); // 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(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 statusLabel = new JLabel(" "); @@ -1026,7 +1064,10 @@ public class FilePanelTab extends JPanel { toggleSelectionAndMoveDown(); e.consume(); } 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(); e.consume(); } else { @@ -1077,34 +1118,62 @@ public class FilePanelTab extends JPanel { private void handleCtrlAltTypeSearch(char c) { if (inlineRenameActive || fileTable.isEditing()) return; - long now = System.currentTimeMillis(); - if (now - ctrlAltTypeLastTs > CTRL_ALT_TYPE_RESET_MS) { - ctrlAltTypeBuffer = ""; + + if (!searchModeActive) { + enterSearchMode(); } - ctrlAltTypeLastTs = now; - ctrlAltTypeBuffer += Character.toLowerCase(c); + + String currentText = filterTextField.getText(); + filterTextField.setText(currentText + c); + filterTextField.requestFocusInWindow(); + } - String match = findFirstItemStartingWith(ctrlAltTypeBuffer); - if (match == null && ctrlAltTypeBuffer.length() > 1) { - ctrlAltTypeBuffer = String.valueOf(Character.toLowerCase(c)); - match = findFirstItemStartingWith(ctrlAltTypeBuffer); - } - if (match != null) { - selectItemByName(match, true); + 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 filteredItems = applyFilter(new ArrayList<>(allItems), filterText); + tableModel.setItems(filteredItems); + + if (filteredItems.size() > 0) { + fileTable.setRowSelectionInterval(0, 0); } } - private String findFirstItemStartingWith(String prefix) { - if (prefix == null || prefix.isEmpty()) return null; - String p = prefix.toLowerCase(Locale.ROOT); - for (FileItem item : tableModel.items) { - if (item == null) continue; - String name = item.getName(); - if (name != null && name.toLowerCase(Locale.ROOT).startsWith(p)) { - return name; + private List applyFilter(List items, String filterText) { + if (filterText == null || filterText.isEmpty()) return items; + String lowerFilter = filterText.toLowerCase(Locale.ROOT); + List result = new ArrayList<>(); + for (FileItem item : items) { + // Always keep ".." if it exists + if (item.getName().equals("..")) { + 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) { @@ -1356,6 +1425,15 @@ public class FilePanelTab extends JPanel { List 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 boolean itemsChanged = !isSameContent(items, tableModel.items); if (itemsChanged) {