Skin ListChooser

This commit is contained in:
drdev
2014-01-04 05:27:28 +00:00
parent 07f94f9d42
commit e954b40d3c
7 changed files with 153 additions and 117 deletions

View File

@@ -14,6 +14,10 @@ Support resetting and hiding filters
Full catalog now available for sideboarding in Deck Editor
Title field and buttons now available on Current Deck pane when on other sections, with the current section name displaying above the table
- More skinned dialogs -
Most remaining dialogs are now skinned, including all message, confirmation, input, and list choice dialogs
- Constructed mode -
Recently a change was made to the constructed mode and this change now allows you to have up to 8 players rather than just two. The game formats that are located in the variants mode also allow up to eight players. The current user interface for this feature is fairly basic and is not very attractive. The settings tab allows you to set the number of opponents. The player tabs includes the same options that you used to see in the constructed mode.

View File

@@ -101,19 +101,18 @@ public class GuiChoose {
}
public static <T> List<T> getChoices(final String message, final int min, final int max, final Collection<T> choices, final T selected, final Function<T, String> display) {
if (null == choices || choices.isEmpty()) {
if (0 == min) {
if (choices == null || choices.isEmpty()) {
if (min == 0) {
return new ArrayList<T>();
} else {
throw new RuntimeException("choice required from empty list");
}
throw new RuntimeException("choice required from empty list");
}
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.getJList();
final JList<T> list = c.getLstChoices();
list.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(final ListSelectionEvent ev) {
@@ -121,7 +120,8 @@ public class GuiChoose {
Card card = (Card) list.getSelectedValue();
if (card.isFaceDown() && Singletons.getControl().mayShowCard(card)) {
CMatchUI.SINGLETON_INSTANCE.setCard(card, true);
} else {
}
else {
CMatchUI.SINGLETON_INSTANCE.setCard(card);
}
@@ -134,10 +134,12 @@ public class GuiChoose {
}
});
if(selected != null)
if (selected != null) {
c.show(selected);
else
}
else {
c.show();
}
GuiUtils.clearPanelSelections();
return c.getSelectedValues();

View File

@@ -19,22 +19,17 @@
package forge.gui;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.util.Collection;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.AbstractListModel;
import javax.swing.Action;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.ListSelectionModel;
@@ -46,6 +41,9 @@ import com.google.common.base.Function;
import com.google.common.collect.Lists;
import forge.FThreads;
import forge.gui.toolbox.FList;
import forge.gui.toolbox.FMouseAdapter;
import forge.gui.toolbox.FOptionPane;
/**
* A simple class that shows a list of choices in a dialog. Two properties
@@ -71,61 +69,70 @@ import forge.FThreads;
* @version $Id$
*/
public class ListChooser<T> {
// Data and number of choices for the list
private List<T> list;
private int minChoices, maxChoices;
// Decoration
private String title;
// Flag: was the dialog already shown?
private boolean called;
// initialized before; listeners may be added to it
private JList<T> jList;
// Temporarily stored for event handlers during show
private JDialog dialog;
private JOptionPane optionPane;
private Action ok, cancel;
private FList<T> lstChoices;
private FOptionPane optionPane;
public ListChooser(final String title, final int minChoices, final int maxChoices, final Collection<T> list, final Function<T, String> display) {
FThreads.assertExecutedByEdt(true);
this.title = title;
this.minChoices = minChoices;
this.maxChoices = maxChoices;
this.list = list.getClass().isInstance(List.class) ? (List<T>)list : Lists.newArrayList(list);
this.jList = new JList<T>(new ChooserListModel());
this.ok = new CloseAction(JOptionPane.OK_OPTION, "OK");
this.ok.setEnabled(minChoices == 0);
this.cancel = new CloseAction(JOptionPane.CANCEL_OPTION, "Cancel");
this.lstChoices = new FList<T>(new ChooserListModel());
Object[] options;
String[] options;
if (minChoices == 0) {
options = new Object[] { new JButton(this.ok), new JButton(this.cancel) };
} else {
options = new Object[] { new JButton(this.ok) };
options = new String[] {"OK","Cancel"};
}
else {
options = new String[] {"OK"};
}
if (maxChoices == 1) {
this.jList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
this.lstChoices.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
}
if( null != display )
this.jList.setCellRenderer(new TransformedCellRenderer(display));
if (display != null) {
this.lstChoices.setCellRenderer(new TransformedCellRenderer(display));
}
this.optionPane = new JOptionPane(new JScrollPane(this.jList), JOptionPane.QUESTION_MESSAGE,
JOptionPane.DEFAULT_OPTION, null, options, options[0]);
this.jList.getSelectionModel().addListSelectionListener(new SelListener());
this.jList.addMouseListener(new DblListener());
this.optionPane = new FOptionPane(null, title, null, new JScrollPane(this.lstChoices), options, 0);
this.optionPane.setButtonEnabled(0, minChoices == 0);
this.lstChoices.getSelectionModel().addListSelectionListener(new SelListener());
this.lstChoices.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
ListChooser.this.commit();
}
}
});
this.lstChoices.addMouseListener(new FMouseAdapter() {
@Override
public void onLeftClick(MouseEvent e) {
if (e.getClickCount() == 2) {
ListChooser.this.commit();
}
}
});
}
/**
* Returns the JList used in the list chooser. this is useful for
* Returns the FList used in the list chooser. this is useful for
* registering listeners before showing the dialog.
*
* @return a {@link javax.swing.JList} object.
*/
public JList<T> getJList() {
return this.jList;
public FList<T> getLstChoices() {
return this.lstChoices;
}
/** @return boolean */
@@ -143,42 +150,45 @@ public class ListChooser<T> {
if (this.called) {
throw new IllegalStateException("Already shown");
}
Integer value;
int result;
do {
this.dialog = this.optionPane.createDialog(JOptionPane.getRootFrame(), this.title);
if (this.minChoices != 0) {
this.dialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
this.optionPane.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
}
if (list.contains(item)) {
jList.setSelectedValue(item, true);
} else {
jList.setSelectedIndex(0);
lstChoices.setSelectedValue(item, true);
}
else {
lstChoices.setSelectedIndex(0);
}
this.dialog.addWindowFocusListener(new WindowFocusListener() {
this.optionPane.addWindowFocusListener(new WindowFocusListener() {
@Override
public void windowGainedFocus(final WindowEvent e) {
ListChooser.this.jList.grabFocus();
ListChooser.this.lstChoices.grabFocus();
}
@Override
public void windowLostFocus(final WindowEvent e) {
}
});
this.dialog.setVisible(true);
this.dialog.dispose();
value = (Integer) this.optionPane.getValue();
if ((value == null) || (value != JOptionPane.OK_OPTION)) {
this.jList.clearSelection();
// can't stop closing by ESC, so repeat if cancelled
this.optionPane.setVisible(true);
result = this.optionPane.getResult();
if (result != 0) {
this.lstChoices.clearSelection();
break;
}
} while ((this.minChoices != 0) && (value != JOptionPane.OK_OPTION));
// can't stop closing by ESC, so repeat if cancelled
} while (this.minChoices != 0);
this.optionPane.dispose();
// this assert checks if we really don't return on a cancel if input is
// mandatory
assert (this.minChoices == 0) || (value == JOptionPane.OK_OPTION);
assert (this.minChoices == 0) || (result == 0);
this.called = true;
return (value != null) && (value == JOptionPane.OK_OPTION);
return (result == 0);
}
/**
@@ -191,7 +201,7 @@ public class ListChooser<T> {
if (!this.called) {
throw new IllegalStateException("not yet shown");
}
return (Integer) this.optionPane.getValue() == JOptionPane.OK_OPTION;
return (this.optionPane.getResult() == 0);
}
/**
@@ -203,7 +213,7 @@ public class ListChooser<T> {
if (!this.called) {
throw new IllegalStateException("not yet shown");
}
return this.jList.getSelectedIndices();
return this.lstChoices.getSelectedIndices();
}
/**
@@ -216,7 +226,7 @@ public class ListChooser<T> {
if (!this.called) {
throw new IllegalStateException("not yet shown");
}
return this.jList.getSelectedValuesList();
return this.lstChoices.getSelectedValuesList();
}
/**
@@ -228,7 +238,7 @@ public class ListChooser<T> {
if (!this.called) {
throw new IllegalStateException("not yet shown");
}
return this.jList.getSelectedIndex();
return this.lstChoices.getSelectedIndex();
}
/**
@@ -240,7 +250,7 @@ public class ListChooser<T> {
if (!this.called) {
throw new IllegalStateException("not yet shown");
}
return (T) this.jList.getSelectedValue();
return (T) this.lstChoices.getSelectedValue();
}
/**
@@ -249,13 +259,12 @@ public class ListChooser<T> {
* </p>
*/
private void commit() {
if (this.ok.isEnabled()) {
this.optionPane.setValue(JOptionPane.OK_OPTION);
if (this.optionPane.isButtonEnabled(0)) {
optionPane.setResult(0);
}
}
private class ChooserListModel extends AbstractListModel<T> {
private static final long serialVersionUID = 3871965346333840556L;
@Override
@@ -269,38 +278,11 @@ public class ListChooser<T> {
}
}
private class CloseAction extends AbstractAction {
private static final long serialVersionUID = -8426767786083886936L;
private final int value;
public CloseAction(final int value, final String label) {
super(label);
this.value = value;
}
@Override
public void actionPerformed(final ActionEvent e) {
ListChooser.this.optionPane.setValue(this.value);
}
}
private class SelListener implements ListSelectionListener {
@Override
public void valueChanged(final ListSelectionEvent e) {
final int num = ListChooser.this.jList.getSelectedIndices().length;
ListChooser.this.ok
.setEnabled((num >= ListChooser.this.minChoices) && (num <= ListChooser.this.maxChoices));
}
}
private class DblListener extends MouseAdapter {
@Override
public void mouseClicked(final MouseEvent e) {
if (e.getClickCount() == 2) {
ListChooser.this.commit();
}
final int num = ListChooser.this.lstChoices.getSelectedIndices().length;
ListChooser.this.optionPane.setButtonEnabled(0, (num >= ListChooser.this.minChoices) && (num <= ListChooser.this.maxChoices));
}
}
@@ -320,13 +302,9 @@ public class ListChooser<T> {
* @see javax.swing.ListCellRenderer#getListCellRendererComponent(javax.swing.JList, java.lang.Object, int, boolean, boolean)
*/
@Override
public Component getListCellRendererComponent(JList<? extends T> list, T value, int index, boolean isSelected,
boolean cellHasFocus) {
public Component getListCellRendererComponent(JList<? extends T> list, T value, int index, boolean isSelected, boolean cellHasFocus) {
// TODO Auto-generated method stub
return defRenderer.getListCellRendererComponent(list, transformer.apply(value), index, isSelected, cellHasFocus);
}
}
}

View File

@@ -22,6 +22,7 @@ import java.awt.event.MouseEvent;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.SwingConstants;
import javax.swing.Timer;
import javax.swing.event.AncestorEvent;
@@ -431,9 +432,16 @@ public class FLabel extends JLabel implements ILocalRepaint {
public int getAutoSizeWidth() {
int width = 0;
if (this.getText() != null && !this.getText().isEmpty()) {
FontMetrics metrics = this.getGraphics().getFontMetrics(this.getFont());
Graphics g = this.getGraphics();
if (g == null) {
g = JOptionPane.getRootFrame().getGraphics(); //fallback to root frame's graphics if needed
}
FontMetrics metrics = g.getFontMetrics(this.getFont());
width = metrics.stringWidth(this.getText());
}
if (this.getIcon() != null) {
width += this.getIcon().getIconWidth() + this.getIconTextGap();
}
if (opaque) {
width += 6; //account for border/padding if opaque
}

View File

@@ -65,7 +65,7 @@ public class FList<E> extends JList<E> {
lblItem.setBorder(new EmptyBorder(4, 3, 4, 3));
lblItemSkin.setBackground(FSkin.getColor(hasFocus() ? FSkin.Colors.CLR_ACTIVE : FSkin.Colors.CLR_INACTIVE));
lblItemSkin.setForeground(FSkin.getColor(FSkin.Colors.CLR_TEXT));
lblItemSkin.setFont(FSkin.getFont(13));
lblItemSkin.setFont(FSkin.getFont(12));
lblItem.setOpaque(isSelected);
return lblItem;
}

View File

@@ -117,13 +117,7 @@ public class FOptionPane extends FDialog {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
SwingUtilities.invokeLater(new Runnable() { //delay so enter can confirm input choice first
@Override
public void run() {
optionPane.result = 0;
optionPane.setVisible(false);
}
});
optionPane.setResult(0);
}
}
});
@@ -142,8 +136,9 @@ public class FOptionPane extends FDialog {
}
private int result = -1; //default result to -1, indicating dialog closed without choosing option
private final FButton[] buttons;
private FOptionPane(String message, String title, SkinImage icon, Component comp, String[] options, int defaultOption) {
public FOptionPane(String message, String title, SkinImage icon, Component comp, String[] options, int defaultOption) {
this.setTitle(title);
int padding = 10;
@@ -176,7 +171,7 @@ public class FOptionPane extends FDialog {
FontMetrics metrics = JOptionPane.getRootFrame().getGraphics().getFontMetrics(btnMeasure.getFont());
int maxTextWidth = 0;
final FButton[] buttons = new FButton[optionCount];
buttons = new FButton[optionCount];
for (int i = 0; i < optionCount; i++) {
int textWidth = metrics.stringWidth(options[i]);
if (textWidth > maxTextWidth) {
@@ -252,4 +247,36 @@ public class FOptionPane extends FDialog {
this.setSize(width, this.getHeight() + buttonHeight); //resize dialog again to account for buttons
}
@Override
public void setVisible(boolean visible) {
if (this.isVisible() == visible) { return; }
if (visible) {
result = -1; //default result to -1 when shown, indicating dialog closed without choosing option
}
super.setVisible(visible);
}
public int getResult() {
return result;
}
public void setResult(int result0) {
this.result = result0;
SwingUtilities.invokeLater(new Runnable() { //delay hiding so action can finish first
@Override
public void run() {
setVisible(false);
}
});
}
public boolean isButtonEnabled(int index) {
return buttons[index].isEnabled();
}
public void setButtonEnabled(int index, boolean enabled) {
buttons[index].setEnabled(enabled);
}
}

View File

@@ -1,15 +1,21 @@
package forge.view;
import java.awt.Dimension;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.SpringLayout;
import forge.gui.toolbox.FSkin;
@SuppressWarnings("serial")
public class FTitleBar extends FTitleBarBase {
private static final FSkin.SkinFont skinFont = FSkin.getFont(12);
private final JLabel lblTitle = new JLabel();
public FTitleBar(ITitleBarOwner owner0) {
super(owner0);
skin.setMatteBorder(0, 0, 1, 0, bottomEdgeColor);
@@ -17,10 +23,10 @@ public class FTitleBar extends FTitleBarBase {
setTitle(owner0.getTitle()); //set default title based on frame title
setIconImage(owner0.getIconImage()); //set default icon image based on frame icon image
FSkin.get(lblTitle).setForeground(foreColor);
FSkin.get(lblTitle).setFont(FSkin.getFont(12));
FSkin.get(lblTitle).setFont(skinFont);
addControls();
}
@Override
protected void addControls() {
add(lblTitle);
@@ -32,8 +38,9 @@ public class FTitleBar extends FTitleBarBase {
@Override
public void setTitle(String title) {
this.lblTitle.setText(title);
updatePreferredSize();
}
@Override
public void setIconImage(Image image) {
if (image != null) {
@@ -42,5 +49,15 @@ public class FTitleBar extends FTitleBarBase {
else {
this.lblTitle.setIcon(null);
}
updatePreferredSize();
}
private void updatePreferredSize() {
int width = skinFont.measureTextWidth(JOptionPane.getRootFrame().getGraphics(), this.lblTitle.getText());
if (this.lblTitle.getIcon() != null) {
width += this.lblTitle.getIcon().getIconWidth() + this.lblTitle.getIconTextGap();
}
width += btnClose.getPreferredSize().width;
this.setPreferredSize(new Dimension(width + 10, visibleHeight));
}
}