From 7cc1908f60cf125d68fa9bf7f989081db622c1f8 Mon Sep 17 00:00:00 2001 From: Geolykt Date: Sun, 19 Oct 2025 16:46:27 +0200 Subject: Allow use of tab in editor panel, fix tab skipping tokens (#578) * Gracefully fall back when lang/index.txt is absent * Allow use of tab in editor panel, fix tab skipping tokens * Document #navigateToNextObfuscatedToken async requirements--- .../java/cuchaz/enigma/gui/panels/EditorPanel.java | 43 ++++++++++++++++++++++ .../cuchaz/enigma/gui/panels/IdentifierPanel.java | 29 +++------------ 2 files changed, 48 insertions(+), 24 deletions(-) (limited to 'enigma-swing') diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/EditorPanel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/EditorPanel.java index 92d1bd7f..377f63c6 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/EditorPanel.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/EditorPanel.java @@ -1,11 +1,13 @@ package cuchaz.enigma.gui.panels; +import java.awt.AWTKeyStroke; import java.awt.Color; import java.awt.Component; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.GridLayout; +import java.awt.KeyboardFocusManager; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -16,8 +18,11 @@ import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.NavigableSet; +import java.util.Set; import javax.swing.JButton; import javax.swing.JComponent; @@ -120,6 +125,11 @@ public class EditorPanel { customizeEditor(this.editor); this.editor.addCaretListener(event -> onCaretMove(event.getDot(), this.mouseIsPressed)); + // Remove the tab key from focus traversal keys (we give it a different meaning) + Set focusTraversalKeys = new HashSet<>(this.editor.getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS)); + focusTraversalKeys.removeIf(key -> key.getKeyCode() == KeyEvent.VK_TAB); + this.editor.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, focusTraversalKeys); + // set unit increment to height of one line, the amount scrolled per // mouse wheel rotation is then controlled by OS settings this.editorScrollPane.getVerticalScrollBar().setUnitIncrement(this.editor.getFontMetrics(this.editor.getFont()).getHeight()); @@ -199,6 +209,9 @@ public class EditorPanel { EditorPanel.this.shouldNavigateOnClick = true; // CTRL break; } + } else if (event.getKeyCode() == KeyEvent.VK_TAB) { + EditorPanel.this.navigateToNextObfuscatedToken(); + event.consume(); } } @@ -668,6 +681,36 @@ public class EditorPanel { } } + /** + * Navigate to the next obfuscated token that can be renamed. + * + *

If the tokens are damaged, then this method should not be called + * synchronously. Instead, the call should be wrapped in a + * {@link SwingUtilities#invokeLater(Runnable)}. Failing to do so + * will induce invalid token highlighting regions. + */ + public void navigateToNextObfuscatedToken() { + int caretPos = this.getEditor().getCaretPosition(); + Token token = this.getToken(this.getEditor().getCaretPosition()); + NavigableSet obfuscatedTokens = this.getSource().getTokenStore().getByType().get(RenamableTokenType.OBFUSCATED); + Token next; + + if (token == null) { + next = obfuscatedTokens.higher(new Token(caretPos, caretPos, null)); + } else { + next = obfuscatedTokens.higher(token); + } + + if (next == null) { + // Wrap to start of document + next = obfuscatedTokens.pollFirst(); + } + + if (next != null) { + this.navigateToToken(next); + } + } + public void navigateToToken(Token token) { if (token == null) { throw new IllegalArgumentException("Token cannot be null!"); diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/IdentifierPanel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/IdentifierPanel.java index 6a8d7347..cf336e00 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/IdentifierPanel.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/IdentifierPanel.java @@ -23,8 +23,6 @@ import cuchaz.enigma.gui.events.ConvertingTextFieldListener; import cuchaz.enigma.gui.util.GridBagConstraintsBuilder; import cuchaz.enigma.gui.util.GuiUtil; import cuchaz.enigma.gui.util.ScaleUtil; -import cuchaz.enigma.source.RenamableTokenType; -import cuchaz.enigma.source.Token; import cuchaz.enigma.translation.mapping.AccessModifier; import cuchaz.enigma.translation.mapping.EntryChange; import cuchaz.enigma.translation.mapping.EntryMapping; @@ -197,36 +195,19 @@ public class IdentifierPanel { @Override public void onStopEditing(ConvertingTextField field, StopEditingCause cause) { + EditorPanel e = gui.getActiveEditor(); + if (cause != StopEditingCause.ABORT) { vc.reset(); vc.setActiveElement(field); doRename(field.getText()); - if (cause == StopEditingCause.TAB) { - EditorPanel editor = gui.getActiveEditor(); - - if (editor == null) { - return; - } - - Token token = editor.getToken(editor.getEditor().getCaretPosition()); - - SwingUtilities.invokeLater(() -> { - Token next = editor.getSource().getTokenStore().getByType().get(RenamableTokenType.OBFUSCATED).higher(token); - - if (next == null) { - editor.getEditor().requestFocusInWindow(); - } else { - editor.navigateToToken(next); - } - }); - - return; + if (cause == StopEditingCause.TAB && e != null) { + // invokeLater as per the method's javadocs + SwingUtilities.invokeLater(e::navigateToNextObfuscatedToken); } } - EditorPanel e = gui.getActiveEditor(); - if (e != null) { e.getEditor().requestFocusInWindow(); } -- cgit v1.2.3