Implement ListChooser

This commit is contained in:
drdev
2014-04-15 03:51:19 +00:00
parent 2afc9d1694
commit 8f95c98a3d
7 changed files with 255 additions and 86 deletions

1
.gitattributes vendored
View File

@@ -1182,6 +1182,7 @@ forge-gui-mobile/src/forge/toolbox/FTextField.java -text
forge-gui-mobile/src/forge/toolbox/FToggleSwitch.java -text forge-gui-mobile/src/forge/toolbox/FToggleSwitch.java -text
forge-gui-mobile/src/forge/toolbox/GuiChoose.java -text forge-gui-mobile/src/forge/toolbox/GuiChoose.java -text
forge-gui-mobile/src/forge/toolbox/GuiDialog.java -text forge-gui-mobile/src/forge/toolbox/GuiDialog.java -text
forge-gui-mobile/src/forge/toolbox/ListChooser.java -text
forge-gui-mobile/src/forge/util/LayoutHelper.java -text forge-gui-mobile/src/forge/util/LayoutHelper.java -text
forge-gui-mobile/src/forge/util/PhysicsObject.java -text forge-gui-mobile/src/forge/util/PhysicsObject.java -text
forge-gui-mobile/src/forge/util/Utils.java -text forge-gui-mobile/src/forge/util/Utils.java -text

View File

