mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 03:08:02 +00:00
Support advanced search in Card list choosers
This commit is contained in:
@@ -303,6 +303,9 @@ public final class ColorSet implements Comparable<ColorSet>, Iterable<Byte>, Ser
|
||||
}
|
||||
|
||||
public Set<Color> toEnumSet() {
|
||||
if (isColorless()) {
|
||||
return EnumSet.of(Color.COLORLESS);
|
||||
}
|
||||
List<Color> list = new ArrayList<Color>();
|
||||
for (Color c : Color.values()) {
|
||||
if (hasAnyColor(c.getColormask())) {
|
||||
|
||||
@@ -866,7 +866,7 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
|
||||
if (lockFiltering || !initialized) { return false; }
|
||||
|
||||
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()) {
|
||||
predicates.add(filter.buildPredicate(genericType));
|
||||
}
|
||||
|
||||
@@ -46,9 +46,13 @@ public class AdvancedSearchFilter<T extends InventoryItem> extends ItemFilter<T>
|
||||
if (expression.isEmpty()) {
|
||||
return Predicates.alwaysTrue();
|
||||
}
|
||||
return getPredicate();
|
||||
}
|
||||
|
||||
public Predicate<T> getPredicate() {
|
||||
return getPredicatePiece(new ExpressionIterator());
|
||||
}
|
||||
|
||||
|
||||
private class ExpressionIterator {
|
||||
private int index;
|
||||
private boolean hasNext() {
|
||||
|
||||
@@ -18,8 +18,12 @@ import forge.card.CardZoom.ActivateHandler;
|
||||
import forge.game.card.CardView;
|
||||
import forge.game.card.IHasCardView;
|
||||
import forge.game.player.PlayerView;
|
||||
import forge.item.InventoryItem;
|
||||
import forge.item.PaperCard;
|
||||
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.views.VAvatar;
|
||||
import forge.screens.match.views.VStack;
|
||||
@@ -87,6 +91,11 @@ public class FChoiceList<T> extends FList<T> implements ActivateHandler {
|
||||
return renderer.layoutHorizontal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdvancedSearchFilter<? extends InventoryItem> getAdvancedSearchFilter(ListChooser<T> listChooser) {
|
||||
return renderer.getAdvancedSearchFilter(listChooser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tap(Integer index, T value, float x, float y, int count) {
|
||||
if (maxChoices > 1) {
|
||||
@@ -290,6 +299,10 @@ public class FChoiceList<T> extends FList<T> implements ActivateHandler {
|
||||
public boolean layoutHorizontal() {
|
||||
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 {
|
||||
@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) {
|
||||
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
|
||||
protected class CardItemRenderer extends ItemRenderer {
|
||||
|
||||
@@ -11,6 +11,8 @@ import forge.assets.FSkinColor;
|
||||
import forge.assets.FSkinFont;
|
||||
import forge.assets.FSkinTexture;
|
||||
import forge.assets.FSkinColor.Colors;
|
||||
import forge.item.InventoryItem;
|
||||
import forge.itemmanager.filters.AdvancedSearchFilter;
|
||||
import forge.model.FModel;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.screens.FScreen;
|
||||
@@ -300,6 +302,10 @@ public class FList<T> extends FScrollPane implements Iterable<T> {
|
||||
public boolean layoutHorizontal() {
|
||||
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> {
|
||||
|
||||
@@ -19,11 +19,21 @@
|
||||
package forge.toolbox;
|
||||
|
||||
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.Iterables;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.Graphics;
|
||||
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.FEventHandler;
|
||||
import forge.toolbox.FOptionPane;
|
||||
@@ -66,11 +76,13 @@ public class ListChooser<T> extends FContainer {
|
||||
|
||||
// initialized before; listeners may be added to it
|
||||
private FTextField txtSearch;
|
||||
private FLabel btnSearch;
|
||||
private ChoiceList lstChoices;
|
||||
private FOptionPane optionPane;
|
||||
private final Collection<T> list;
|
||||
private final Function<T, String> display;
|
||||
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) {
|
||||
FThreads.assertExecutedByEdt(true);
|
||||
@@ -87,26 +99,38 @@ public class ListChooser<T> extends FContainer {
|
||||
txtSearch.setChangedHandler(new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
String pattern = txtSearch.getText().toLowerCase();
|
||||
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);
|
||||
applyFilters();
|
||||
}
|
||||
});
|
||||
|
||||
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;
|
||||
@@ -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() {
|
||||
boolean needRevalidate = getHeight() > 0; //needs to revalidate if already has height
|
||||
if (lstChoices.getListItemRenderer().layoutHorizontal()) {
|
||||
@@ -193,10 +262,24 @@ public class ListChooser<T> extends FContainer {
|
||||
protected void doLayout(float width, float height) {
|
||||
float y = 0;
|
||||
if (txtSearch != null) {
|
||||
float padding = txtSearch.getHeight() * 0.25f;
|
||||
float fieldWidth = width;
|
||||
float fieldHeight = txtSearch.getHeight();
|
||||
float padding = fieldHeight * 0.25f;
|
||||
y += padding;
|
||||
txtSearch.setBounds(0, y, width, txtSearch.getHeight());
|
||||
y += txtSearch.getHeight() + padding;
|
||||
if (btnSearch != null) {
|
||||
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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user