Merge pull request #8742 from Jetz72/fixes20250918

Refactor "Unique Cards Only" Setting for Mobile Editor
This commit is contained in:
Jetz72
2025-11-05 09:27:38 -06:00
committed by GitHub
5 changed files with 111 additions and 18 deletions

View File

@@ -85,7 +85,7 @@ public class AdventureDeckEditor extends FDeckEditor {
}
@Override
public ItemPool<PaperCard> getCardPool(boolean wantUnique) {
public ItemPool<PaperCard> getCardPool() {
ItemPool<PaperCard> pool = new ItemPool<>(PaperCard.class);
pool.addAll(Current.player().getCards());
return pool;
@@ -163,7 +163,7 @@ public class AdventureDeckEditor extends FDeckEditor {
}
@Override
public ItemPool<PaperCard> getCardPool(boolean wantUnique) {
public ItemPool<PaperCard> getCardPool() {
return deckToPreview.getAllCardsInASinglePool(true, true);
}

View File

@@ -66,8 +66,8 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
return gameType == null ? null : gameType.getDeckFormat();
}
public ItemPool<PaperCard> getCardPool(boolean wantUnique) {
return wantUnique ? FModel.getUniqueCardsNoAlt() : FModel.getAllCardsNoAlt();
public ItemPool<PaperCard> getCardPool() {
return FModel.getAllCardsNoAlt();
}
protected Predicate<PaperCard> getCardFilter() { return null; }
@@ -172,10 +172,10 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
@Override public boolean hasCommander() { return deckFormat.hasCommander(); }
@Override
public ItemPool<PaperCard> getCardPool(boolean wantUnique) {
public ItemPool<PaperCard> getCardPool() {
if(this.itemPoolSupplier != null)
return itemPoolSupplier.get();
return super.getCardPool(wantUnique);
return super.getCardPool();
}
@Override
@@ -1713,7 +1713,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
//Clone the pool to ensure we don't mutate it by adding to or removing from this page.
//Can override this if that behavior is desired.
ItemPool<PaperCard> cardPool = CardPool.createFrom(parentScreen.getEditorConfig().getCardPool(cardManager.getWantUnique()), PaperCard.class);
ItemPool<PaperCard> cardPool = CardPool.createFrom(parentScreen.getEditorConfig().getCardPool(), PaperCard.class);
if(editorConfig.usePlayerInventory() && currentDeck != null) {
//Remove any items from the pool that are in the deck.
@@ -1886,8 +1886,8 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
menu.addItem(new FCheckBoxMenuItem(Forge.getLocalizer().getMessage("lblUniqueCardsOnly"), cardManager.getWantUnique(), e -> {
boolean wantUnique = !cardManager.getWantUnique();
cardManager.setWantUnique(wantUnique);
refresh();
cardManager.getConfig().setUniqueCardsOnly(wantUnique);
cardManager.refresh();
}));
}
}

View File

@@ -76,7 +76,7 @@ public class FDeckImportDialog extends FDialog {
boolean replacingDeck = !currentDeck.isEmpty() || usingInventory;
this.currentDeck = currentDeck;
this.editorConfig = editorConfig;
ItemPool<PaperCard> cardPool = editorConfig.getCardPool(false);
ItemPool<PaperCard> cardPool = editorConfig.getCardPool();
controller = new DeckImportController(dateTimeCheck, monthDropdown, yearDropdown, replacingDeck);
String contents = Forge.getClipboard().getContents();
if (contents == null)

View File

@@ -1,10 +1,18 @@
package forge.itemmanager;
import java.util.*;
import java.util.Map.Entry;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimaps;
import forge.Graphics;
import forge.StaticData;
import forge.assets.FSkinColor;
import forge.assets.FSkinFont;
import forge.card.CardEdition;
import forge.card.CardRenderer;
import forge.card.CardZoom;
import forge.item.PaperCard;
@@ -60,6 +68,77 @@ public class CardManager extends ItemManager<PaperCard> {
return new AdvancedSearchFilter<>(itemManager);
}
@Override
protected Iterable<Entry<PaperCard, Integer>> getUnique(Iterable<Entry<PaperCard, Integer>> items) {
//TO-maybe-DO: Share logic between this and identical method in desktop.
ListMultimap<String, Entry<PaperCard, Integer>> entriesByName = Multimaps.newListMultimap(
new TreeMap<>(String.CASE_INSENSITIVE_ORDER), Lists::newArrayList);
for (Entry<PaperCard, Integer> item : items) {
final String cardName = item.getKey().getName();
entriesByName.put(cardName, item);
}
// Now we're ready to go on with retrieving cards to be returned
Map<PaperCard, Integer> cardsMap = new HashMap<>();
for (String cardName : entriesByName.keySet()) {
List<Entry<PaperCard, Integer>> entries = entriesByName.get(cardName);
ListMultimap<CardEdition, Entry<PaperCard, Integer>> entriesByEdition = Multimaps.newListMultimap(new HashMap<>(), Lists::newArrayList);
for (Entry<PaperCard, Integer> entry : entries) {
CardEdition ed = StaticData.instance().getCardEdition(entry.getKey().getEdition());
if (ed != null)
entriesByEdition.put(ed, entry);
}
if (entriesByEdition.isEmpty())
continue; // skip card
// Try to retain only those editions accepted by the current Card Art Preference Policy
Predicate<CardEdition> editionPredicate = ed -> StaticData.instance().getCardArtPreference().accept(ed);
List<CardEdition> acceptedEditions = entriesByEdition.keySet().stream().filter(editionPredicate).collect(Collectors.toList());
// If policy too strict, fall back to getting all editions.
if (acceptedEditions.isEmpty())
// Policy is too strict for current PaperCard in Entry. Remove any filter
acceptedEditions.addAll(entriesByEdition.keySet());
Entry<PaperCard, Integer> cardEntry = getCardEntryToAdd(entriesByEdition, acceptedEditions);
if (cardEntry != null)
cardsMap.put(cardEntry.getKey(), cardEntry.getValue());
}
return cardsMap.entrySet();
}
// Select the Card Art Entry to add, based on current Card Art Preference Order.
// This method will prefer the entry currently having an image. If that's not the case,
private Entry<PaperCard, Integer> getCardEntryToAdd(ListMultimap<CardEdition, Entry<PaperCard, Integer>> entriesByEdition,
List<CardEdition> acceptedEditions) {
// Use standard sort + index, for better performance!
Collections.sort(acceptedEditions);
if (StaticData.instance().cardArtPreferenceIsLatest())
Collections.reverse(acceptedEditions);
Iterator<CardEdition> editionIterator = acceptedEditions.iterator();
Entry<PaperCard, Integer> candidateEntry = null;
Entry<PaperCard, Integer> firstCandidateEntryFound = null;
while (editionIterator.hasNext() && candidateEntry == null){
CardEdition cardEdition = editionIterator.next();
// These are now the entries to add to Cards Map
List<Entry<PaperCard, Integer>> cardEntries = entriesByEdition.get(cardEdition);
Iterator<Entry<PaperCard, Integer>> entriesIterator = cardEntries.iterator();
candidateEntry = entriesIterator.hasNext() ? entriesIterator.next() : null;
if (candidateEntry != null && firstCandidateEntryFound == null)
firstCandidateEntryFound = candidateEntry; // save reference to the first candidate entry found!
while ((candidateEntry == null || !candidateEntry.getKey().hasImage()) && entriesIterator.hasNext()) {
candidateEntry = entriesIterator.next();
if (firstCandidateEntryFound == null)
firstCandidateEntryFound = candidateEntry;
}
if (candidateEntry != null && !candidateEntry.getKey().hasImage())
candidateEntry = null; // resetting for next edition
}
return candidateEntry != null ? candidateEntry : firstCandidateEntryFound;
}
@Override
public ItemRenderer getListItemRenderer(final CompactModeHandler compactModeHandler) {
return new CardListItemRenderer(compactModeHandler);

View File

@@ -728,6 +728,10 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
protected abstract AdvancedSearchFilter<? extends T> createAdvancedSearchFilter();
protected Iterable<Entry<T, Integer>> getUnique(final Iterable<Entry<T, Integer>> items) {
return Aggregates.uniqueByLast(items, from -> from.getKey().getName());
}
public void addFilter(final ItemFilter<? extends T> filter) {
filters.get().add(filter);
add(filter.getWidget());
@@ -908,19 +912,29 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
}
public void updateView(final boolean forceFilter, final Iterable<T> itemsToSelect) {
//TO-maybe-DO: Share logic between this and identical method in desktop.
final boolean useFilter = (forceFilter && (filterPredicate != null)) || !isUnfiltered();
if (useFilter || forceFilter) {
model.clear();
Iterable<Entry<T, Integer>> items = pool;
if (useFilter) {
Predicate<Entry<T, Integer>> pred = x -> x != null && filterPredicate.test(x.getKey());
items = IterableUtil.filter(pool, pred);
}
model.addItems(items);
if (useFilter || this.wantUnique || forceFilter) {
this.model.clear();
}
if (useFilter && this.wantUnique) {
final Predicate<Entry<T, Integer>> filterForPool = x -> this.filterPredicate.test(x.getKey());
final Iterable<Entry<T, Integer>> items = getUnique(IterableUtil.filter(this.pool, filterForPool));
this.model.addItems(items);
}
else if (useFilter) {
final Predicate<Entry<T, Integer>> pred = x -> this.filterPredicate.test(x.getKey());
this.model.addItems(IterableUtil.filter(this.pool, pred));
}
else if (this.wantUnique) {
final Iterable<Entry<T, Integer>> items = getUnique(this.pool);
this.model.addItems(items);
}
else if (forceFilter) {
this.model.addItems(this.pool);
}
currentView.refresh(itemsToSelect, getSelectedIndex(), forceFilter ? 0 : currentView.getScrollValue());
//update ratio of # in filtered pool / # in total pool