From 1554e32e9384a6bc2cd168e061595917e1687e25 Mon Sep 17 00:00:00 2001 From: YanisBft Date: Mon, 25 Sep 2023 23:34:30 +0200 Subject: Search dialog improvements (#529) * Improve search dialogs * fix checkstyle * change search class keystroke * better dialog title & don't calculate score if not needed--- .../cuchaz/enigma/gui/dialog/SearchDialog.java | 34 ++++++++++++++++++---- .../java/cuchaz/enigma/gui/elements/MenuBar.java | 4 ++- .../java/cuchaz/enigma/gui/search/SearchUtil.java | 17 +++++++++-- enigma/src/main/resources/lang/en_us.json | 1 + 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java index 7814dd81..536ebcaa 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java @@ -29,6 +29,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; import javax.swing.DefaultListModel; import javax.swing.JButton; +import javax.swing.JCheckBox; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JList; @@ -55,7 +56,9 @@ import cuchaz.enigma.translation.representation.entry.ParentedEntry; import cuchaz.enigma.utils.I18n; public class SearchDialog { + private final JPanel inputPanel; private final JTextField searchField; + private final JCheckBox onlyExactMatchesCheckbox; private DefaultListModel classListModel; private final JList classList; private final JDialog dialog; @@ -69,11 +72,15 @@ public class SearchDialog { su = new SearchUtil<>(); - dialog = new JDialog(parent.getFrame(), I18n.translate("menu.search"), true); + dialog = new JDialog(parent.getFrame(), true); JPanel contentPane = new JPanel(); contentPane.setBorder(ScaleUtil.createEmptyBorder(4, 4, 4, 4)); contentPane.setLayout(new BorderLayout(ScaleUtil.scale(4), ScaleUtil.scale(4))); + inputPanel = new JPanel(); + inputPanel.setLayout(new BorderLayout(ScaleUtil.scale(4), ScaleUtil.scale(4))); + contentPane.add(inputPanel, BorderLayout.NORTH); + searchField = new JTextField(); searchField.getDocument().addDocumentListener(new DocumentListener() { @Override @@ -108,7 +115,11 @@ public class SearchDialog { } }); searchField.addActionListener(e -> openSelected()); - contentPane.add(searchField, BorderLayout.NORTH); + inputPanel.add(searchField, BorderLayout.NORTH); + + onlyExactMatchesCheckbox = new JCheckBox(I18n.translate("menu.search.only_exact_matches")); + onlyExactMatchesCheckbox.addActionListener(e -> updateList()); + inputPanel.add(onlyExactMatchesCheckbox, BorderLayout.SOUTH); classListModel = new DefaultListModel<>(); classList = new JList<>(); @@ -167,6 +178,7 @@ public class SearchDialog { searchField.requestFocus(); searchField.selectAll(); + dialog.setTitle(I18n.translate(type.getTranslationKey())); dialog.setVisible(true); } @@ -238,7 +250,7 @@ public class SearchDialog { } }; - currentSearch = su.asyncSearch(searchField.getText(), (idx, e) -> queue.add(new Order(idx, e))); + currentSearch = su.asyncSearch(searchField.getText(), (idx, e) -> queue.add(new Order(idx, e)), onlyExactMatchesCheckbox.isSelected()); SwingUtilities.invokeLater(updater); } @@ -328,8 +340,18 @@ public class SearchDialog { } public enum Type { - CLASS, - METHOD, - FIELD + CLASS("menu.search.class"), + METHOD("menu.search.method"), + FIELD("menu.search.field"); + + private final String translationKey; + + Type(String translationKey) { + this.translationKey = translationKey; + } + + public String getTranslationKey() { + return translationKey; + } } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java index 24a69b65..1cfad504 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java @@ -138,7 +138,9 @@ public class MenuBar { ui.add(this.helpMenu); this.saveMappingsItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK)); - this.searchClassItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.SHIFT_DOWN_MASK)); + this.searchClassItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1, InputEvent.CTRL_DOWN_MASK)); + this.searchMethodItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_2, InputEvent.CTRL_DOWN_MASK)); + this.searchFieldItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_3, InputEvent.CTRL_DOWN_MASK)); this.jarOpenItem.addActionListener(_e -> this.onOpenJarClicked()); this.jarCloseItem.addActionListener(_e -> this.gui.getController().closeJar()); diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchUtil.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchUtil.java index c8212ce5..67414ca7 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchUtil.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchUtil.java @@ -55,7 +55,7 @@ public class SearchUtil { return entries.values().parallelStream().map(e -> new Pair<>(e, e.getScore(term, hitCount.getOrDefault(e.searchEntry.getIdentifier(), 0)))).filter(e -> e.b > 0).sorted(Comparator.comparingDouble(o -> -o.b)).map(e -> e.a.searchEntry).sequential(); } - public SearchControl asyncSearch(String term, SearchResultConsumer consumer) { + public SearchControl asyncSearch(String term, SearchResultConsumer consumer, boolean onlyExactMatches) { Map hitCount = new HashMap<>(this.hitCount); Map> entries = new HashMap<>(this.entries); float[] scores = new float[entries.size()]; @@ -71,6 +71,11 @@ public class SearchUtil { return; } + // if onlyExactMatches is true, don't add any entries that don't have an exact match + if (onlyExactMatches && value.searchEntry.getSearchableNames().stream().noneMatch(name -> name.equalsIgnoreCase(term))) { + return; + } + float score = value.getScore(term, hitCount.getOrDefault(value.searchEntry.getIdentifier(), 0)); if (score <= 0) { @@ -140,7 +145,15 @@ public class SearchUtil { public float getScore(String term, int hits) { String ucTerm = term.toUpperCase(Locale.ROOT); - float maxScore = (float) Arrays.stream(components).mapToDouble(name -> getScoreFor(ucTerm, name)).max().orElse(0.0); + float maxScore; + + // if exact match, make sure it's at the top of the list + if (searchEntry.getSearchableNames().stream().anyMatch(name -> name.equalsIgnoreCase(term))) { + maxScore = Float.MAX_VALUE / 2; + } else { + maxScore = (float) Arrays.stream(components).mapToDouble(name -> getScoreFor(ucTerm, name)).max().orElse(0.0); + } + return maxScore * (hits + 1); } diff --git a/enigma/src/main/resources/lang/en_us.json b/enigma/src/main/resources/lang/en_us.json index 5d7ee258..421620e9 100644 --- a/enigma/src/main/resources/lang/en_us.json +++ b/enigma/src/main/resources/lang/en_us.json @@ -53,6 +53,7 @@ "menu.search.class": "Search Classes", "menu.search.method": "Search Methods", "menu.search.field": "Search Fields", + "menu.search.only_exact_matches": "Only show exact matches", "menu.collab": "Collab", "menu.collab.connect": "Connect to Server", "menu.collab.connect.error": "Error connecting to server", -- cgit v1.2.3