diff --git a/.gitattributes b/.gitattributes index d25da4f8e91..3f0b46f98db 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1166,6 +1166,7 @@ forge-gui-mobile/src/forge/screens/match/winlose/ViewWinLose.java -text forge-gui-mobile/src/forge/screens/quest/QuestScreen.java -text forge-gui-mobile/src/forge/screens/sealed/SealedScreen.java -text forge-gui-mobile/src/forge/screens/settings/SettingsScreen.java -text +forge-gui-mobile/src/forge/toolbox/DualListBox.java -text forge-gui-mobile/src/forge/toolbox/FButton.java -text forge-gui-mobile/src/forge/toolbox/FCardPanel.java -text forge-gui-mobile/src/forge/toolbox/FCheckBox.java -text diff --git a/forge-gui-mobile/src/forge/toolbox/DualListBox.java b/forge-gui-mobile/src/forge/toolbox/DualListBox.java new file mode 100644 index 00000000000..497471892a8 --- /dev/null +++ b/forge-gui-mobile/src/forge/toolbox/DualListBox.java @@ -0,0 +1,289 @@ +package forge.toolbox; + +import forge.Forge.Graphics; +import forge.assets.FSkinColor; +import forge.assets.FSkinFont; +import forge.toolbox.FEvent.FEventHandler; +import forge.toolbox.FEvent.FEventType; +import forge.util.Callback; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import com.badlogic.gdx.graphics.g2d.BitmapFont.HAlignment; + +// An input box for handling the order of choices. +// Left box has the original choices +// Right box has the final order +// Top string will be like Top of the Stack or Top of the Library +// Bottom string will be like Bottom of the Stack or Bottom of the Library +// Single Arrows in between left box and right box for ordering +// Multi Arrows for moving everything in order +// Up/down arrows on the right of the right box for swapping +// Single ok button, disabled until left box has specified number of items remaining +public class DualListBox extends FDialog { + private final ChoiceList sourceList; + private final ChoiceList destList; + + private final FButton addButton; + private final FButton addAllButton; + private final FButton removeButton; + private final FButton removeAllButton; + private final FButton okButton; + private final FButton autoButton; + + private final FLabel orderedLabel; + private final FLabel selectOrder; + + private final int targetRemainingSourcesMin; + private final int targetRemainingSourcesMax; + + private boolean sideboardingMode = false; + + public DualListBox(String title, int remainingSources, List sourceElements, List destElements, final Callback> callback) { + this(title, remainingSources, remainingSources, sourceElements, destElements, callback); + } + + public DualListBox(String title, int remainingSourcesMin, int remainingSourcesMax, List sourceElements, List destElements, final Callback> callback) { + super(title); + targetRemainingSourcesMin = remainingSourcesMin; + targetRemainingSourcesMax = remainingSourcesMax; + + final FEventHandler onAdd = new FEventHandler() { + @Override + public void handleEvent(FEvent e) { + if (!addButton.isEnabled()) { return; } + + List selected = new ArrayList(); + for (int index : sourceList.selectedIndices) { + selected.add(sourceList.getItemAt(index)); + } + for (T item : selected) { + sourceList.removeItem(item); + destList.addItem(item); + } + setButtonState(); + } + }; + + final FEventHandler onRemove = new FEventHandler() { + @Override + public void handleEvent(FEvent e) { + if (!removeButton.isEnabled()) { return; } + + List selected = new ArrayList(); + for (int index : destList.selectedIndices) { + selected.add(destList.getItemAt(index)); + } + for (T item : selected) { + destList.removeItem(item); + sourceList.addItem(item); + } + setButtonState(); + } + }; + + sourceList = add(new ChoiceList(sourceElements, onAdd)); + destList = add(new ChoiceList(destElements, onRemove)); + + // Dual List control buttons + addButton = add(new FButton(">", onAdd)); + addAllButton = add(new FButton(">>", new FEventHandler() { + @Override + public void handleEvent(FEvent e) { + addAll(); + } + })); + removeButton = add(new FButton("<", onRemove)); + removeAllButton = add(new FButton("<<", new FEventHandler() { + @Override + public void handleEvent(FEvent e) { + removeAll(); + } + })); + + final FEventHandler onAccept = new FEventHandler() { + @Override + public void handleEvent(FEvent e) { + hide(); + callback.run(destList.extractListData()); + } + }; + + // Dual List Complete Buttons + okButton = add(new FButton("OK", onAccept)); + autoButton = add(new FButton("Auto", new FEventHandler() { + @Override + public void handleEvent(FEvent e) { + addAll(); + onAccept.handleEvent(e); + } + })); + + selectOrder = add(new FLabel.Builder().align(HAlignment.CENTER).text("Select Order:").build()); + orderedLabel = add(new FLabel.Builder().align(HAlignment.CENTER).build()); + + setButtonState(); + } + + @Override + protected float layoutAndGetHeight(float width, float maxHeight) { + float x = FOptionPane.PADDING; + float y = FOptionPane.PADDING; + width -= 2 * x; + + float buttonHeight = FOptionPane.BUTTON_HEIGHT; + float labelHeight = selectOrder.getAutoSizeBounds().height; + float listHeight = (maxHeight - 2 * labelHeight - 2 * buttonHeight - 3 * FOptionPane.PADDING - FOptionPane.GAP_BELOW_BUTTONS) / 2; + selectOrder.setBounds(x, y, width, labelHeight); + y += labelHeight; + sourceList.setBounds(x, y, width, listHeight); + y += listHeight + FOptionPane.PADDING; + + float gapBetweenButtons = FOptionPane.PADDING / 2; + float buttonWidth = (width - 3 * gapBetweenButtons) / 4; + float dx = buttonWidth + gapBetweenButtons; + addButton.setBounds(x, y, buttonWidth, buttonHeight); + x += dx; + addAllButton.setBounds(x, y, buttonWidth, buttonHeight); + x += dx; + removeButton.setBounds(x, y, buttonWidth, buttonHeight); + x += dx; + removeAllButton.setBounds(x, y, buttonWidth, buttonHeight); + + x = FList.PADDING; + y += buttonHeight + FOptionPane.PADDING; + orderedLabel.setBounds(x, y, width, labelHeight); + y += labelHeight; + destList.setBounds(x, y, width, listHeight); + y += listHeight + FOptionPane.PADDING; + + buttonWidth = (width - gapBetweenButtons) / 2; + dx = buttonWidth + gapBetweenButtons; + okButton.setBounds(x, y, buttonWidth, buttonHeight); + x += dx; + autoButton.setBounds(x, y, buttonWidth, buttonHeight); + + return maxHeight; + } + + public void setSecondColumnLabelText(String label) { + orderedLabel.setText(label); + } + + public void setSideboardMode(boolean isSideboardMode) { + sideboardingMode = isSideboardMode; + if (sideboardingMode) { + addAllButton.setVisible(false); + removeAllButton.setVisible(false); + autoButton.setEnabled(false); + selectOrder.setText(String.format("Sideboard (%d):", sourceList.getCount())); + orderedLabel.setText(String.format("Main Deck (%d):", destList.getCount())); + } + } + + public List getRemainingSourceList() { + return sourceList.extractListData(); + } + + private void addAll() { + for (T item : sourceList) { + destList.addItem(item); + } + sourceList.clear(); + setButtonState(); + } + + private void removeAll() { + for (T item : destList) { + sourceList.addItem(item); + } + destList.clear(); + setButtonState(); + } + + private void setButtonState() { + if (sideboardingMode) { + removeAllButton.setVisible(false); + addAllButton.setVisible(false); + selectOrder.setText(String.format("Sideboard (%d):", sourceList.getCount())); + orderedLabel.setText(String.format("Main Deck (%d):", destList.getCount())); + } + + boolean anySize = targetRemainingSourcesMax < 0; + boolean canAdd = sourceList.getCount() != 0 && (anySize || targetRemainingSourcesMin <= sourceList.getCount()); + boolean canRemove = destList.getCount() != 0; + boolean targetReached = anySize || targetRemainingSourcesMin <= sourceList.getCount() && targetRemainingSourcesMax >= sourceList.getCount(); + + autoButton.setEnabled(targetRemainingSourcesMax == 0 && !targetReached && !sideboardingMode); + + addButton.setEnabled(canAdd); + addAllButton.setEnabled(canAdd); + removeButton.setEnabled(canRemove); + removeAllButton.setEnabled(canRemove); + okButton.setEnabled(targetReached); + } + + private class ChoiceList extends FList { + private List selectedIndices = new ArrayList(); + + private ChoiceList(Collection items, final FEventHandler dblTapCommand) { + super(items != null ? items : new ArrayList()); //handle null without crashing + + setListItemRenderer(new ListItemRenderer() { + @Override + public float getItemHeight() { + return ListChooser.ITEM_HEIGHT; + } + + @Override + public boolean tap(T value, float x, float y, int count) { + Integer index = ChoiceList.this.getIndexOf(value); + if (selectedIndices.contains(index)) { + selectedIndices.remove(index); + } + else { + selectedIndices.add(index); + } + if (count == 2) { + dblTapCommand.handleEvent(new FEvent(ChoiceList.this, FEventType.ACTIVATE, index)); + } + return true; + } + + @Override + public void drawValue(Graphics g, T value, FSkinFont font, FSkinColor foreColor, boolean pressed, float x, float y, float w, float h) { + g.drawText(value.toString(), font, foreColor, x, y, w, h, false, HAlignment.LEFT, true); + } + }); + setFontSize(12); + } + + @Override + protected void drawBackground(Graphics g) { + g.fillRect(ListChooser.BACK_COLOR, 0, 0, getWidth(), getHeight()); + } + + @Override + public void drawOverlay(Graphics g) { + g.drawRect(1.5f, ListChooser.BORDER_COLOR, 0, 0, getWidth(), getHeight()); + } + + @Override + protected FSkinColor getItemFillColor(int index) { + if (selectedIndices.contains(index)) { + return ListChooser.SEL_COLOR; + } + if (index % 2 == 1) { + return ListChooser.ALT_ITEM_COLOR; + } + return null; + } + + @Override + protected boolean drawLineSeparators() { + return false; + } + } +} diff --git a/forge-gui-mobile/src/forge/toolbox/FList.java b/forge-gui-mobile/src/forge/toolbox/FList.java index 80a7ee17eb2..bb324cfc73c 100644 --- a/forge-gui-mobile/src/forge/toolbox/FList.java +++ b/forge-gui-mobile/src/forge/toolbox/FList.java @@ -1,6 +1,7 @@ package forge.toolbox; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import com.badlogic.gdx.graphics.g2d.BitmapFont.HAlignment; @@ -13,7 +14,7 @@ import forge.assets.FSkinColor.Colors; import forge.screens.FScreen; import forge.util.Utils; -public class FList extends FScrollPane { +public class FList extends FScrollPane implements Iterable { public static final float PADDING = 3; public static final FSkinColor FORE_COLOR = FSkinColor.get(Colors.CLR_TEXT); public static final FSkinColor PRESSED_COLOR = FSkinColor.get(Colors.CLR_ACTIVE).alphaColor(0.9f); @@ -59,6 +60,9 @@ public class FList extends FScrollPane { items.clear(); } + public List extractListData() { + return new ArrayList(items); //create copy to avoid modifying items + } public void setListData(Iterable items0) { clear(); for (E item : items0) { @@ -233,4 +237,9 @@ public class FList extends FScrollPane { g.drawText(value.toString(), font, color, x, y, w, h, false, HAlignment.LEFT, true); } } + + @Override + public Iterator iterator() { + return items.iterator(); + } } diff --git a/forge-gui-mobile/src/forge/toolbox/FOptionPane.java b/forge-gui-mobile/src/forge/toolbox/FOptionPane.java index 7c52bf732a9..c655b62d4f8 100644 --- a/forge-gui-mobile/src/forge/toolbox/FOptionPane.java +++ b/forge-gui-mobile/src/forge/toolbox/FOptionPane.java @@ -15,9 +15,9 @@ public class FOptionPane extends FDialog { public static final FSkinImage WARNING_ICON = FSkinImage.WARNING; public static final FSkinImage ERROR_ICON = FSkinImage.ERROR; - private static float PADDING = 10; - private static final float GAP_BELOW_BUTTONS = PADDING * 0.5f; - private static final float BUTTON_HEIGHT = Utils.AVG_FINGER_HEIGHT * 0.75f; + public static float PADDING = 10; + public static final float GAP_BELOW_BUTTONS = PADDING * 0.5f; + public static final float BUTTON_HEIGHT = Utils.AVG_FINGER_HEIGHT * 0.75f; public static float getMaxDisplayObjHeight() { return Forge.getCurrentScreen().getHeight() - 2 * VPrompt.HEIGHT - FDialog.TITLE_HEIGHT - diff --git a/forge-gui-mobile/src/forge/toolbox/GuiChoose.java b/forge-gui-mobile/src/forge/toolbox/GuiChoose.java index b39886c0c89..7f64aa8e5ca 100644 --- a/forge-gui-mobile/src/forge/toolbox/GuiChoose.java +++ b/forge-gui-mobile/src/forge/toolbox/GuiChoose.java @@ -263,40 +263,10 @@ public class GuiChoose { public static void order(final String title, final String top, final int remainingObjectsMin, final int remainingObjectsMax, final List sourceChoices, final List destChoices, final Card referenceCard, final boolean sideboardingMode, final Callback> callback) { // An input box for handling the order of choices. - - /*Callable> callable = new Callable>() { - @Override - public List call() throws Exception { - DualListBox dual = new DualListBox(remainingObjectsMin, remainingObjectsMax, sourceChoices, destChoices); - dual.setSecondColumnLabelText(top); - - dual.setSideboardMode(sideboardingMode); - - dual.setTitle(title); - dual.pack(); - dual.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); - if (referenceCard != null) { - CMatchUI.SINGLETON_INSTANCE.setCard(referenceCard); - // MARKED FOR UPDATE - } - dual.setVisible(true); - - List objects = dual.getOrderedList(); - - dual.dispose(); - GuiUtils.clearPanelSelections(); - return objects; - } - }; - - FutureTask> ft = new FutureTask>(callable); - FThreads.invokeInEdtAndWait(ft); - try { - return ft.get(); - } catch (Exception e) { // we have waited enough - e.printStackTrace(); - }*/ - callback.run(null); + DualListBox dual = new DualListBox(title, remainingObjectsMin, remainingObjectsMax, sourceChoices, destChoices, callback); + dual.setSecondColumnLabelText(top); + dual.setSideboardMode(sideboardingMode); + dual.show(); } // If comparer is NULL, T has to be comparable. Otherwise you'll get an exception from inside the Arrays.sort() routine diff --git a/forge-gui-mobile/src/forge/toolbox/ListChooser.java b/forge-gui-mobile/src/forge/toolbox/ListChooser.java index 03abfe34757..48382d0e392 100644 --- a/forge-gui-mobile/src/forge/toolbox/ListChooser.java +++ b/forge-gui-mobile/src/forge/toolbox/ListChooser.java @@ -62,11 +62,11 @@ import java.util.List; * @version $Id: ListChooser.java 25183 2014-03-14 23:09:45Z drdev $ */ public class ListChooser extends FContainer { - private static final FSkinColor BACK_COLOR = FSkinColor.get(Colors.CLR_ZEBRA); - private static final FSkinColor ALT_ITEM_COLOR = BACK_COLOR.getContrastColor(-20); - private static final FSkinColor SEL_COLOR = FSkinColor.get(Colors.CLR_ACTIVE); - private static final FSkinColor BORDER_COLOR = FSkinColor.get(Colors.CLR_BORDERS); - private static final float ITEM_HEIGHT = Utils.AVG_FINGER_HEIGHT * 0.75f; + public static final FSkinColor BACK_COLOR = FSkinColor.get(Colors.CLR_ZEBRA); + public static final FSkinColor ALT_ITEM_COLOR = BACK_COLOR.getContrastColor(-20); + public static final FSkinColor SEL_COLOR = FSkinColor.get(Colors.CLR_ACTIVE); + public static final FSkinColor BORDER_COLOR = FSkinColor.get(Colors.CLR_BORDERS); + public static final float ITEM_HEIGHT = Utils.AVG_FINGER_HEIGHT * 0.75f; // Data and number of choices for the list private int minChoices, maxChoices; @@ -220,7 +220,7 @@ public class ListChooser extends FContainer { @Override public boolean tap(T value, float x, float y, int count) { - int index = lstChoices.getIndexOf(value); + Integer index = ChoiceList.this.getIndexOf(value); if (allowMultipleSelections) { if (selectedIndices.contains(index)) { selectedIndices.remove(index);