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;
|
||||
|
||||
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() {
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
|
|
|
|||
|
|
@ -780,7 +780,7 @@
|
|||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="jTextFieldSearch">
|
||||
<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>
|
||||
</Component>
|
||||
<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.setText("Names");
|
||||
|
|
|
|||
|
|
@ -1,19 +1,27 @@
|
|||
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.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
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
|
||||
*
|
||||
* @author North
|
||||
* @author North, JayDi85
|
||||
*/
|
||||
public class CardTextPredicate implements Predicate<Card> {
|
||||
|
||||
|
|
@ -22,7 +30,10 @@ public class CardTextPredicate implements Predicate<Card> {
|
|||
private final boolean inTypes;
|
||||
private final boolean inRules;
|
||||
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) {
|
||||
this.text = text;
|
||||
|
|
@ -30,22 +41,33 @@ public class CardTextPredicate implements Predicate<Card> {
|
|||
this.inTypes = inTypes;
|
||||
this.inRules = inRules;
|
||||
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
|
||||
public boolean apply(Card input, Game game) {
|
||||
if (text.isEmpty() && !isUnique) {
|
||||
return true;
|
||||
if (this.textTokens.isEmpty()) {
|
||||
return saveAndReturnUniqueFind(input);
|
||||
}
|
||||
|
||||
if (text.isEmpty() && isUnique) {
|
||||
boolean found = !seenCards.containsKey(input.getName());
|
||||
seenCards.put(input.getName(), true);
|
||||
return found;
|
||||
}
|
||||
|
||||
// first check in card name
|
||||
// name: need all tokens
|
||||
if (inNames) {
|
||||
String fullName = input.getName();
|
||||
if (input instanceof MockCard) {
|
||||
|
|
@ -55,99 +77,74 @@ public class CardTextPredicate implements Predicate<Card> {
|
|||
} else if (input instanceof CardWithSpellOption) {
|
||||
fullName = input.getName() + MockCard.CARD_WITH_SPELL_OPTION_NAME_SEPARATOR + ((CardWithSpellOption) input).getSpellCard().getName();
|
||||
}
|
||||
|
||||
if (fullName.toLowerCase(Locale.ENGLISH).contains(text.toLowerCase(Locale.ENGLISH))) {
|
||||
if (isUnique && seenCards.containsKey(input.getName())) {
|
||||
return false;
|
||||
}
|
||||
if (isUnique) {
|
||||
seenCards.put(input.getName(), true);
|
||||
}
|
||||
return true;
|
||||
if (textHasTokens(fullName, true)) {
|
||||
return saveAndReturnUniqueFind(input);
|
||||
}
|
||||
}
|
||||
|
||||
// separate by spaces
|
||||
String[] tokens = text.toLowerCase(Locale.ENGLISH).split(" ");
|
||||
for (String token : tokens) {
|
||||
boolean found = false;
|
||||
if (!token.isEmpty()) {
|
||||
// then try to find in rules
|
||||
// rules: need all tokens
|
||||
if (inRules) {
|
||||
List<String> fullRules = new ArrayList<>(input.getRules(game));
|
||||
if (input instanceof SplitCard) {
|
||||
for (String rule : ((SplitCard) input).getLeftHalfCard().getRules(game)) {
|
||||
if (rule.toLowerCase(Locale.ENGLISH).contains(token)) {
|
||||
found = true;
|
||||
break;
|
||||
fullRules.addAll(((SplitCard) input).getLeftHalfCard().getRules(game));
|
||||
fullRules.addAll(((SplitCard) input).getRightHalfCard().getRules(game));
|
||||
}
|
||||
}
|
||||
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;
|
||||
fullRules.addAll(((ModalDoubleFacedCard) input).getLeftHalfCard().getRules(game));
|
||||
fullRules.addAll(((ModalDoubleFacedCard) input).getRightHalfCard().getRules(game));
|
||||
}
|
||||
}
|
||||
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;
|
||||
fullRules.addAll(((CardWithSpellOption) input).getSpellCard().getRules(game));
|
||||
}
|
||||
if (textHasTokens(String.join("@", fullRules), true)) {
|
||||
return saveAndReturnUniqueFind(input);
|
||||
}
|
||||
}
|
||||
|
||||
for (String rule : input.getRules(game)) {
|
||||
if (rule.toLowerCase(Locale.ENGLISH).contains(token)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// types: need all tokens
|
||||
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;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (found && isUnique && seenCards.containsKey(input.getName())) {
|
||||
found = false;
|
||||
}
|
||||
if (!found) {
|
||||
// not found
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean saveAndReturnUniqueFind(Card card) {
|
||||
if (isUnique) {
|
||||
boolean found = !seenCards.containsKey(card.getName());
|
||||
seenCards.put(card.getName(), true);
|
||||
return found;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isUnique) {
|
||||
seenCards.put(input.getName(), 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;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
if (needAllTokens) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue