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 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 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 - - 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. 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) { 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 (choices == null || choices.isEmpty()) {
if (0 == min) { if (min == 0) {
return new ArrayList<T>(); 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>>() { Callable<List<T>> showChoice = new Callable<List<T>>() {
@Override @Override
public List<T> call() { public List<T> call() {
ListChooser<T> c = new ListChooser<T>(message, min, max, choices, display); 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() { list.addListSelectionListener(new ListSelectionListener() {
@Override @Override
public void valueChanged(final ListSelectionEvent ev) { public void valueChanged(final ListSelectionEvent ev) {
@@ -121,7 +120,8 @@ public class GuiChoose {
Card card = (Card) list.getSelectedValue(); Card card = (Card) list.getSelectedValue();
if (card.isFaceDown() && Singletons.getControl().mayShowCard(card)) { if (card.isFaceDown() && Singletons.getControl().mayShowCard(card)) {
CMatchUI.SINGLETON_INSTANCE.setCard(card, true); CMatchUI.SINGLETON_INSTANCE.setCard(card, true);
} else { }
else {
CMatchUI.SINGLETON_INSTANCE.setCard(card); CMatchUI.SINGLETON_INSTANCE.setCard(card);
} }
@@ -134,10 +134,12 @@ public class GuiChoose {
} }
}); });
if(selected != null) if (selected != null) {
c.show(selected); c.show(selected);
else }
else {
c.show(); c.show();
}
GuiUtils.clearPanelSelections(); GuiUtils.clearPanelSelections();
return c.getSelectedValues(); return c.getSelectedValues();

View File

@@ -19,22 +19,17 @@
package forge.gui; package forge.gui;
import java.awt.Component; import java.awt.Component;
import java.awt.event.ActionEvent; import java.awt.event.KeyAdapter;
import java.awt.event.MouseAdapter; import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent; import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener; import java.awt.event.WindowFocusListener;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.AbstractListModel; import javax.swing.AbstractListModel;
import javax.swing.Action;
import javax.swing.DefaultListCellRenderer; import javax.swing.DefaultListCellRenderer;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JList; import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer; import javax.swing.ListCellRenderer;
import javax.swing.ListSelectionModel; import javax.swing.ListSelectionModel;
@@ -46,6 +41,9 @@ import com.google.common.base.Function;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import forge.FThreads; 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 * A simple class that shows a list of choices in a dialog. Two properties
@@ -71,61 +69,70 @@ import forge.FThreads;
* @version $Id$ * @version $Id$
*/ */
public class ListChooser<T> { public class ListChooser<T> {
// Data and number of choices for the list // Data and number of choices for the list
private List<T> list; private List<T> list;
private int minChoices, maxChoices; private int minChoices, maxChoices;
// Decoration
private String title;
// Flag: was the dialog already shown? // Flag: was the dialog already shown?
private boolean called; private boolean called;
// initialized before; listeners may be added to it // initialized before; listeners may be added to it
private JList<T> jList; private FList<T> lstChoices;
// Temporarily stored for event handlers during show private FOptionPane optionPane;
private JDialog dialog;
private JOptionPane optionPane;
private Action ok, cancel;
public ListChooser(final String title, final int minChoices, final int maxChoices, final Collection<T> list, final Function<T, String> display) { public ListChooser(final String title, final int minChoices, final int maxChoices, final Collection<T> list, final Function<T, String> display) {
FThreads.assertExecutedByEdt(true); FThreads.assertExecutedByEdt(true);
this.title = title;
this.minChoices = minChoices; this.minChoices = minChoices;
this.maxChoices = maxChoices; this.maxChoices = maxChoices;
this.list = list.getClass().isInstance(List.class) ? (List<T>)list : Lists.newArrayList(list); this.list = list.getClass().isInstance(List.class) ? (List<T>)list : Lists.newArrayList(list);
this.jList = new JList<T>(new ChooserListModel()); this.lstChoices = new FList<T>(new ChooserListModel());
this.ok = new CloseAction(JOptionPane.OK_OPTION, "OK");
this.ok.setEnabled(minChoices == 0);
this.cancel = new CloseAction(JOptionPane.CANCEL_OPTION, "Cancel");
Object[] options; String[] options;
if (minChoices == 0) { if (minChoices == 0) {
options = new Object[] { new JButton(this.ok), new JButton(this.cancel) }; options = new String[] {"OK","Cancel"};
} else {
options = new Object[] { new JButton(this.ok) };
} }
else {
options = new String[] {"OK"};
}
if (maxChoices == 1) { if (maxChoices == 1) {
this.jList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); this.lstChoices.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
} }
if( null != display ) if (display != null) {
this.jList.setCellRenderer(new TransformedCellRenderer(display)); this.lstChoices.setCellRenderer(new TransformedCellRenderer(display));
}
this.optionPane = new JOptionPane(new JScrollPane(this.jList), JOptionPane.QUESTION_MESSAGE, this.optionPane = new FOptionPane(null, title, null, new JScrollPane(this.lstChoices), options, 0);
JOptionPane.DEFAULT_OPTION, null, options, options[0]); this.optionPane.setButtonEnabled(0, minChoices == 0);
this.jList.getSelectionModel().addListSelectionListener(new SelListener());
this.jList.addMouseListener(new DblListener()); 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. * registering listeners before showing the dialog.
* *
* @return a {@link javax.swing.JList} object. * @return a {@link javax.swing.JList} object.
*/ */
public JList<T> getJList() { public FList<T> getLstChoices() {
return this.jList; return this.lstChoices;
} }
/** @return boolean */ /** @return boolean */
@@ -143,42 +150,45 @@ public class ListChooser<T> {
if (this.called) { if (this.called) {
throw new IllegalStateException("Already shown"); throw new IllegalStateException("Already shown");
} }
Integer value; int result;
do { do {
this.dialog = this.optionPane.createDialog(JOptionPane.getRootFrame(), this.title);
if (this.minChoices != 0) { if (this.minChoices != 0) {
this.dialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); this.optionPane.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
} }
if (list.contains(item)) { if (list.contains(item)) {
jList.setSelectedValue(item, true); lstChoices.setSelectedValue(item, true);
} else { }
jList.setSelectedIndex(0); else {
lstChoices.setSelectedIndex(0);
} }
this.dialog.addWindowFocusListener(new WindowFocusListener() { this.optionPane.addWindowFocusListener(new WindowFocusListener() {
@Override @Override
public void windowGainedFocus(final WindowEvent e) { public void windowGainedFocus(final WindowEvent e) {
ListChooser.this.jList.grabFocus(); ListChooser.this.lstChoices.grabFocus();
} }
@Override @Override
public void windowLostFocus(final WindowEvent e) { public void windowLostFocus(final WindowEvent e) {
} }
}); });
this.dialog.setVisible(true); this.optionPane.setVisible(true);
this.dialog.dispose(); result = this.optionPane.getResult();
value = (Integer) this.optionPane.getValue(); if (result != 0) {
if ((value == null) || (value != JOptionPane.OK_OPTION)) { this.lstChoices.clearSelection();
this.jList.clearSelection(); break;
// can't stop closing by ESC, so repeat if cancelled
} }
} 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 // this assert checks if we really don't return on a cancel if input is
// mandatory // mandatory
assert (this.minChoices == 0) || (value == JOptionPane.OK_OPTION); assert (this.minChoices == 0) || (result == 0);
this.called = true; this.called = true;
return (value != null) && (value == JOptionPane.OK_OPTION); return (result == 0);
} }
/** /**
@@ -191,7 +201,7 @@ public class ListChooser<T> {
if (!this.called) { if (!this.called) {
throw new IllegalStateException("not yet shown"); 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) { if (!this.called) {
throw new IllegalStateException("not yet shown"); 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) { if (!this.called) {
throw new IllegalStateException("not yet shown"); 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) { if (!this.called) {
throw new IllegalStateException("not yet shown"); 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) { if (!this.called) {
throw new IllegalStateException("not yet shown"); 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> * </p>
*/ */
private void commit() { private void commit() {
if (this.ok.isEnabled()) { if (this.optionPane.isButtonEnabled(0)) {
this.optionPane.setValue(JOptionPane.OK_OPTION); optionPane.setResult(0);
} }
} }
private class ChooserListModel extends AbstractListModel<T> { private class ChooserListModel extends AbstractListModel<T> {
private static final long serialVersionUID = 3871965346333840556L; private static final long serialVersionUID = 3871965346333840556L;
@Override @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 { private class SelListener implements ListSelectionListener {
@Override @Override
public void valueChanged(final ListSelectionEvent e) { public void valueChanged(final ListSelectionEvent e) {
final int num = ListChooser.this.jList.getSelectedIndices().length; final int num = ListChooser.this.lstChoices.getSelectedIndices().length;
ListChooser.this.ok ListChooser.this.optionPane.setButtonEnabled(0, (num >= ListChooser.this.minChoices) && (num <= ListChooser.this.maxChoices));
.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();
}
} }
} }
@@ -320,13 +302,9 @@ public class ListChooser<T> {
* @see javax.swing.ListCellRenderer#getListCellRendererComponent(javax.swing.JList, java.lang.Object, int, boolean, boolean) * @see javax.swing.ListCellRenderer#getListCellRendererComponent(javax.swing.JList, java.lang.Object, int, boolean, boolean)
*/ */
@Override @Override
public Component getListCellRendererComponent(JList<? extends T> list, T value, int index, boolean isSelected, public Component getListCellRendererComponent(JList<? extends T> list, T value, int index, boolean isSelected, boolean cellHasFocus) {
boolean cellHasFocus) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
return defRenderer.getListCellRendererComponent(list, transformer.apply(value), index, isSelected, cellHasFocus); 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.Icon;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.SwingConstants; import javax.swing.SwingConstants;
import javax.swing.Timer; import javax.swing.Timer;
import javax.swing.event.AncestorEvent; import javax.swing.event.AncestorEvent;
@@ -431,9 +432,16 @@ public class FLabel extends JLabel implements ILocalRepaint {
public int getAutoSizeWidth() { public int getAutoSizeWidth() {
int width = 0; int width = 0;
if (this.getText() != null && !this.getText().isEmpty()) { 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()); width = metrics.stringWidth(this.getText());
} }
if (this.getIcon() != null) {
width += this.getIcon().getIconWidth() + this.getIconTextGap();
}
if (opaque) { if (opaque) {
width += 6; //account for border/padding 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)); lblItem.setBorder(new EmptyBorder(4, 3, 4, 3));
lblItemSkin.setBackground(FSkin.getColor(hasFocus() ? FSkin.Colors.CLR_ACTIVE : FSkin.Colors.CLR_INACTIVE)); lblItemSkin.setBackground(FSkin.getColor(hasFocus() ? FSkin.Colors.CLR_ACTIVE : FSkin.Colors.CLR_INACTIVE));
lblItemSkin.setForeground(FSkin.getColor(FSkin.Colors.CLR_TEXT)); lblItemSkin.setForeground(FSkin.getColor(FSkin.Colors.CLR_TEXT));
lblItemSkin.setFont(FSkin.getFont(13)); lblItemSkin.setFont(FSkin.getFont(12));
lblItem.setOpaque(isSelected); lblItem.setOpaque(isSelected);
return lblItem; return lblItem;
} }

View File

@@ -117,13 +117,7 @@ public class FOptionPane extends FDialog {
@Override @Override
public void keyPressed(KeyEvent e) { public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) { if (e.getKeyCode() == KeyEvent.VK_ENTER) {
SwingUtilities.invokeLater(new Runnable() { //delay so enter can confirm input choice first optionPane.setResult(0);
@Override
public void run() {
optionPane.result = 0;
optionPane.setVisible(false);
}
});
} }
} }
}); });
@@ -142,8 +136,9 @@ public class FOptionPane extends FDialog {
} }
private int result = -1; //default result to -1, indicating dialog closed without choosing option 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); this.setTitle(title);
int padding = 10; int padding = 10;
@@ -176,7 +171,7 @@ public class FOptionPane extends FDialog {
FontMetrics metrics = JOptionPane.getRootFrame().getGraphics().getFontMetrics(btnMeasure.getFont()); FontMetrics metrics = JOptionPane.getRootFrame().getGraphics().getFontMetrics(btnMeasure.getFont());
int maxTextWidth = 0; int maxTextWidth = 0;
final FButton[] buttons = new FButton[optionCount]; buttons = new FButton[optionCount];
for (int i = 0; i < optionCount; i++) { for (int i = 0; i < optionCount; i++) {
int textWidth = metrics.stringWidth(options[i]); int textWidth = metrics.stringWidth(options[i]);
if (textWidth > maxTextWidth) { 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 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; package forge.view;
import java.awt.Dimension;
import java.awt.Image; import java.awt.Image;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.SpringLayout; import javax.swing.SpringLayout;
import forge.gui.toolbox.FSkin; import forge.gui.toolbox.FSkin;
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class FTitleBar extends FTitleBarBase { public class FTitleBar extends FTitleBarBase {
private static final FSkin.SkinFont skinFont = FSkin.getFont(12);
private final JLabel lblTitle = new JLabel(); private final JLabel lblTitle = new JLabel();
public FTitleBar(ITitleBarOwner owner0) { public FTitleBar(ITitleBarOwner owner0) {
super(owner0); super(owner0);
skin.setMatteBorder(0, 0, 1, 0, bottomEdgeColor); 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 setTitle(owner0.getTitle()); //set default title based on frame title
setIconImage(owner0.getIconImage()); //set default icon image based on frame icon image setIconImage(owner0.getIconImage()); //set default icon image based on frame icon image
FSkin.get(lblTitle).setForeground(foreColor); FSkin.get(lblTitle).setForeground(foreColor);
FSkin.get(lblTitle).setFont(FSkin.getFont(12)); FSkin.get(lblTitle).setFont(skinFont);
addControls(); addControls();
} }
@Override @Override
protected void addControls() { protected void addControls() {
add(lblTitle); add(lblTitle);
@@ -32,8 +38,9 @@ public class FTitleBar extends FTitleBarBase {
@Override @Override
public void setTitle(String title) { public void setTitle(String title) {
this.lblTitle.setText(title); this.lblTitle.setText(title);
updatePreferredSize();
} }
@Override @Override
public void setIconImage(Image image) { public void setIconImage(Image image) {
if (image != null) { if (image != null) {
@@ -42,5 +49,15 @@ public class FTitleBar extends FTitleBarBase {
else { else {
this.lblTitle.setIcon(null); 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));
} }
} }