fixed esc

This commit is contained in:
rdavidek 2026-01-18 16:40:35 +01:00
parent 803e1e707b
commit d4bfeab00d
4 changed files with 105 additions and 6 deletions

View File

@ -5,6 +5,7 @@ import cz.kamma.kfmanager.model.FileItem;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
@ -47,6 +48,7 @@ public class FilePanel extends JPanel {
} }
private void initComponents() { private void initComponents() {
tabbedPane = new JTabbedPane();
setLayout(new BorderLayout()); setLayout(new BorderLayout());
setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
@ -55,6 +57,16 @@ public class FilePanel extends JPanel {
// Drive selection dropdown placed before the path field // Drive selection dropdown placed before the path field
driveCombo = new JComboBox<>(); driveCombo = new JComboBox<>();
// Return focus to table when ESC is pressed in the combo
driveCombo.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "cancel-drive-selection");
driveCombo.getActionMap().put("cancel-drive-selection", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
driveCombo.hidePopup();
FilePanelTab tab = getCurrentTab();
if (tab != null) tab.getFileTable().requestFocusInWindow();
}
});
// Allow the combo to receive focus so keyboard navigation and popup interaction work // Allow the combo to receive focus so keyboard navigation and popup interaction work
driveCombo.setFocusable(true); driveCombo.setFocusable(true);
driveCombo.setToolTipText("Select drive"); driveCombo.setToolTipText("Select drive");
@ -156,7 +168,6 @@ public class FilePanel extends JPanel {
add(topPanel, BorderLayout.NORTH); add(topPanel, BorderLayout.NORTH);
// JTabbedPane for tabs // JTabbedPane for tabs
tabbedPane = new JTabbedPane();
tabbedPane.setTabPlacement(JTabbedPane.TOP); tabbedPane.setTabPlacement(JTabbedPane.TOP);
// Listener for updating path and style on tab change // Listener for updating path and style on tab change

View File

@ -62,6 +62,7 @@ public class FilePanelTab extends JPanel {
// we can cleanup older temp directories when navigation changes. // we can cleanup older temp directories when navigation changes.
private Path currentArchiveTempDir = null; private Path currentArchiveTempDir = null;
private File currentArchiveSourceFile = null; private File currentArchiveSourceFile = null;
private boolean inlineRenameActive = false;
public FilePanelTab(String initialPath) { public FilePanelTab(String initialPath) {
this(initialPath, true); this(initialPath, true);
@ -88,8 +89,14 @@ public class FilePanelTab extends JPanel {
editCol = 0; // name column in FULL editCol = 0; // name column in FULL
} }
// Enable editing temporarily
inlineRenameActive = true;
// Only allow editing if the cell represents a real item // Only allow editing if the cell represents a real item
if (!tableModel.isCellEditable(selRow, editCol)) return; if (!tableModel.isCellEditable(selRow, editCol)) {
inlineRenameActive = false;
return;
}
// Start editing and select all text in editor // Start editing and select all text in editor
boolean started = fileTable.editCellAt(selRow, editCol); boolean started = fileTable.editCellAt(selRow, editCol);
@ -100,6 +107,8 @@ public class FilePanelTab extends JPanel {
tf.requestFocusInWindow(); tf.requestFocusInWindow();
tf.selectAll(); tf.selectAll();
} }
} else {
inlineRenameActive = false;
} }
} }
@ -205,6 +214,18 @@ public class FilePanelTab extends JPanel {
// Also override mouse-motion processing to suppress drag events and // Also override mouse-motion processing to suppress drag events and
// prevent any drag-and-drop transfer handling. // prevent any drag-and-drop transfer handling.
fileTable = new JTable(tableModel) { fileTable = new JTable(tableModel) {
@Override
public void editingStopped(javax.swing.event.ChangeEvent e) {
super.editingStopped(e);
inlineRenameActive = false;
}
@Override
public void editingCanceled(javax.swing.event.ChangeEvent e) {
super.editingCanceled(e);
inlineRenameActive = false;
}
@Override @Override
protected void processMouseEvent(java.awt.event.MouseEvent e) { protected void processMouseEvent(java.awt.event.MouseEvent e) {
// Show system-like context menu on popup trigger (right-click) without // Show system-like context menu on popup trigger (right-click) without
@ -586,6 +607,11 @@ public class FilePanelTab extends JPanel {
} else if (e.getKeyCode() == java.awt.event.KeyEvent.VK_INSERT) { } else if (e.getKeyCode() == java.awt.event.KeyEvent.VK_INSERT) {
toggleSelectionAndMoveDown(); toggleSelectionAndMoveDown();
e.consume(); e.consume();
} else if (e.getKeyCode() == java.awt.event.KeyEvent.VK_ESCAPE) {
if (fileTable.isEditing()) {
fileTable.getCellEditor().cancelCellEditing();
e.consume();
}
} else if (viewMode == ViewMode.BRIEF) { } else if (viewMode == ViewMode.BRIEF) {
handleBriefKeyNavigation(e); handleBriefKeyNavigation(e);
} else if (viewMode == ViewMode.FULL) { } else if (viewMode == ViewMode.FULL) {
@ -1156,11 +1182,17 @@ public class FilePanelTab extends JPanel {
} else if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.OPEN)) { } else if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.OPEN)) {
// 3. Fallback to system default // 3. Fallback to system default
Desktop.getDesktop().open(file); Desktop.getDesktop().open(file);
// Try to keep focus or at least set it back for when user returns
SwingUtilities.invokeLater(() -> {
fileTable.requestFocusInWindow();
});
} }
} catch (Exception ex) { } catch (Exception ex) {
try { try {
JOptionPane.showMessageDialog(this, "Error opening file: " + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); JOptionPane.showMessageDialog(this, "Error opening file: " + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
} catch (Exception ignore) {} } catch (Exception ignore) {}
} finally {
fileTable.requestFocusInWindow();
} }
} }
@ -1196,7 +1228,7 @@ public class FilePanelTab extends JPanel {
} catch (Exception ignore) {} } catch (Exception ignore) {}
currentArchiveTempDir = temp; currentArchiveTempDir = temp;
currentArchiveSourceFile = item.getFile(); currentArchiveSourceFile = item.getFile();
loadDirectory(temp.toFile()); loadDirectory(temp.toFile(), true, true);
} }
} else if (item.isDirectory()) { } else if (item.isDirectory()) {
loadDirectory(item.getFile()); loadDirectory(item.getFile());
@ -1364,7 +1396,7 @@ public class FilePanelTab extends JPanel {
PropertiesDialog dialog = new PropertiesDialog(parent, f); PropertiesDialog dialog = new PropertiesDialog(parent, f);
dialog.setVisible(true); dialog.setVisible(true);
// Refresh current directory after potential attribute changes // Refresh current directory after potential attribute changes
loadDirectory(getCurrentDirectory()); loadDirectory(getCurrentDirectory(), false, true);
} }
} catch (Exception ex) { } catch (Exception ex) {
try { JOptionPane.showMessageDialog(FilePanelTab.this, "Cannot show properties: " + ex.getMessage()); } catch (Exception ignore) {} try { JOptionPane.showMessageDialog(FilePanelTab.this, "Cannot show properties: " + ex.getMessage()); } catch (Exception ignore) {}
@ -1414,11 +1446,11 @@ public class FilePanelTab extends JPanel {
} }
} }
private void selectItemByName(String name) { public void selectItemByName(String name) {
selectItemByName(name, true); selectItemByName(name, true);
} }
private void selectItemByName(String name, boolean requestFocus) { public void selectItemByName(String name, boolean requestFocus) {
if (viewMode == ViewMode.BRIEF) { if (viewMode == ViewMode.BRIEF) {
// Re-calculate layout if needed before searching to ensure current mapping // Re-calculate layout if needed before searching to ensure current mapping
if (tableModel.items.size() > 0 && (tableModel.briefColumns == 0 || tableModel.briefRowsPerColumn == 0)) { if (tableModel.items.size() > 0 && (tableModel.briefColumns == 0 || tableModel.briefRowsPerColumn == 0)) {
@ -2545,6 +2577,8 @@ public class FilePanelTab extends JPanel {
@Override @Override
public boolean isCellEditable(int rowIndex, int columnIndex) { public boolean isCellEditable(int rowIndex, int columnIndex) {
if (!inlineRenameActive) return false;
if (viewMode == ViewMode.BRIEF) { if (viewMode == ViewMode.BRIEF) {
FileItem it = getItemFromBriefLayout(rowIndex, columnIndex); FileItem it = getItemFromBriefLayout(rowIndex, columnIndex);
return it != null; // allow editing name in brief cells return it != null; // allow editing name in brief cells

View File

@ -687,6 +687,7 @@ public class MainWindow extends JFrame {
dlg.setVisible(true); dlg.setVisible(true);
// After dialog closed, ensure appearance applied // After dialog closed, ensure appearance applied
applyAppearanceSettings(); applyAppearanceSettings();
requestFocusInActivePanel();
} }
/** /**
@ -839,9 +840,18 @@ 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 -> {
boolean textCleared = false;
Object currentItem = commandLine.getEditor().getItem(); Object currentItem = commandLine.getEditor().getItem();
if (currentItem != null && !currentItem.toString().isEmpty()) { if (currentItem != null && !currentItem.toString().isEmpty()) {
commandLine.getEditor().setItem(""); commandLine.getEditor().setItem("");
textCleared = true;
}
// If we just cleared text, or if focus is not in any table, return focus to active panel
Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
boolean inTable = (focusOwner instanceof JTable);
if (textCleared || !inTable) {
if (activePanel != null && activePanel.getFileTable() != null) { if (activePanel != null && activePanel.getFileTable() != null) {
activePanel.getFileTable().requestFocusInWindow(); activePanel.getFileTable().requestFocusInWindow();
} }
@ -1178,6 +1188,7 @@ public class MainWindow extends JFrame {
"No files selected", "No files selected",
"Copy", "Copy",
JOptionPane.INFORMATION_MESSAGE); JOptionPane.INFORMATION_MESSAGE);
requestFocusInActivePanel();
return; return;
} }
@ -1210,6 +1221,7 @@ public class MainWindow extends JFrame {
"No files selected", "No files selected",
"Move", "Move",
JOptionPane.INFORMATION_MESSAGE); JOptionPane.INFORMATION_MESSAGE);
requestFocusInActivePanel();
return; return;
} }
@ -1242,6 +1254,7 @@ public class MainWindow extends JFrame {
"No files selected", "No files selected",
"Delete", "Delete",
JOptionPane.INFORMATION_MESSAGE); JOptionPane.INFORMATION_MESSAGE);
requestFocusInActivePanel();
return; return;
} }
// remember current selection index so we can restore selection after deletion // remember current selection index so we can restore selection after deletion
@ -1296,6 +1309,7 @@ public class MainWindow extends JFrame {
"No files selected", "No files selected",
"Zip", "Zip",
JOptionPane.INFORMATION_MESSAGE); JOptionPane.INFORMATION_MESSAGE);
requestFocusInActivePanel();
return; return;
} }
@ -1360,6 +1374,7 @@ public class MainWindow extends JFrame {
"No files selected", "No files selected",
"Unzip", "Unzip",
JOptionPane.INFORMATION_MESSAGE); JOptionPane.INFORMATION_MESSAGE);
requestFocusInActivePanel();
return; return;
} }
@ -1369,6 +1384,7 @@ public class MainWindow extends JFrame {
"Selected file is not a ZIP archive", "Selected file is not a ZIP archive",
"Unzip", "Unzip",
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
requestFocusInActivePanel();
return; return;
} }
@ -1407,6 +1423,7 @@ public class MainWindow extends JFrame {
"Select one file to rename", "Select one file to rename",
"Rename", "Rename",
JOptionPane.INFORMATION_MESSAGE); JOptionPane.INFORMATION_MESSAGE);
requestFocusInActivePanel();
return; return;
} }
FileItem item = selectedItems.get(0); FileItem item = selectedItems.get(0);
@ -1455,6 +1472,12 @@ public class MainWindow extends JFrame {
*/ */
private void showSearchDialog() { private void showSearchDialog() {
SearchDialog dialog = new SearchDialog(this, activePanel.getCurrentDirectory(), config); SearchDialog dialog = new SearchDialog(this, activePanel.getCurrentDirectory(), config);
dialog.addWindowListener(new java.awt.event.WindowAdapter() {
@Override
public void windowClosed(java.awt.event.WindowEvent e) {
requestFocusInActivePanel();
}
});
dialog.setVisible(true); dialog.setVisible(true);
} }
@ -1543,6 +1566,12 @@ public class MainWindow extends JFrame {
// Removed previous 10 MB limit: allow opening large files in the viewer (paged hex will stream large binaries). // Removed previous 10 MB limit: allow opening large files in the viewer (paged hex will stream large binaries).
FileEditor viewer = new FileEditor(this, file, config, true); FileEditor viewer = new FileEditor(this, file, config, true);
viewer.addWindowListener(new java.awt.event.WindowAdapter() {
@Override
public void windowClosed(java.awt.event.WindowEvent e) {
requestFocusInActivePanel();
}
});
viewer.setVisible(true); viewer.setVisible(true);
} }
@ -1581,6 +1610,12 @@ public class MainWindow extends JFrame {
// Removed previous 10 MB limit: allow opening large files in the editor. The editor may still choose hex/paged mode for large binaries. // Removed previous 10 MB limit: allow opening large files in the editor. The editor may still choose hex/paged mode for large binaries.
FileEditor editor = new FileEditor(this, file, config, false); FileEditor editor = new FileEditor(this, file, config, false);
editor.addWindowListener(new java.awt.event.WindowAdapter() {
@Override
public void windowClosed(java.awt.event.WindowEvent e) {
requestFocusInActivePanel();
}
});
editor.setVisible(true); editor.setVisible(true);
} }
@ -1846,6 +1881,7 @@ public class MainWindow extends JFrame {
"Backspace - Parent directory", "Backspace - Parent directory",
"About", "About",
JOptionPane.INFORMATION_MESSAGE); JOptionPane.INFORMATION_MESSAGE);
requestFocusInActivePanel();
} }
/** /**
@ -2028,4 +2064,10 @@ public class MainWindow extends JFrame {
private interface FileOperation { private interface FileOperation {
void execute(FileOperations.ProgressCallback callback) throws Exception; void execute(FileOperations.ProgressCallback callback) throws Exception;
} }
private void requestFocusInActivePanel() {
if (activePanel != null && activePanel.getFileTable() != null) {
activePanel.getFileTable().requestFocusInWindow();
}
}
} }

View File

@ -542,6 +542,12 @@ public class SearchDialog extends JDialog {
try { try {
Frame owner = (Frame) SwingUtilities.getWindowAncestor(this); Frame owner = (Frame) SwingUtilities.getWindowAncestor(this);
FileEditor viewer = new FileEditor(owner, item.getFile(), config, true); FileEditor viewer = new FileEditor(owner, item.getFile(), config, true);
viewer.addWindowListener(new java.awt.event.WindowAdapter() {
@Override
public void windowClosed(java.awt.event.WindowEvent e) {
resultsTable.requestFocusInWindow();
}
});
viewer.setVisible(true); viewer.setVisible(true);
} catch (Exception e) { } catch (Exception e) {
JOptionPane.showMessageDialog(this, "Chyba při otevírání souboru: " + e.getMessage(), "Chyba", JOptionPane.ERROR_MESSAGE); JOptionPane.showMessageDialog(this, "Chyba při otevírání souboru: " + e.getMessage(), "Chyba", JOptionPane.ERROR_MESSAGE);
@ -561,6 +567,12 @@ public class SearchDialog extends JDialog {
try { try {
Frame owner = (Frame) SwingUtilities.getWindowAncestor(this); Frame owner = (Frame) SwingUtilities.getWindowAncestor(this);
FileEditor editor = new FileEditor(owner, item.getFile(), config, false); FileEditor editor = new FileEditor(owner, item.getFile(), config, false);
editor.addWindowListener(new java.awt.event.WindowAdapter() {
@Override
public void windowClosed(java.awt.event.WindowEvent e) {
resultsTable.requestFocusInWindow();
}
});
editor.setVisible(true); editor.setVisible(true);
} catch (Exception e) { } catch (Exception e) {
JOptionPane.showMessageDialog(this, "Chyba při otevírání souboru: " + e.getMessage(), "Chyba", JOptionPane.ERROR_MESSAGE); JOptionPane.showMessageDialog(this, "Chyba při otevírání souboru: " + e.getMessage(), "Chyba", JOptionPane.ERROR_MESSAGE);