focus on esc fixed

This commit is contained in:
rdavidek 2026-01-18 15:56:55 +01:00
parent 3b1065bc81
commit 28e3e1bdad
4 changed files with 239 additions and 5 deletions

View File

@ -34,6 +34,12 @@ public class FilePanel extends JPanel {
if (tab != null) tab.startInlineRename(); if (tab != null) tab.startInlineRename();
} }
/** Select files by wildcard pattern in the current tab. */
public void selectByWildcard(String pattern) {
FilePanelTab tab = getCurrentTab();
if (tab != null) tab.selectByWildcard(pattern);
}
private Runnable switchPanelCallback; private Runnable switchPanelCallback;
public void setSwitchPanelCallback(Runnable cb) { public void setSwitchPanelCallback(Runnable cb) {

View File

@ -1564,6 +1564,91 @@ public class FilePanelTab extends JPanel {
updateStatus(); updateStatus();
} }
/**
* Select all items that match the given wildcard pattern.
* Supports * (any characters) and ? (single character).
* Example patterns: *.txt, test*.*, file?.dat
*/
public void selectByWildcard(String pattern) {
if (pattern == null || pattern.trim().isEmpty()) {
return;
}
pattern = pattern.trim();
// Convert wildcard pattern to regex
String regex = wildcardToRegex(pattern);
java.util.regex.Pattern compiledPattern;
try {
compiledPattern = java.util.regex.Pattern.compile(regex, java.util.regex.Pattern.CASE_INSENSITIVE);
} catch (Exception e) {
return; // Invalid pattern
}
// Mark all matching items
int matchCount = 0;
for (FileItem item : tableModel.items) {
if (item.getName().equals("..")) {
continue; // Skip parent directory
}
if (compiledPattern.matcher(item.getName()).matches()) {
item.setMarked(true);
matchCount++;
}
}
// Refresh the table to show the marked items
fileTable.repaint();
updateStatus();
// Request focus back to the table
fileTable.requestFocusInWindow();
}
/**
* Convert wildcard pattern to regex.
* * matches any characters
* ? matches single character
*/
private String wildcardToRegex(String wildcard) {
StringBuilder regex = new StringBuilder("^");
for (int i = 0; i < wildcard.length(); i++) {
char c = wildcard.charAt(i);
switch (c) {
case '*':
regex.append(".*");
break;
case '?':
regex.append(".");
break;
case '.':
regex.append("\\.");
break;
case '\\':
regex.append("\\\\");
break;
case '(':
case ')':
case '[':
case ']':
case '{':
case '}':
case '^':
case '$':
case '|':
case '+':
regex.append("\\").append(c);
break;
default:
regex.append(c);
break;
}
}
regex.append("$");
return regex.toString();
}
public void copyToClipboard(boolean cut) { public void copyToClipboard(boolean cut) {
List<FileItem> selected = getSelectedItems(); List<FileItem> selected = getSelectedItems();
if (selected.isEmpty()) return; if (selected.isEmpty()) return;

View File

@ -205,6 +205,9 @@ public class MainWindow extends JFrame {
leftPanel.getFileTable().addFocusListener(new FocusAdapter() { leftPanel.getFileTable().addFocusListener(new FocusAdapter() {
@Override @Override
public void focusGained(FocusEvent e) { public void focusGained(FocusEvent e) {
activePanel = leftPanel;
updateActivePanelBorder();
updateCommandLinePrompt();
// Ensure some row is selected // Ensure some row is selected
JTable leftTable = leftPanel.getFileTable(); JTable leftTable = leftPanel.getFileTable();
if (leftTable.getSelectedRow() == -1 && leftTable.getRowCount() > 0) { if (leftTable.getSelectedRow() == -1 && leftTable.getRowCount() > 0) {
@ -222,6 +225,9 @@ public class MainWindow extends JFrame {
rightPanel.getFileTable().addFocusListener(new FocusAdapter() { rightPanel.getFileTable().addFocusListener(new FocusAdapter() {
@Override @Override
public void focusGained(FocusEvent e) { public void focusGained(FocusEvent e) {
activePanel = rightPanel;
updateActivePanelBorder();
updateCommandLinePrompt();
// Ensure some row is selected // Ensure some row is selected
JTable rightTable = rightPanel.getFileTable(); JTable rightTable = rightPanel.getFileTable();
if (rightTable.getSelectedRow() == -1 && rightTable.getRowCount() > 0) { if (rightTable.getSelectedRow() == -1 && rightTable.getRowCount() > 0) {
@ -285,8 +291,12 @@ public class MainWindow extends JFrame {
@Override @Override
public void keyPressed(KeyEvent e) { public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
if (!tf.getText().isEmpty()) {
tf.setText(""); tf.setText("");
if (activePanel != null && activePanel.getFileTable() != null) {
activePanel.getFileTable().requestFocusInWindow(); activePanel.getFileTable().requestFocusInWindow();
}
}
e.consume(); e.consume();
} else if (e.getKeyCode() == KeyEvent.VK_TAB) { } else if (e.getKeyCode() == KeyEvent.VK_TAB) {
activePanel.getFileTable().requestFocusInWindow(); activePanel.getFileTable().requestFocusInWindow();
@ -614,6 +624,10 @@ public class MainWindow extends JFrame {
searchItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK)); searchItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK));
searchItem.addActionListener(e -> showSearchDialog()); searchItem.addActionListener(e -> showSearchDialog());
JMenuItem selectWildcardItem = new JMenuItem("Select by wildcard...");
selectWildcardItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK));
selectWildcardItem.addActionListener(e -> showWildcardSelectDialog());
JMenuItem refreshItem = new JMenuItem("Refresh"); JMenuItem refreshItem = new JMenuItem("Refresh");
refreshItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F5, InputEvent.CTRL_DOWN_MASK)); refreshItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F5, InputEvent.CTRL_DOWN_MASK));
refreshItem.addActionListener(e -> refreshPanels()); refreshItem.addActionListener(e -> refreshPanels());
@ -623,6 +637,7 @@ public class MainWindow extends JFrame {
exitItem.addActionListener(e -> saveConfigAndExit()); exitItem.addActionListener(e -> saveConfigAndExit());
fileMenu.add(searchItem); fileMenu.add(searchItem);
fileMenu.add(selectWildcardItem);
fileMenu.add(refreshItem); fileMenu.add(refreshItem);
fileMenu.addSeparator(); fileMenu.addSeparator();
fileMenu.add(exitItem); fileMenu.add(exitItem);
@ -824,11 +839,13 @@ public class MainWindow extends JFrame {
// ESC - global escape to return focus to panels // ESC - global escape to return focus to panels
rootPane.registerKeyboardAction(e -> { rootPane.registerKeyboardAction(e -> {
if (activePanel != null) { Object currentItem = commandLine.getEditor().getItem();
// Always clear command line and return focus to panels if (currentItem != null && !currentItem.toString().isEmpty()) {
commandLine.getEditor().setItem(""); commandLine.getEditor().setItem("");
if (activePanel != null && activePanel.getFileTable() != null) {
activePanel.getFileTable().requestFocusInWindow(); activePanel.getFileTable().requestFocusInWindow();
} }
}
}, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
JComponent.WHEN_IN_FOCUSED_WINDOW); JComponent.WHEN_IN_FOCUSED_WINDOW);
@ -919,6 +936,11 @@ public class MainWindow extends JFrame {
rootPane.registerKeyboardAction(e -> showCommandLineHistory(), rootPane.registerKeyboardAction(e -> showCommandLineHistory(),
KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.CTRL_DOWN_MASK), KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.CTRL_DOWN_MASK),
JComponent.WHEN_IN_FOCUSED_WINDOW); JComponent.WHEN_IN_FOCUSED_WINDOW);
// Ctrl+A - Select files by wildcard
rootPane.registerKeyboardAction(e -> showWildcardSelectDialog(),
KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK),
JComponent.WHEN_IN_FOCUSED_WINDOW);
} }
/** /**
@ -1001,6 +1023,8 @@ public class MainWindow extends JFrame {
* Attach TAB handling to switch panels * Attach TAB handling to switch panels
*/ */
private void addTabKeyHandler(JTable table) { private void addTabKeyHandler(JTable table) {
if (table == null) return;
// Remove standard Swing TAB behavior // Remove standard Swing TAB behavior
table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
.put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), "switchPanel"); .put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), "switchPanel");
@ -1072,6 +1096,16 @@ public class MainWindow extends JFrame {
} }
} }
}); });
// Wildcard selection (Ctrl+A)
table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK), "wildcardSelect");
table.getActionMap().put("wildcardSelect", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
showWildcardSelectDialog();
}
});
} }
/** /**
@ -1419,6 +1453,26 @@ public class MainWindow extends JFrame {
dialog.setVisible(true); dialog.setVisible(true);
} }
/**
* Show wildcard select dialog
*/
public void showWildcardSelectDialog() {
if (activePanel == null) return;
WildcardSelectDialog dialog = new WildcardSelectDialog(this);
dialog.setVisible(true);
String pattern = dialog.getPattern();
if (pattern != null && !pattern.isEmpty()) {
activePanel.selectByWildcard(pattern);
} else {
// If cancelled, return focus to the active panel
if (activePanel.getFileTable() != null) {
activePanel.getFileTable().requestFocusInWindow();
}
}
}
/** /**
* Show the given file's parent directory in the panel that currently has focus * Show the given file's parent directory in the panel that currently has focus
* and select the file in that panel. * and select the file in that panel.

View File

@ -0,0 +1,89 @@
package cz.kamma.kfmanager.ui;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
/**
* Dialog for entering a wildcard pattern to select matching files
*/
public class WildcardSelectDialog extends JDialog {
private JTextField patternField;
private boolean confirmed = false;
public WildcardSelectDialog(Frame parent) {
super(parent, "Vybrat podle masky", true);
initComponents();
pack();
setLocationRelativeTo(parent);
}
private void initComponents() {
setLayout(new BorderLayout(10, 10));
// Main panel with padding
JPanel mainPanel = new JPanel(new BorderLayout(5, 5));
mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
// Label
JLabel label = new JLabel("Zadejte masku (např. *.txt, test*.*, file?.dat):");
mainPanel.add(label, BorderLayout.NORTH);
// Text field for pattern
patternField = new JTextField("*", 30);
patternField.selectAll();
mainPanel.add(patternField, BorderLayout.CENTER);
add(mainPanel, BorderLayout.CENTER);
// Button panel
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
JButton okButton = new JButton("OK");
okButton.addActionListener(e -> {
confirmed = true;
dispose();
});
JButton cancelButton = new JButton("Zrušit");
cancelButton.addActionListener(e -> {
confirmed = false;
dispose();
});
buttonPanel.add(okButton);
buttonPanel.add(cancelButton);
add(buttonPanel, BorderLayout.SOUTH);
// Enter confirms
patternField.addActionListener(e -> {
confirmed = true;
dispose();
});
// ESC cancels
getRootPane().registerKeyboardAction(e -> {
confirmed = false;
dispose();
}, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
getRootPane().setDefaultButton(okButton);
}
public String getPattern() {
return confirmed ? patternField.getText().trim() : null;
}
@Override
public void setVisible(boolean visible) {
if (visible) {
SwingUtilities.invokeLater(() -> {
patternField.requestFocusInWindow();
patternField.selectAll();
});
}
super.setVisible(visible);
}
}