diff --git a/forge-core/src/main/java/forge/card/ColorSet.java b/forge-core/src/main/java/forge/card/ColorSet.java index db4751e2122..d0a93ce7b44 100644 --- a/forge-core/src/main/java/forge/card/ColorSet.java +++ b/forge-core/src/main/java/forge/card/ColorSet.java @@ -303,6 +303,9 @@ public final class ColorSet implements Comparable, Iterable, Ser } public Set toEnumSet() { + if (isColorless()) { + return EnumSet.of(Color.COLORLESS); + } List list = new ArrayList(); for (Color c : Color.values()) { if (hasAnyColor(c.getColormask())) { diff --git a/forge-gui-mobile/src/forge/itemmanager/ItemManager.java b/forge-gui-mobile/src/forge/itemmanager/ItemManager.java index 7572a0d4087..dd94826a6a5 100644 --- a/forge-gui-mobile/src/forge/itemmanager/ItemManager.java +++ b/forge-gui-mobile/src/forge/itemmanager/ItemManager.java @@ -866,7 +866,7 @@ public abstract class ItemManager extends FContainer im if (lockFiltering || !initialized) { return false; } List> predicates = new ArrayList>(); - for (ItemFilter filter : filters) { //TODO: Support custom filter logic + for (ItemFilter filter : filters) { if (!filter.isEmpty()) { predicates.add(filter.buildPredicate(genericType)); } diff --git a/forge-gui-mobile/src/forge/itemmanager/filters/AdvancedSearchFilter.java b/forge-gui-mobile/src/forge/itemmanager/filters/AdvancedSearchFilter.java index e1b477b4030..2c768f7929a 100644 --- a/forge-gui-mobile/src/forge/itemmanager/filters/AdvancedSearchFilter.java +++ b/forge-gui-mobile/src/forge/itemmanager/filters/AdvancedSearchFilter.java @@ -46,9 +46,13 @@ public class AdvancedSearchFilter extends ItemFilter if (expression.isEmpty()) { return Predicates.alwaysTrue(); } + return getPredicate(); + } + + public Predicate getPredicate() { return getPredicatePiece(new ExpressionIterator()); } - + private class ExpressionIterator { private int index; private boolean hasNext() { diff --git a/forge-gui-mobile/src/forge/toolbox/FChoiceList.java b/forge-gui-mobile/src/forge/toolbox/FChoiceList.java index d5a173d20f9..593cd6a5061 100644 --- a/forge-gui-mobile/src/forge/toolbox/FChoiceList.java +++ b/forge-gui-mobile/src/forge/toolbox/FChoiceList.java @@ -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 extends FList implements ActivateHandler { return renderer.layoutHorizontal(); } + @Override + public AdvancedSearchFilter getAdvancedSearchFilter(ListChooser 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 extends FList implements ActivateHandler { public boolean layoutHorizontal() { return false; //this doesn't need to be overridden to specify vertical layouts } + + public AdvancedSearchFilter getAdvancedSearchFilter(ListChooser listChooser) { + return null; //allow overriding to support advanced search + } } protected class DefaultItemRenderer extends ItemRenderer { @Override @@ -374,6 +387,30 @@ public class FChoiceList extends FList 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 getAdvancedSearchFilter(final ListChooser 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 filter) { + //handle update the visibility of the advanced search filter + boolean empty = filter.isEmpty(); + ItemFilter.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 { diff --git a/forge-gui-mobile/src/forge/toolbox/FList.java b/forge-gui-mobile/src/forge/toolbox/FList.java index bc918d16d70..164ea3457db 100644 --- a/forge-gui-mobile/src/forge/toolbox/FList.java +++ b/forge-gui-mobile/src/forge/toolbox/FList.java @@ -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 extends FScrollPane implements Iterable { public boolean layoutHorizontal() { return false; //this doesn't need to be overridden to specify vertical layouts } + + public AdvancedSearchFilter getAdvancedSearchFilter(ListChooser listChooser) { + return null; //allow overriding to support advanced search + } } public static class DefaultListItemRenderer extends ListItemRenderer { diff --git a/forge-gui-mobile/src/forge/toolbox/ListChooser.java b/forge-gui-mobile/src/forge/toolbox/ListChooser.java index 477a43192a8..2ece2270b30 100644 --- a/forge-gui-mobile/src/forge/toolbox/ListChooser.java +++ b/forge-gui-mobile/src/forge/toolbox/ListChooser.java @@ -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 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 list; private final Function display; private final Callback> callback; + private AdvancedSearchFilter advancedSearchFilter; public ListChooser(final String title, final int minChoices, final int maxChoices, final Collection list0, final Function display0, final Callback> callback0) { FThreads.assertExecutedByEdt(true); @@ -87,26 +99,38 @@ public class ListChooser 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 filteredList = new ArrayList(); - 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 options; @@ -140,6 +164,51 @@ public class ListChooser extends FContainer { }; } + public void resetFilters() { + txtSearch.setText(""); + if (advancedSearchFilter != null) { + advancedSearchFilter.reset(); + ItemFilter.Widget widget = advancedSearchFilter.getWidget(); + if (widget.isVisible()) { + widget.setVisible(false); + revalidate(); + } + } + applyFilters(); + } + + @SuppressWarnings("unchecked") + public void applyFilters() { + lstChoices.clearSelection(); + + List> predicates = new ArrayList>(); + + final String pattern = txtSearch.getText().toLowerCase(); + if (!pattern.isEmpty()) { + predicates.add(new Predicate() { + @Override + public boolean apply(T input) { + return lstChoices.getChoiceText(input).toLowerCase().contains(pattern); + } + }); + } + if (advancedSearchFilter != null && !advancedSearchFilter.isEmpty()) { + predicates.add((Predicate)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 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); }