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

@@ -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));
}
}