@@ -11,7 +11,6 @@ import forge.assets.FSkinFont;
import forge.assets.FSkinColor.Colors; import forge.assets.FSkinColor.Colors;
import forge.toolbox.FOptionPane; import forge.toolbox.FOptionPane;
import forge.toolbox.FScrollPane; import forge.toolbox.FScrollPane;
import forge.util.Callback;
public class BugReportDialog extends FOptionPane { public class BugReportDialog extends FOptionPane {
private static boolean dialogShown; private static boolean dialogShown;
@@ -34,7 +33,7 @@ public class BugReportDialog extends FOptionPane {
} }
@Override @Override
protected void onButtonClick(final int option, final Callback<Integer> callback) { public void setResult(final int option) {
switch (option) { switch (option) {
case 0: case 0:
BugReporter.copyAndGoToForums(text); BugReporter.copyAndGoToForums(text);

View File

@@ -120,8 +120,9 @@ public class FOptionPane extends FDialog {
private final FTextArea prompt; private final FTextArea prompt;
private final FDisplayObject displayObj; private final FDisplayObject displayObj;
private final FButton[] buttons; private final FButton[] buttons;
private final Callback<Integer> callback;
public FOptionPane(String message, String title, FSkinImage icon, FDisplayObject displayObj0, String[] options, int defaultOption, final Callback<Integer> callback) { public FOptionPane(String message, String title, FSkinImage icon, FDisplayObject displayObj0, String[] options, int defaultOption, final Callback<Integer> callback0) {
super(title); super(title);
if (icon != null) { if (icon != null) {
@@ -144,6 +145,8 @@ public class FOptionPane extends FDialog {
add(displayObj); add(displayObj);
} }
callback = callback0;
int optionCount = options.length; int optionCount = options.length;
buttons = new FButton[optionCount]; buttons = new FButton[optionCount];
for (int i = 0; i < optionCount; i++) { for (int i = 0; i < optionCount; i++) {
@@ -151,13 +154,13 @@ public class FOptionPane extends FDialog {
buttons[i] = add(new FButton(options[i], new FEventHandler() { buttons[i] = add(new FButton(options[i], new FEventHandler() {
@Override @Override
public void handleEvent(FEvent e) { public void handleEvent(FEvent e) {
onButtonClick(option, callback); setResult(option);
} }
})); }));
} }
} }
protected void onButtonClick(final int option, final Callback<Integer> callback) { public void setResult(final int option) {
hide(); hide();
if (callback != null) { if (callback != null) {
callback.run(option); callback.run(option);
@@ -205,8 +208,12 @@ public class FOptionPane extends FDialog {
promptHeight = prompt.getHeight(); promptHeight = prompt.getHeight();
} }
} }
x = PADDING; x = PADDING;
y += promptHeight + gapBottom; if (promptHeight > 0) {
y += promptHeight + gapBottom;
}
if (displayObj != null) { if (displayObj != null) {
displayObj.setBounds(x, y, width - 2 * x, displayObj.getHeight()); displayObj.setBounds(x, y, width - 2 * x, displayObj.getHeight());
y += displayObj.getHeight() + gapBottom; y += displayObj.getHeight() + gapBottom;

View File

@@ -10,10 +10,7 @@ import org.apache.commons.lang3.StringUtils;
import java.util.*; import java.util.*;
/**
* TODO: Write javadoc for this type.
*
*/
public class GuiChoose { public class GuiChoose {
/** /**
@@ -239,65 +236,8 @@ public class GuiChoose {
throw new RuntimeException("choice required from empty list"); throw new RuntimeException("choice required from empty list");
} }
//TODO: Remove this temporary code and uncomment below ListChooser<T> c = new ListChooser<T>(message, min, max, choices, display, callback);
int resultCount = min; c.show(selected);
if (resultCount == 0 && max > 0) {
resultCount = 1;
}
List<T> result = new ArrayList<T>();
for (T choice : choices) {
result.add(choice);
if (result.size() == resultCount) {
break;
}
}
callback.run(result);
/*Callable<List<T>> showChoice = new Callable<List<T>>() {
@Override
public List<T> call() {
ListChooser<T> c = new ListChooser<T>(message, min, max, choices, display);
final JList<T> list = c.getLstChoices();
list.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(final ListSelectionEvent ev) {
if (list.getSelectedValue() instanceof Card) {
Card card = (Card) list.getSelectedValue();
if (card.isFaceDown() && FControl.mayShowCard(card)) {
CMatchUI.SINGLETON_INSTANCE.setCard(card, true);
}
else {
CMatchUI.SINGLETON_INSTANCE.setCard(card);
}
GuiUtils.clearPanelSelections();
GuiUtils.setPanelSelection(card);
}
if (list.getSelectedValue() instanceof InventoryItem) {
CMatchUI.SINGLETON_INSTANCE.setCard((InventoryItem) list.getSelectedValue());
}
}
});
if (selected != null) {
c.show(selected);
}
else {
c.show();
}
GuiUtils.clearPanelSelections();
return c.getSelectedValues();
}
};
FutureTask<List<T>> future = new FutureTask<List<T>>(showChoice);
FThreads.invokeInEdtAndWait(future);
try {
return future.get();
} catch (Exception e) { // should be no exception here
e.printStackTrace();
}*/
} }
public static <T> void many(final String title, final String topCaption, int cnt, final List<T> sourceChoices, Card referenceCard, final Callback<List<T>> callback) { public static <T> void many(final String title, final String topCaption, int cnt, final List<T> sourceChoices, Card referenceCard, final Callback<List<T>> callback) {

View File

@@ -24,18 +24,13 @@ public class GuiDialog {
} }
public static void confirm(final Card c, final String question, final boolean defaultIsYes, final String[] options, final Callback<Boolean> callback) { public static void confirm(final Card c, final String question, final boolean defaultIsYes, final String[] options, final Callback<Boolean> callback) {
FThreads.invokeInEdtAndWait(new Runnable() { final String title = c == null ? "Question" : c.getName() + " - Ability";
String questionToUse = StringUtils.isBlank(question) ? "Activate card's ability?" : question;
String[] opts = options == null ? defaultConfirmOptions : options;
FOptionPane.showOptionDialog(questionToUse, title, FOptionPane.QUESTION_ICON, opts, defaultIsYes ? 0 : 1, new Callback<Integer>() {
@Override @Override
public void run() { public void run(Integer result) {
final String title = c == null ? "Question" : c.getName() + " - Ability"; callback.run(result == 0);
String questionToUse = StringUtils.isBlank(question) ? "Activate card's ability?" : question;
String[] opts = options == null ? defaultConfirmOptions : options;
FOptionPane.showOptionDialog(questionToUse, title, FOptionPane.QUESTION_ICON, opts, defaultIsYes ? 0 : 1, new Callback<Integer>() {
@Override
public void run(Integer result) {
callback.run(result == 0);
}
});
} }
}); });
} }

View File

@@ -0,0 +1,228 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.toolbox;
import com.badlogic.gdx.graphics.g2d.BitmapFont.HAlignment;
import com.google.common.base.Function;
import forge.FThreads;
import forge.Forge.Graphics;
import forge.assets.FSkinColor;
import forge.assets.FSkinFont;
import forge.assets.FSkinColor.Colors;
import forge.toolbox.FList;
import forge.toolbox.FOptionPane;
import forge.util.Callback;
import forge.util.Utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* A simple class that shows a list of choices in a dialog. Two properties
* influence the behavior of a list chooser: minSelection and maxSelection.
* These two give the allowed number of selected items for the dialog to be
* closed. A negative value for minSelection suggests that the list is revealed
* and the choice doesn't matter.
* <ul>
* <li>If minSelection is 0, there will be a Cancel button.</li>
* <li>If minSelection is -1, 0 or 1, double-clicking a choice will also close the
* dialog.</li>
* <li>If the number of selections is out of bounds, the "OK" button is
* disabled.</li>
* <li>The dialog was "committed" if "OK" was clicked or a choice was double
* clicked.</li>
* <li>The dialog was "canceled" if "Cancel" or "X" was clicked.</li>
* <li>If the dialog was canceled, the selection will be empty.</li>
* <li>
* </ul>
*
* @param <T>
* the generic type
* @author Forge
* @version $Id: ListChooser.java 25183 2014-03-14 23:09:45Z drdev $
*/
public class ListChooser<T> {
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.8f;
// Data and number of choices for the list
private int minChoices, maxChoices;
// Flag: was the dialog already shown?
private boolean called;
// initialized before; listeners may be added to it
private ChoiceList lstChoices;
private FOptionPane optionPane;
private final Callback<List<T>> callback;
public ListChooser(final String title, final int minChoices0, final int maxChoices0, final Collection<T> list, final Function<T, String> display, final Callback<List<T>> callback0) {
FThreads.assertExecutedByEdt(true);
minChoices = minChoices0;
maxChoices = maxChoices0;
lstChoices = new ChoiceList(list);
callback = callback0;
String[] options;
if (minChoices == 0) {
options = new String[] {"OK","Cancel"};
}
else {
options = new String[] {"OK"};
}
if (maxChoices == 1 || minChoices == -1) {
lstChoices.allowMultipleSelections = false;
}
else {
lstChoices.allowMultipleSelections = true;
}
lstChoices.setHeight(ITEM_HEIGHT * Math.min(list.size(), 8)); //make tall enough to show 8 items without scrolling
optionPane = new FOptionPane(null, title, null, lstChoices, options, minChoices < 0 ? 0 : -1, new Callback<Integer>() {
@Override
public void run(Integer result) {
called = false;
if (result == 0) {
List<T> choices = new ArrayList<T>();
for (int i : lstChoices.selectedIndices) {
choices.add(lstChoices.getItemValueAt(i));
}
callback.run(choices);
}
else if (minChoices > 0) {
show(); //show if user tries to cancel when input is mandatory
}
else {
callback.run(new ArrayList<T>());
}
}
});
}
public void show() {
show(null);
}
/**
* Shows the dialog and returns after the dialog was closed.
*
* @param index0 index to select when shown
* @return a boolean.
*/
public void show(final T item) {
if (called) {
throw new IllegalStateException("Already shown");
}
called = true;
lstChoices.selectedIndices.clear();
if (item == null) {
if (maxChoices > 0) {
lstChoices.selectedIndices.add(0);
}
}
else {
lstChoices.selectedIndices.add(lstChoices.getIndexOf(item));
}
onSelectionChange();
optionPane.show();
}
private void onSelectionChange() {
final int num = lstChoices.selectedIndices.size();
optionPane.setButtonEnabled(0, (num >= minChoices) && (num <= maxChoices || maxChoices == -1));
}
public final class ChoiceList extends FList<T> {
private boolean allowMultipleSelections;
private List<Integer> selectedIndices = new ArrayList<Integer>();
private ChoiceList(Collection<T> items) {
super(items);
setListItemRenderer(new ListItemRenderer<T>() {
@Override
public float getItemHeight() {
return ITEM_HEIGHT;
}
@Override
public boolean tap(T value, float x, float y, int count) {
int index = lstChoices.getIndexOf(value);
if (allowMultipleSelections) {
if (selectedIndices.contains(index)) {
selectedIndices.remove(index);
}
else {
selectedIndices.add(index);
}
}
else {
selectedIndices.clear();
selectedIndices.add(index);
}
onSelectionChange();
if (count == 2 && optionPane.isButtonEnabled(0)) {
optionPane.setResult(0);
}
return true;
}
@Override
public void drawValue(Graphics g, T value, FSkinFont font, FSkinColor foreColor, float width, float height) {
float x = width * INSETS_FACTOR;
g.drawText(value.toString(), font, foreColor, x, 0, width - 2 * x, height, false, HAlignment.LEFT, true);
}
});
setFontSize(12);
}
@Override
protected void drawBackground(Graphics g) {
g.fillRect(BACK_COLOR, 0, 0, getWidth(), getHeight());
}
@Override
public void drawOverlay(Graphics g) {
g.drawRect(1.5f, BORDER_COLOR, 0, 0, getWidth(), getHeight());
}
@Override
protected FSkinColor getItemFillColor(ListItem item) {
int index = Math.round(item.getTop() / ITEM_HEIGHT); //more efficient indexing strategy
if (selectedIndices.contains(index)) {
return SEL_COLOR;
}
if (index % 2 == 1) {
return ALT_ITEM_COLOR;
}
return null;
}
@Override
protected boolean drawLineSeparators() {
return false;
}
}
}

View File

@@ -34,7 +34,7 @@ public class SGuiChoose {
} }
final List<T> choice = SGuiChoose.getChoices(message, 0, 1, choices); final List<T> choice = SGuiChoose.getChoices(message, 0, 1, choices);
return choice.isEmpty() ? null : choice.get(0); return choice.isEmpty() ? null : choice.get(0);
} // getChoiceOptional(String,T...) }
public static <T> T oneOrNone(final String message, final Collection<T> choices) { public static <T> T oneOrNone(final String message, final Collection<T> choices) {
if ((choices == null) || choices.isEmpty()) { if ((choices == null) || choices.isEmpty()) {
@@ -42,7 +42,7 @@ public class SGuiChoose {
} }
final List<T> choice = SGuiChoose.getChoices(message, 0, 1, choices); final List<T> choice = SGuiChoose.getChoices(message, 0, 1, choices);
return choice.isEmpty() ? null : choice.get(0); return choice.isEmpty() ? null : choice.get(0);
} // getChoiceOptional(String,T...) }
// returned Object will never be null // returned Object will never be null
/** /**
@@ -208,7 +208,7 @@ public class SGuiChoose {
} }
final List<T> choice = SGuiChoose.sortedGetChoices(message, 0, 1, choices, comparer); final List<T> choice = SGuiChoose.sortedGetChoices(message, 0, 1, choices, comparer);
return choice.isEmpty() ? null : choice.get(0); return choice.isEmpty() ? null : choice.get(0);
} // getChoiceOptional(String,T...) }
// If comparer is NULL, T has to be comparable. Otherwise you'll get an exception from inside the Arrays.sort() routine // If comparer is NULL, T has to be comparable. Otherwise you'll get an exception from inside the Arrays.sort() routine
public static <T> T sortedOneOrNone(final String message, final List<T> choices, Comparator<T> comparer) { public static <T> T sortedOneOrNone(final String message, final List<T> choices, Comparator<T> comparer) {
@@ -217,15 +217,14 @@ public class SGuiChoose {
} }
final List<T> choice = SGuiChoose.sortedGetChoices(message, 0, 1, choices, comparer); final List<T> choice = SGuiChoose.sortedGetChoices(message, 0, 1, choices, comparer);
return choice.isEmpty() ? null : choice.get(0); return choice.isEmpty() ? null : choice.get(0);
} // getChoiceOptional(String,T...) }
// If comparer is NULL, T has to be comparable. Otherwise you'll get an exception from inside the Arrays.sort() routine // If comparer is NULL, T has to be comparable. Otherwise you'll get an exception from inside the Arrays.sort() routine
public static <T> T sortedOne(final String message, final T[] choices, Comparator<T> comparer) { public static <T> T sortedOne(final String message, final T[] choices, Comparator<T> comparer) {
final List<T> choice = SGuiChoose.sortedGetChoices(message, 1, 1, choices, comparer); final List<T> choice = SGuiChoose.sortedGetChoices(message, 1, 1, choices, comparer);
assert choice.size() == 1; assert choice.size() == 1;
return choice.get(0); return choice.get(0);
} // getChoice() }
// If comparer is NULL, T has to be comparable. Otherwise you'll get an exception from inside the Arrays.sort() routine // If comparer is NULL, T has to be comparable. Otherwise you'll get an exception from inside the Arrays.sort() routine
public static <T> T sortedOne(final String message, final List<T> choices, Comparator<T> comparer) { public static <T> T sortedOne(final String message, final List<T> choices, Comparator<T> comparer) {