focus fixes
This commit is contained in:
parent
d116ed7d0f
commit
f88a6c10a1
@ -155,6 +155,42 @@ public class FilePanel extends JPanel {
|
||||
});
|
||||
|
||||
add(tabbedPane, BorderLayout.CENTER);
|
||||
|
||||
// Click on panel background or empty areas should focus the table
|
||||
addMouseListenerToComponents(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively adds a mouse listener to all components (except buttons/combos)
|
||||
* to request focus for the active table when clicked.
|
||||
*/
|
||||
private void addMouseListenerToComponents(Component comp) {
|
||||
if (comp == null) return;
|
||||
|
||||
// Components that should NOT steal focus back to the table when clicked
|
||||
boolean isInteractive = comp instanceof JButton ||
|
||||
comp instanceof JComboBox ||
|
||||
comp instanceof JTextField ||
|
||||
comp instanceof JTable;
|
||||
|
||||
if (!isInteractive) {
|
||||
comp.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(java.awt.event.MouseEvent e) {
|
||||
FilePanelTab tab = getCurrentTab();
|
||||
if (tab != null) {
|
||||
tab.getFileTable().requestFocusInWindow();
|
||||
tab.selectLastItem();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (comp instanceof Container) {
|
||||
for (Component child : ((Container) comp).getComponents()) {
|
||||
addMouseListenerToComponents(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -196,6 +232,9 @@ public class FilePanel extends JPanel {
|
||||
tabbedPane.addTab(tabTitle, tab);
|
||||
tabbedPane.setSelectedComponent(tab);
|
||||
|
||||
// Ensure clicking on empty areas of the tab/scrollpane focuses the table
|
||||
addMouseListenerToComponents(tab);
|
||||
|
||||
// Update path field
|
||||
updatePathField();
|
||||
updateTabStyles();
|
||||
@ -225,6 +264,9 @@ public class FilePanel extends JPanel {
|
||||
tabbedPane.addTab(tabTitle, tab);
|
||||
tabbedPane.setSelectedComponent(tab);
|
||||
|
||||
// Ensure clicking on empty areas of the tab/scrollpane focuses the table
|
||||
addMouseListenerToComponents(tab);
|
||||
|
||||
updatePathField();
|
||||
updateTabStyles();
|
||||
|
||||
|
||||
@ -42,7 +42,10 @@ public class FilePanelTab extends JPanel {
|
||||
// Sorting state for FULL mode header clicks
|
||||
private int sortColumn = -1; // 0=name,1=size,2=date
|
||||
private boolean sortAscending = true;
|
||||
private com.kfmanager.config.AppConfig persistedConfig;
|
||||
private com.kfmanager.config.AppConfig persistedConfig;
|
||||
// Track last selection to restore it if focus is requested on empty area
|
||||
private int lastValidRow = 0;
|
||||
private int lastValidBriefColumn = 0;
|
||||
// If an archive was opened, we may extract it to a temp directory; track it so
|
||||
// we can cleanup older temp directories when navigation changes.
|
||||
private Path currentArchiveTempDir = null;
|
||||
@ -239,10 +242,14 @@ public class FilePanelTab extends JPanel {
|
||||
repaint();
|
||||
}
|
||||
|
||||
// Request focus on table even if clicking on empty area
|
||||
fileTable.requestFocusInWindow();
|
||||
|
||||
// Single left-click should focus/select the item under cursor but
|
||||
// should NOT toggle its marked state. This preserves keyboard
|
||||
// marking (Insert) while making mouse clicks act as simple focus.
|
||||
if (e.getClickCount() == 1 && javax.swing.SwingUtilities.isLeftMouseButton(e)) {
|
||||
boolean selected = false;
|
||||
if (row >= 0) {
|
||||
// Convert brief layout coordinates to absolute index where needed
|
||||
if (viewMode == ViewMode.BRIEF) {
|
||||
@ -255,17 +262,23 @@ public class FilePanelTab extends JPanel {
|
||||
briefCurrentColumn = selCol;
|
||||
fileTable.setRowSelectionInterval(selRow, selRow);
|
||||
fileTable.scrollRectToVisible(fileTable.getCellRect(selRow, selCol, true));
|
||||
selected = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// FULL mode: rows map directly
|
||||
fileTable.setRowSelectionInterval(row, row);
|
||||
fileTable.scrollRectToVisible(fileTable.getCellRect(row, 0, true));
|
||||
selected = true;
|
||||
}
|
||||
fileTable.requestFocusInWindow();
|
||||
repaint();
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
if (!selected) {
|
||||
// Clicked on empty area (row < 0 or empty cell in BRIEF): select the last item in the panel
|
||||
selectLastItem();
|
||||
}
|
||||
repaint();
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
// Double-click opens the item under cursor (directories)
|
||||
@ -281,9 +294,11 @@ public class FilePanelTab extends JPanel {
|
||||
// Allow MOUSE_PRESSED for drag initiating gestures, but block standard selection change.
|
||||
// We'll process selection manually in MOUSE_CLICKED above.
|
||||
if (e.getID() == java.awt.event.MouseEvent.MOUSE_PRESSED) {
|
||||
fileTable.requestFocusInWindow();
|
||||
// Start selection logic on press to support DnD initiate
|
||||
int col = columnAtPoint(e.getPoint());
|
||||
int row = rowAtPoint(e.getPoint());
|
||||
boolean selected = false;
|
||||
if (row >= 0) {
|
||||
if (viewMode == ViewMode.BRIEF) {
|
||||
FileItem item = tableModel.getItemFromBriefLayout(row, col);
|
||||
@ -293,14 +308,25 @@ public class FilePanelTab extends JPanel {
|
||||
int selRow = index % tableModel.briefRowsPerColumn;
|
||||
fileTable.setRowSelectionInterval(selRow, selRow);
|
||||
briefCurrentColumn = index / tableModel.briefRowsPerColumn;
|
||||
selected = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fileTable.setRowSelectionInterval(row, row);
|
||||
selected = true;
|
||||
}
|
||||
fileTable.requestFocusInWindow();
|
||||
repaint();
|
||||
}
|
||||
|
||||
if (!selected) {
|
||||
// Clicked on empty area of the table: select the last item
|
||||
selectLastItem();
|
||||
// For empty area, we MUST consume the event to prevent JTable from clearing selection
|
||||
e.consume();
|
||||
return;
|
||||
}
|
||||
repaint();
|
||||
// If we are on a row, we let it pass to super so Drag and Drop can be initiated,
|
||||
// but since we already set selection, JTable will just confirm it.
|
||||
}
|
||||
|
||||
if (e.getID() == java.awt.event.MouseEvent.MOUSE_RELEASED) {
|
||||
@ -308,7 +334,9 @@ public class FilePanelTab extends JPanel {
|
||||
return;
|
||||
}
|
||||
|
||||
super.processMouseEvent(e);
|
||||
if (!e.isConsumed()) {
|
||||
super.processMouseEvent(e);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void processMouseMotionEvent(java.awt.event.MouseEvent e) {
|
||||
@ -433,6 +461,40 @@ public class FilePanelTab extends JPanel {
|
||||
fileTable.setBackground(this.getBackground());
|
||||
fileTable.setOpaque(true);
|
||||
|
||||
fileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
// Enforce that at least one item is always active (selected) if the table is not empty.
|
||||
fileTable.getSelectionModel().addListSelectionListener(e -> {
|
||||
int row = fileTable.getSelectedRow();
|
||||
if (row >= 0) {
|
||||
if (!e.getValueIsAdjusting()) {
|
||||
lastValidRow = row;
|
||||
lastValidBriefColumn = briefCurrentColumn;
|
||||
}
|
||||
} else {
|
||||
// Selection became empty. Attempt to restore it.
|
||||
// We do this even if e.getValueIsAdjusting() is true to prevent temporary selection loss.
|
||||
if (fileTable.getRowCount() > 0) {
|
||||
int targetRow = Math.min(lastValidRow, fileTable.getRowCount() - 1);
|
||||
if (targetRow < 0) targetRow = 0;
|
||||
|
||||
final int finalRow = targetRow;
|
||||
final int finalCol = lastValidBriefColumn;
|
||||
|
||||
// Use invokeLater to avoid potential re-entrancy issues with selection model
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
if (fileTable != null && fileTable.getSelectionModel().isSelectionEmpty() && fileTable.getRowCount() > 0) {
|
||||
briefCurrentColumn = finalCol;
|
||||
fileTable.setRowSelectionInterval(finalRow, finalRow);
|
||||
// Ensure the restored selection is visible
|
||||
try {
|
||||
fileTable.scrollRectToVisible(fileTable.getCellRect(finalRow, finalCol, true));
|
||||
} catch (Exception ignore) {}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(fileTable);
|
||||
// Enable horizontal scrollbar when needed so BRIEF mode can scroll left-right
|
||||
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
||||
@ -728,6 +790,8 @@ public class FilePanelTab extends JPanel {
|
||||
|
||||
this.currentDirectory = directory;
|
||||
briefCurrentColumn = 0;
|
||||
lastValidRow = 0;
|
||||
lastValidBriefColumn = 0;
|
||||
|
||||
File[] files = directory.listFiles();
|
||||
List<FileItem> items = new ArrayList<>();
|
||||
@ -1294,6 +1358,28 @@ public class FilePanelTab extends JPanel {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark/Select the very last item in the list
|
||||
*/
|
||||
public void selectLastItem() {
|
||||
int count = tableModel.getRowCount();
|
||||
if (count > 0) {
|
||||
if (viewMode == ViewMode.BRIEF) {
|
||||
int lastIndex = tableModel.items.size() - 1;
|
||||
briefCurrentColumn = lastIndex / tableModel.briefRowsPerColumn;
|
||||
int row = lastIndex % tableModel.briefRowsPerColumn;
|
||||
fileTable.setRowSelectionInterval(row, row);
|
||||
fileTable.scrollRectToVisible(fileTable.getCellRect(row, briefCurrentColumn, true));
|
||||
} else {
|
||||
int last = count - 1;
|
||||
fileTable.setRowSelectionInterval(last, last);
|
||||
fileTable.scrollRectToVisible(fileTable.getCellRect(last, 0, true));
|
||||
}
|
||||
repaint();
|
||||
updateStatus();
|
||||
}
|
||||
}
|
||||
|
||||
public void setViewMode(ViewMode mode) {
|
||||
if (this.viewMode != mode) {
|
||||
String selectedItemName = null;
|
||||
|
||||
@ -144,20 +144,33 @@ public class MainWindow extends JFrame {
|
||||
// ignore and keep default
|
||||
}
|
||||
|
||||
// Focus listeners to track active panel
|
||||
// Global focus listener to track which panel is active based on focused component
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener("permanentFocusOwner", evt -> {
|
||||
Component focused = (Component) evt.getNewValue();
|
||||
if (focused != null) {
|
||||
if (SwingUtilities.isDescendingFrom(focused, leftPanel)) {
|
||||
activePanel = leftPanel;
|
||||
updateActivePanelBorder();
|
||||
leftPanel.getFileTable().repaint();
|
||||
rightPanel.getFileTable().repaint();
|
||||
} else if (SwingUtilities.isDescendingFrom(focused, rightPanel)) {
|
||||
activePanel = rightPanel;
|
||||
updateActivePanelBorder();
|
||||
leftPanel.getFileTable().repaint();
|
||||
rightPanel.getFileTable().repaint();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Focus listeners to track active panel and ensure selection
|
||||
leftPanel.getFileTable().addFocusListener(new FocusAdapter() {
|
||||
@Override
|
||||
public void focusGained(FocusEvent e) {
|
||||
activePanel = leftPanel;
|
||||
updateActivePanelBorder();
|
||||
// Ensure some row is selected
|
||||
JTable leftTable = leftPanel.getFileTable();
|
||||
if (leftTable.getSelectedRow() == -1 && leftTable.getRowCount() > 0) {
|
||||
leftTable.setRowSelectionInterval(0, 0);
|
||||
}
|
||||
// Repaint both panels
|
||||
leftPanel.getFileTable().repaint();
|
||||
rightPanel.getFileTable().repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -170,16 +183,11 @@ public class MainWindow extends JFrame {
|
||||
rightPanel.getFileTable().addFocusListener(new FocusAdapter() {
|
||||
@Override
|
||||
public void focusGained(FocusEvent e) {
|
||||
activePanel = rightPanel;
|
||||
updateActivePanelBorder();
|
||||
// Ensure some row is selected
|
||||
JTable rightTable = rightPanel.getFileTable();
|
||||
if (rightTable.getSelectedRow() == -1 && rightTable.getRowCount() > 0) {
|
||||
rightTable.setRowSelectionInterval(0, 0);
|
||||
}
|
||||
// Repaint both panels
|
||||
leftPanel.getFileTable().repaint();
|
||||
rightPanel.getFileTable().repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user