Support advanced search in Card list choosers

This commit is contained in:
drdev
2015-09-06 19:53:05 +00:00
parent 5e6f7453ef
commit 0282f861ef
6 changed files with 156 additions and 23 deletions

View File

@@ -303,6 +303,9 @@ public final class ColorSet implements Comparable<ColorSet>, Iterable<Byte>, Ser
} }
public Set<Color> toEnumSet() { public Set<Color> toEnumSet() {
if (isColorless()) {
return EnumSet.of(Color.COLORLESS);
}
List<Color> list = new ArrayList<Color>(); List<Color> list = new ArrayList<Color>();
for (Color c : Color.values()) { for (Color c : Color.values()) {
if (hasAnyColor(c.getColormask())) { if (hasAnyColor(c.getColormask())) {

View File

@@ -866,7 +866,7 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
if (lockFiltering || !initialized) { return false; } if (lockFiltering || !initialized) { return false; }
List<Predicate<? super T>> predicates = new ArrayList<Predicate<? super T>>(); List<Predicate<? super T>> predicates = new ArrayList<Predicate<? super T>>();
for (ItemFilter<? extends T> filter : filters) { //TODO: Support custom filter logic for (ItemFilter<? extends T> filter : filters) {
if (!filter.isEmpty()) { if (!filter.isEmpty()) {
predicates.add(filter.buildPredicate(genericType)); predicates.add(filter.buildPredicate(genericType));
} }

View File

@@ -46,6 +46,10 @@ public class AdvancedSearchFilter<T extends InventoryItem> extends ItemFilter<T>
if (expression.isEmpty()) { if (expression.isEmpty()) {
return Predicates.alwaysTrue(); return Predicates.alwaysTrue();
} }
return getPredicate();
}
public Predicate<T> getPredicate() {
return getPredicatePiece(new ExpressionIterator()); return getPredicatePiece(new ExpressionIterator());
} }

View File

@@ -18,8 +18,12 @@ import forge.card.CardZoom.ActivateHandler;
import forge.game.card.CardView; import forge.game.card.CardView;
import forge.game.card.IHasCardView; import forge.game.card.IHasCardView;
import forge.game.player.PlayerView; import forge.game.player.PlayerView;
import forge.item.InventoryItem;
import forge.item.PaperCard; import forge.item.PaperCard;
import forge.itemmanager.AdvancedSearch.FilterOperator; import forge.itemmanager.AdvancedSearch.FilterOperator;
import forge.itemmanager.CardManager;
import forge.itemmanager.filters.AdvancedSearchFilter;
import forge.itemmanager.filters.ItemFilter;
import forge.screens.match.MatchController; import forge.screens.match.MatchController;
import forge.screens.match.views.VAvatar; import forge.screens.match.views.VAvatar;
import forge.screens.match.views.VStack; import forge.screens.match.views.VStack;
@@ -87,6 +91,11 @@ public class FChoiceList<T> extends FList<T> implements ActivateHandler {
return renderer.layoutHorizontal(); return renderer.layoutHorizontal();
} }
@Override
public AdvancedSearchFilter<? extends InventoryItem> getAdvancedSearchFilter(ListChooser<T> listChooser) {
return renderer.getAdvancedSearchFilter(listChooser);
}
@Override @Override
public boolean tap(Integer index, T value, float x, float y, int count) { public boolean tap(Integer index, T value, float x, float y, int count) {
if (maxChoices > 1) { if (maxChoices > 1) {
@@ -290,6 +299,10 @@ public class FChoiceList<T> extends FList<T> implements ActivateHandler {
public boolean layoutHorizontal() { public boolean layoutHorizontal() {
return false; //this doesn't need to be overridden to specify vertical layouts return false; //this doesn't need to be overridden to specify vertical layouts
} }
public AdvancedSearchFilter<? extends InventoryItem> getAdvancedSearchFilter(ListChooser<T> listChooser) {
return null; //allow overriding to support advanced search
}
} }
protected class DefaultItemRenderer extends ItemRenderer { protected class DefaultItemRenderer extends ItemRenderer {
@Override @Override
@@ -374,6 +387,30 @@ public class FChoiceList<T> extends FList<T> implements ActivateHandler {
public void drawValue(Graphics g, T value, FSkinFont font, FSkinColor foreColor, boolean pressed, float x, float y, float w, float h) { public void drawValue(Graphics g, T value, FSkinFont font, FSkinColor foreColor, boolean pressed, float x, float y, float w, float h) {
CardRenderer.drawCardListItem(g, font, foreColor, (PaperCard)value, 0, null, x, y, w, h, compactModeHandler.isCompactMode()); CardRenderer.drawCardListItem(g, font, foreColor, (PaperCard)value, 0, null, x, y, w, h, compactModeHandler.isCompactMode());
} }
@Override
public AdvancedSearchFilter<? extends InventoryItem> getAdvancedSearchFilter(final ListChooser<T> listChooser) {
//must create a fake CardManager in order to utilize advance search filter
final CardManager manager = new CardManager(true) {
@Override
public void applyNewOrModifiedFilter(final ItemFilter<? extends PaperCard> filter) {
//handle update the visibility of the advanced search filter
boolean empty = filter.isEmpty();
ItemFilter<? extends PaperCard>.Widget widget = filter.getWidget();
if (widget.isVisible() == empty) {
widget.setVisible(!empty);
listChooser.revalidate();
}
listChooser.applyFilters();
}
@Override
protected void addDefaultFilters() {
//avoid creating unneeded filters
}
};
return CardManager.createAdvancedSearchFilter(manager);
}
} }
//special renderer for cards //special renderer for cards
protected class CardItemRenderer extends ItemRenderer { protected class CardItemRenderer extends ItemRenderer {

View File

@@ -11,6 +11,8 @@ import forge.assets.FSkinColor;
import forge.assets.FSkinFont; import forge.assets.FSkinFont;
import forge.assets.FSkinTexture; import forge.assets.FSkinTexture;
import forge.assets.FSkinColor.Colors; import forge.assets.FSkinColor.Colors;
import forge.item.InventoryItem;
import forge.itemmanager.filters.AdvancedSearchFilter;
import forge.model.FModel; import forge.model.FModel;
import forge.properties.ForgePreferences.FPref; import forge.properties.ForgePreferences.FPref;
import forge.screens.FScreen; import forge.screens.FScreen;
@@ -300,6 +302,10 @@ public class FList<T> extends FScrollPane implements Iterable<T> {
public boolean layoutHorizontal() { public boolean layoutHorizontal() {
return false; //this doesn't need to be overridden to specify vertical layouts return false; //this doesn't need to be overridden to specify vertical layouts
} }
public AdvancedSearchFilter<? extends InventoryItem> getAdvancedSearchFilter(ListChooser<V> listChooser) {
return null; //allow overriding to support advanced search
}
} }
public static class DefaultListItemRenderer<V> extends ListItemRenderer<V> { public static class DefaultListItemRenderer<V> extends ListItemRenderer<V> {

View File

@@ -19,11 +19,21 @@
package forge.toolbox; package forge.toolbox;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import forge.FThreads; import forge.FThreads;
import forge.Graphics; import forge.Graphics;
import forge.assets.FSkinFont; import forge.assets.FSkinFont;
import forge.assets.FSkinImage;
import forge.item.InventoryItem;
import forge.itemmanager.filters.AdvancedSearchFilter;
import forge.itemmanager.filters.ItemFilter;
import forge.itemmanager.filters.ListLabelFilter;
import forge.menu.FMenuItem;
import forge.menu.FPopupMenu;
import forge.toolbox.FEvent; import forge.toolbox.FEvent;
import forge.toolbox.FEvent.FEventHandler; import forge.toolbox.FEvent.FEventHandler;
import forge.toolbox.FOptionPane; import forge.toolbox.FOptionPane;
@@ -66,11 +76,13 @@ public class ListChooser<T> extends FContainer {
// initialized before; listeners may be added to it // initialized before; listeners may be added to it
private FTextField txtSearch; private FTextField txtSearch;
private FLabel btnSearch;
private ChoiceList lstChoices; private ChoiceList lstChoices;
private FOptionPane optionPane; private FOptionPane optionPane;
private final Collection<T> list; private final Collection<T> list;
private final Function<T, String> display; private final Function<T, String> display;
private final Callback<List<T>> callback; private final Callback<List<T>> callback;
private AdvancedSearchFilter<? extends InventoryItem> advancedSearchFilter;
public ListChooser(final String title, final int minChoices, final int maxChoices, final Collection<T> list0, final Function<T, String> display0, final Callback<List<T>> callback0) { public ListChooser(final String title, final int minChoices, final int maxChoices, final Collection<T> list0, final Function<T, String> display0, final Callback<List<T>> callback0) {
FThreads.assertExecutedByEdt(true); FThreads.assertExecutedByEdt(true);
@@ -87,26 +99,38 @@ public class ListChooser<T> extends FContainer {
txtSearch.setChangedHandler(new FEventHandler() { txtSearch.setChangedHandler(new FEventHandler() {
@Override @Override
public void handleEvent(FEvent e) { public void handleEvent(FEvent e) {
String pattern = txtSearch.getText().toLowerCase(); applyFilters();
lstChoices.clearSelection();
if (pattern.isEmpty()) {
lstChoices.setListData(list);
}
else {
List<T> filteredList = new ArrayList<T>();
for (T option : list) {
if (lstChoices.getChoiceText(option).toLowerCase().contains(pattern)) {
filteredList.add(option);
}
}
lstChoices.setListData(filteredList);
}
if (!lstChoices.isEmpty() && maxChoices > 0) {
lstChoices.addSelectedIndex(0);
}
lstChoices.setScrollTop(0);
} }
}); });
advancedSearchFilter = lstChoices.getListItemRenderer().getAdvancedSearchFilter(this);
if (advancedSearchFilter != null) {
btnSearch = add(new FLabel.ButtonBuilder()
.icon(FSkinImage.SEARCH).iconScaleFactor(0.9f).command(new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
FPopupMenu menu = new FPopupMenu() {
@Override
protected void buildMenu() {
addItem(new FMenuItem("Advanced Search", FSkinImage.SEARCH, new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
advancedSearchFilter.edit();
}
}));
addItem(new FMenuItem("Reset Filters", FSkinImage.DELETE, new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
resetFilters();
}
}));
}
};
menu.show(btnSearch, 0, btnSearch.getHeight());
}
}).build());
add(advancedSearchFilter.getWidget());
}
} }
final List<String> options; final List<String> options;
@@ -140,6 +164,51 @@ public class ListChooser<T> extends FContainer {
}; };
} }
public void resetFilters() {
txtSearch.setText("");
if (advancedSearchFilter != null) {
advancedSearchFilter.reset();
ItemFilter<? extends InventoryItem>.Widget widget = advancedSearchFilter.getWidget();
if (widget.isVisible()) {
widget.setVisible(false);
revalidate();
}
}
applyFilters();
}
@SuppressWarnings("unchecked")
public void applyFilters() {
lstChoices.clearSelection();
List<Predicate<? super T>> predicates = new ArrayList<Predicate<? super T>>();
final String pattern = txtSearch.getText().toLowerCase();
if (!pattern.isEmpty()) {
predicates.add(new Predicate<T>() {
@Override
public boolean apply(T input) {
return lstChoices.getChoiceText(input).toLowerCase().contains(pattern);
}
});
}
if (advancedSearchFilter != null && !advancedSearchFilter.isEmpty()) {
predicates.add((Predicate<? super T>)advancedSearchFilter.getPredicate());
}
if (predicates.isEmpty()) {
lstChoices.setListData(list);
}
else {
lstChoices.setListData(Iterables.filter(list, Predicates.and(predicates)));
}
if (!lstChoices.isEmpty() && lstChoices.getMaxChoices() > 0) {
lstChoices.addSelectedIndex(0);
}
lstChoices.setScrollTop(0);
}
private void updateHeight() { private void updateHeight() {
boolean needRevalidate = getHeight() > 0; //needs to revalidate if already has height boolean needRevalidate = getHeight() > 0; //needs to revalidate if already has height
if (lstChoices.getListItemRenderer().layoutHorizontal()) { if (lstChoices.getListItemRenderer().layoutHorizontal()) {
@@ -193,10 +262,24 @@ public class ListChooser<T> extends FContainer {
protected void doLayout(float width, float height) { protected void doLayout(float width, float height) {
float y = 0; float y = 0;
if (txtSearch != null) { if (txtSearch != null) {
float padding = txtSearch.getHeight() * 0.25f; float fieldWidth = width;
float fieldHeight = txtSearch.getHeight();
float padding = fieldHeight * 0.25f;
y += padding; y += padding;
txtSearch.setBounds(0, y, width, txtSearch.getHeight()); if (btnSearch != null) {
y += txtSearch.getHeight() + padding; float buttonWidth = fieldHeight;
btnSearch.setBounds(width - buttonWidth, y, buttonWidth, fieldHeight);
fieldWidth -= buttonWidth + ItemFilter.PADDING;
}
txtSearch.setBounds(0, y, fieldWidth, fieldHeight);
if (advancedSearchFilter != null && advancedSearchFilter.getWidget().isVisible()) {
padding = ItemFilter.PADDING;
y += fieldHeight + padding;
fieldHeight = FTextField.getDefaultHeight(ListLabelFilter.LABEL_FONT);
advancedSearchFilter.getWidget().setBounds(0, y, width, fieldHeight);
}
y += fieldHeight + padding;
} }
lstChoices.setBounds(0, y, width, height - y); lstChoices.setBounds(0, y, width, height - y);
} }