mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 02:30:08 -08:00
GUI, deck: improved cards search (close #6548):
- added non-strict search (enter multiple words in any order or case); - added strict search (enter exact phrase inside quotes);
This commit is contained in:
parent
501b7e882b
commit
6521f0b978
4 changed files with 96 additions and 99 deletions
|
|
@ -1044,7 +1044,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
||||||
selectBySearchPanelC.fill = GridBagConstraints.VERTICAL;
|
selectBySearchPanelC.fill = GridBagConstraints.VERTICAL;
|
||||||
|
|
||||||
searchByTextField = new JTextField();
|
searchByTextField = new JTextField();
|
||||||
searchByTextField.setToolTipText("Searches for card names, types, rarity, casting cost and rules text. NB: Mana symbols are written like {W},{U},{C} etc");
|
searchByTextField.setToolTipText("Search cards by any data like name or mana symbols like {W}, {U}, {C}, etc (use quotes for exact search)");
|
||||||
searchByTextField.addKeyListener(new KeyAdapter() {
|
searchByTextField.addKeyListener(new KeyAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void keyReleased(KeyEvent e) {
|
public void keyReleased(KeyEvent e) {
|
||||||
|
|
|
||||||
|
|
@ -780,7 +780,7 @@
|
||||||
</Component>
|
</Component>
|
||||||
<Component class="javax.swing.JTextField" name="jTextFieldSearch">
|
<Component class="javax.swing.JTextField" name="jTextFieldSearch">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="toolTipText" type="java.lang.String" value="Searches for card names and in the rule text of the card."/>
|
<Property name="toolTipText" type="java.lang.String" value="Search cards by any data like name or mana symbols like {W}, {U}, {C}, etc (use quotes for exact search)"/>
|
||||||
</Properties>
|
</Properties>
|
||||||
</Component>
|
</Component>
|
||||||
<Component class="javax.swing.JCheckBox" name="chkNames">
|
<Component class="javax.swing.JCheckBox" name="chkNames">
|
||||||
|
|
|
||||||
|
|
@ -1085,7 +1085,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
jTextFieldSearch.setToolTipText("Searches for card names and in the rule text of the card.");
|
jTextFieldSearch.setToolTipText("Search cards by any data like name or mana symbols like {W}, {U}, {C}, etc (use quotes for exact search)");
|
||||||
|
|
||||||
chkNames.setSelected(true);
|
chkNames.setSelected(true);
|
||||||
chkNames.setText("Names");
|
chkNames.setText("Names");
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,27 @@
|
||||||
package mage.filter.predicate.card;
|
package mage.filter.predicate.card;
|
||||||
|
|
||||||
import mage.cards.*;
|
import mage.cards.Card;
|
||||||
|
import mage.cards.CardWithSpellOption;
|
||||||
|
import mage.cards.ModalDoubleFacedCard;
|
||||||
|
import mage.cards.SplitCard;
|
||||||
import mage.cards.mock.MockCard;
|
import mage.cards.mock.MockCard;
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
import mage.constants.SuperType;
|
import mage.constants.SuperType;
|
||||||
import mage.filter.predicate.Predicate;
|
import mage.filter.predicate.Predicate;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Special predicate to search cards in deck editor
|
* Special predicate to search cards in deck editor
|
||||||
*
|
*
|
||||||
* @author North
|
* @author North, JayDi85
|
||||||
*/
|
*/
|
||||||
public class CardTextPredicate implements Predicate<Card> {
|
public class CardTextPredicate implements Predicate<Card> {
|
||||||
|
|
||||||
|
|
@ -22,7 +30,10 @@ public class CardTextPredicate implements Predicate<Card> {
|
||||||
private final boolean inTypes;
|
private final boolean inTypes;
|
||||||
private final boolean inRules;
|
private final boolean inRules;
|
||||||
private final boolean isUnique;
|
private final boolean isUnique;
|
||||||
private HashMap<String, Boolean> seenCards = null;
|
private HashMap<String, Boolean> seenCards;
|
||||||
|
private final Pattern pattern;
|
||||||
|
private final Matcher matcher;
|
||||||
|
private final List<String> textTokens;
|
||||||
|
|
||||||
public CardTextPredicate(String text, boolean inNames, boolean inTypes, boolean inRules, boolean isUnique) {
|
public CardTextPredicate(String text, boolean inNames, boolean inTypes, boolean inRules, boolean isUnique) {
|
||||||
this.text = text;
|
this.text = text;
|
||||||
|
|
@ -30,22 +41,33 @@ public class CardTextPredicate implements Predicate<Card> {
|
||||||
this.inTypes = inTypes;
|
this.inTypes = inTypes;
|
||||||
this.inRules = inRules;
|
this.inRules = inRules;
|
||||||
this.isUnique = isUnique;
|
this.isUnique = isUnique;
|
||||||
seenCards = new HashMap<>();
|
this.seenCards = new HashMap<>();
|
||||||
|
|
||||||
|
// regexp to find texts inside "xxx" like
|
||||||
|
// "123 345" → ["123", "345"]
|
||||||
|
// "123 345 678" → ["123", "345", "678"]
|
||||||
|
// "123 "345 678"" → ["123", "345 678"]
|
||||||
|
this.textTokens = new ArrayList<>();
|
||||||
|
this.pattern = Pattern.compile("\"([^\"]*)\"|(\\S+)");
|
||||||
|
this.matcher = this.pattern.matcher(text.toLowerCase(Locale.ENGLISH));
|
||||||
|
while (matcher.find()) {
|
||||||
|
if (matcher.group(1) != null) {
|
||||||
|
// inside "xxx"
|
||||||
|
this.textTokens.add(matcher.group(1));
|
||||||
|
} else {
|
||||||
|
// normal xxx
|
||||||
|
this.textTokens.add(matcher.group(2));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Card input, Game game) {
|
public boolean apply(Card input, Game game) {
|
||||||
if (text.isEmpty() && !isUnique) {
|
if (this.textTokens.isEmpty()) {
|
||||||
return true;
|
return saveAndReturnUniqueFind(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (text.isEmpty() && isUnique) {
|
// name: need all tokens
|
||||||
boolean found = !seenCards.containsKey(input.getName());
|
|
||||||
seenCards.put(input.getName(), true);
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
// first check in card name
|
|
||||||
if (inNames) {
|
if (inNames) {
|
||||||
String fullName = input.getName();
|
String fullName = input.getName();
|
||||||
if (input instanceof MockCard) {
|
if (input instanceof MockCard) {
|
||||||
|
|
@ -55,99 +77,74 @@ public class CardTextPredicate implements Predicate<Card> {
|
||||||
} else if (input instanceof CardWithSpellOption) {
|
} else if (input instanceof CardWithSpellOption) {
|
||||||
fullName = input.getName() + MockCard.CARD_WITH_SPELL_OPTION_NAME_SEPARATOR + ((CardWithSpellOption) input).getSpellCard().getName();
|
fullName = input.getName() + MockCard.CARD_WITH_SPELL_OPTION_NAME_SEPARATOR + ((CardWithSpellOption) input).getSpellCard().getName();
|
||||||
}
|
}
|
||||||
|
if (textHasTokens(fullName, true)) {
|
||||||
if (fullName.toLowerCase(Locale.ENGLISH).contains(text.toLowerCase(Locale.ENGLISH))) {
|
return saveAndReturnUniqueFind(input);
|
||||||
if (isUnique && seenCards.containsKey(input.getName())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (isUnique) {
|
|
||||||
seenCards.put(input.getName(), true);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// separate by spaces
|
// rules: need all tokens
|
||||||
String[] tokens = text.toLowerCase(Locale.ENGLISH).split(" ");
|
if (inRules) {
|
||||||
for (String token : tokens) {
|
List<String> fullRules = new ArrayList<>(input.getRules(game));
|
||||||
boolean found = false;
|
if (input instanceof SplitCard) {
|
||||||
if (!token.isEmpty()) {
|
fullRules.addAll(((SplitCard) input).getLeftHalfCard().getRules(game));
|
||||||
// then try to find in rules
|
fullRules.addAll(((SplitCard) input).getRightHalfCard().getRules(game));
|
||||||
if (inRules) {
|
|
||||||
if (input instanceof SplitCard) {
|
|
||||||
for (String rule : ((SplitCard) input).getLeftHalfCard().getRules(game)) {
|
|
||||||
if (rule.toLowerCase(Locale.ENGLISH).contains(token)) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (String rule : ((SplitCard) input).getRightHalfCard().getRules(game)) {
|
|
||||||
if (rule.toLowerCase(Locale.ENGLISH).contains(token)) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input instanceof ModalDoubleFacedCard) {
|
|
||||||
for (String rule : ((ModalDoubleFacedCard) input).getLeftHalfCard().getRules(game)) {
|
|
||||||
if (rule.toLowerCase(Locale.ENGLISH).contains(token)) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (String rule : ((ModalDoubleFacedCard) input).getRightHalfCard().getRules(game)) {
|
|
||||||
if (rule.toLowerCase(Locale.ENGLISH).contains(token)) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input instanceof CardWithSpellOption) {
|
|
||||||
for (String rule : ((CardWithSpellOption) input).getSpellCard().getRules(game)) {
|
|
||||||
if (rule.toLowerCase(Locale.ENGLISH).contains(token)) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String rule : input.getRules(game)) {
|
|
||||||
if (rule.toLowerCase(Locale.ENGLISH).contains(token)) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (inTypes) {
|
|
||||||
for (SubType subType : input.getSubtype(game)) {
|
|
||||||
if (subType.toString().equalsIgnoreCase(token)) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (SuperType superType : input.getSuperType(game)) {
|
|
||||||
if (superType.toString().equalsIgnoreCase(token)) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (input instanceof ModalDoubleFacedCard) {
|
||||||
if (found && isUnique && seenCards.containsKey(input.getName())) {
|
fullRules.addAll(((ModalDoubleFacedCard) input).getLeftHalfCard().getRules(game));
|
||||||
found = false;
|
fullRules.addAll(((ModalDoubleFacedCard) input).getRightHalfCard().getRules(game));
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (input instanceof CardWithSpellOption) {
|
||||||
return false;
|
fullRules.addAll(((CardWithSpellOption) input).getSpellCard().getRules(game));
|
||||||
|
}
|
||||||
|
if (textHasTokens(String.join("@", fullRules), true)) {
|
||||||
|
return saveAndReturnUniqueFind(input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// types: need all tokens
|
||||||
|
if (inTypes) {
|
||||||
|
List<String> fullTypes = new ArrayList<>();
|
||||||
|
fullTypes.addAll(input.getSubtype(game).stream().map(SubType::toString).collect(Collectors.toList()));
|
||||||
|
fullTypes.addAll(input.getSuperType(game).stream().map(SuperType::toString).collect(Collectors.toList()));
|
||||||
|
if (textHasTokens(String.join("@", fullTypes), true)) {
|
||||||
|
return saveAndReturnUniqueFind(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// not found
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean saveAndReturnUniqueFind(Card card) {
|
||||||
if (isUnique) {
|
if (isUnique) {
|
||||||
seenCards.put(input.getName(), true);
|
boolean found = !seenCards.containsKey(card.getName());
|
||||||
|
seenCards.put(card.getName(), true);
|
||||||
|
return found;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
}
|
||||||
|
|
||||||
|
private boolean textHasTokens(String text, boolean needAllTokens) {
|
||||||
|
String searchingText = text + "@" + text.replace(", ", " ");
|
||||||
|
searchingText += "@" + searchingText.toLowerCase(Locale.ENGLISH);
|
||||||
|
|
||||||
|
boolean found = false;
|
||||||
|
for (String token : this.textTokens) {
|
||||||
|
if (searchingText.contains(token)) {
|
||||||
|
found = true;
|
||||||
|
if (!needAllTokens) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (needAllTokens) {
|
||||||
|
found = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue