diff options
Diffstat (limited to 'base/src/main/java/bjc/utils/gui')
22 files changed, 1869 insertions, 0 deletions
diff --git a/base/src/main/java/bjc/utils/gui/ExtensionFileFilter.java b/base/src/main/java/bjc/utils/gui/ExtensionFileFilter.java new file mode 100644 index 0000000..7c487eb --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/ExtensionFileFilter.java @@ -0,0 +1,56 @@ +package bjc.utils.gui; + +import java.io.File; +import java.util.List; + +import javax.swing.filechooser.FileFilter; + +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; + +/** + * A file filter based on extensions. + * + * Built for Swing. + * + * @author ben + * + */ +public class ExtensionFileFilter extends FileFilter { + /** + * The list holding all filtered extensions + */ + private final IList<String> extensions; + + /** + * Create a new filter only showing files with the specified extensions. + * + * @param exts + * The extensions to show in this filter. + */ + public ExtensionFileFilter(final List<String> exts) { + extensions = new FunctionalList<>(exts); + } + + /** + * Create a new filter only showing files with the specified extensions. + * + * @param exts + * The extensions to show in this filter. + */ + public ExtensionFileFilter(final String... exts) { + extensions = new FunctionalList<>(exts); + } + + @Override + public boolean accept(final File pathname) { + if (pathname == null) throw new NullPointerException("Pathname must not be null"); + + return extensions.anyMatch(pathname.getName()::endsWith); + } + + @Override + public String getDescription() { + return extensions.toString(); + } +}
\ No newline at end of file diff --git a/base/src/main/java/bjc/utils/gui/SimpleDialogs.java b/base/src/main/java/bjc/utils/gui/SimpleDialogs.java new file mode 100644 index 0000000..59eb1c3 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/SimpleDialogs.java @@ -0,0 +1,269 @@ +package bjc.utils.gui; + +import java.awt.Component; +import java.awt.Frame; +import java.util.function.Function; +import java.util.function.Predicate; + +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; + +import bjc.utils.gui.layout.VLayout; + +/** + * Utility class for getting simple input from the user. + * + * @author ben + * + */ +public class SimpleDialogs { + /** + * Get a bounded integer from the user. + * + * @param parent + * The parent component for the dialogs. + * @param title + * The title for the dialogs. + * @param prompt + * The prompt to tell the user what to enter. + * @param lowerBound + * The lower integer bound to accept. + * @param upperBound + * The upper integer bound to accept. + * @return A int within the specified bounds. + */ + public static int getBoundedInt(final Component parent, final String title, final String prompt, + final int lowerBound, final int upperBound) { + return getValue(parent, title, prompt, (strang) -> { + try { + final int value = Integer.parseInt(strang); + + return value < upperBound && value > lowerBound; + } catch (final NumberFormatException nfex) { + // We don't care about the specifics of the + // exception, just + // that this value isn't good + return false; + } + }, Integer::parseInt); + } + + /** + * Asks the user to pick an option from a series of choices. + * + * @param <E> + * The type of choices for the user to pick + * + * @param parent + * The parent frame for this dialog + * @param title + * The title of this dialog + * @param question + * The question being asked + * @param choices + * The available choices for the question + * @return The choice the user picked, or null if they didn't pick one + */ + @SuppressWarnings("unchecked") + public static <E> E getChoice(final Frame parent, final String title, final String question, + final E... choices) { + if (parent == null) + throw new NullPointerException("Parent must not be null"); + else if (title == null) + throw new NullPointerException("Title must not be null"); + else if (question == null) throw new NullPointerException("Question must not be null"); + + final JDialog chooser = new JDialog(parent, title, true); + chooser.setLayout(new VLayout(2)); + + final JPanel questionPane = new JPanel(); + + final JLabel questionText = new JLabel(question); + final JComboBox<E> questionChoices = new JComboBox<>(choices); + + questionPane.add(questionText); + questionPane.add(questionChoices); + + final JPanel buttonPane = new JPanel(); + + final JButton okButton = new JButton("Ok"); + final JButton cancelButton = new JButton("Cancel"); + + okButton.addActionListener((event) -> chooser.dispose()); + cancelButton.addActionListener((event) -> chooser.dispose()); + + buttonPane.add(cancelButton); + buttonPane.add(okButton); + + chooser.add(questionPane); + chooser.add(buttonPane); + + chooser.pack(); + chooser.setVisible(true); + + return (E) questionChoices.getSelectedItem(); + } + + /** + * Get a integer from the user + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for dialogs. + * @param prompt + * The prompt to tell the user what to enter. + * @return A int. + */ + public static int getInt(final Component parent, final String title, final String prompt) { + return getValue(parent, title, prompt, strang -> { + try { + Integer.parseInt(strang); + return true; + } catch (final NumberFormatException nfex) { + // We don't care about this exception, just mark + // the value + // as not good + return false; + } + }, Integer::parseInt); + } + + /** + * Get a string from the user + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for the dialogs. + * @param prompt + * The prompt to tell the user what to enter. + * @return A string. + */ + public static String getString(final Component parent, final String title, final String prompt) { + if (parent == null) + throw new NullPointerException("Parent must not be null"); + else if (title == null) + throw new NullPointerException("Title must not be null"); + else if (prompt == null) throw new NullPointerException("Prompt must not be null"); + + return JOptionPane.showInputDialog(parent, prompt, title, JOptionPane.QUESTION_MESSAGE); + } + + /** + * Get a value parsable from a string from the user. + * + * @param <E> + * The type of the value parsed from the string + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for dialogs. + * @param prompt + * The prompt to tell the user what to enter. + * @param validator + * A predicate to determine if a input is valid. + * @param transformer + * The function to transform the string into a value. + * @return The value parsed from a string. + */ + public static <E> E getValue(final Component parent, final String title, final String prompt, + final Predicate<String> validator, final Function<String, E> transformer) { + if (validator == null) + throw new NullPointerException("Validator must not be null"); + else if (transformer == null) throw new NullPointerException("Transformer must not be null"); + + String input = getString(parent, title, prompt); + + while (!validator.test(input)) { + showError(parent, "I/O Error", "Please enter a valid value"); + + input = getString(parent, title, prompt); + } + + return transformer.apply(input); + } + + /** + * Get a whole number from the user. + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for dialogs. + * @param prompt + * The prompt to tell the user what to enter. + * @return A whole number. + */ + public static int getWhole(final Component parent, final String title, final String prompt) { + return getBoundedInt(parent, title, prompt, 0, Integer.MAX_VALUE); + } + + /** + * Ask the user a Yes/No question. + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for dialogs. + * @param question + * The question to ask the user. + * @return True if the user said yes, false otherwise. + */ + public static boolean getYesNo(final Component parent, final String title, final String question) { + if (parent == null) + throw new NullPointerException("Parent must not be null"); + else if (title == null) + throw new NullPointerException("Title must not be null"); + else if (question == null) throw new NullPointerException("Question must not be null"); + + final int result = JOptionPane.showConfirmDialog(parent, question, title, JOptionPane.YES_NO_OPTION); + + return result == JOptionPane.YES_OPTION ? true : false; + } + + /** + * Show a error message to the user + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for dialogs. + * @param message + * The error to show the user. + */ + public static void showError(final Component parent, final String title, final String message) { + if (parent == null) + throw new NullPointerException("Parent must not be null"); + else if (title == null) + throw new NullPointerException("Title must not be null"); + else if (message == null) throw new NullPointerException("Error message must not be null"); + + JOptionPane.showMessageDialog(parent, message, title, JOptionPane.ERROR_MESSAGE); + } + + /** + * Show an informative message to the user + * + * @param parent + * The parent for this dialog + * @param title + * Show the title for this dialog + * @param message + * Show the message for this dialog + */ + public static void showMessage(final Component parent, final String title, final String message) { + if (parent == null) + throw new NullPointerException("Parent must not be null"); + else if (title == null) + throw new NullPointerException("Title must not be null"); + else if (message == null) throw new NullPointerException("Message must not be null"); + + JOptionPane.showMessageDialog(parent, title, message, JOptionPane.INFORMATION_MESSAGE); + } +} diff --git a/base/src/main/java/bjc/utils/gui/SimpleFileChooser.java b/base/src/main/java/bjc/utils/gui/SimpleFileChooser.java new file mode 100644 index 0000000..7da0bd8 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/SimpleFileChooser.java @@ -0,0 +1,198 @@ +package bjc.utils.gui; + +import java.awt.Component; +import java.io.File; + +import javax.swing.JFileChooser; + +import bjc.utils.exceptions.FileNotChosenException; + +/** + * Utility class for easily prompting user for files. + * + * Built for Swing. + * + * @author ben + * + */ +public class SimpleFileChooser { + private static File doOpenFile(final Component parent, final String title, final JFileChooser files) { + if (title == null) throw new NullPointerException("Title must not be null"); + + files.setDialogTitle(title); + + boolean success = false; + + while (!success) { + try { + maybeDoOpenFile(parent, files); + + success = true; + } catch (final FileNotChosenException fncx) { + // We don't care about specifics + SimpleDialogs.showError(parent, "I/O Error", "Please pick a file to open"); + } + } + + return files.getSelectedFile(); + } + + private static File doSaveFile(final Component parent, final String title, final JFileChooser files) { + if (title == null) throw new NullPointerException("Title must not be null"); + + files.setDialogTitle(title); + + final boolean success = false; + + while (!success) { + try { + maybeDoSaveFile(parent, files); + + return files.getSelectedFile(); + } catch (final FileNotChosenException fncex) { + // We don't care about specifics + SimpleDialogs.showError(parent, "I/O Error", "Please pick a file to save to"); + } + } + } + + /** + * Prompt the user with a "Open File..." dialog. Keeps prompting them + * until they pick a file. + * + * @param parent + * The component to use as the parent for the dialog. + * @param title + * The title of the dialog to prompt with. + * @return The file the user has chosen. + */ + public static File getOpenFile(final Component parent, final String title) { + final JFileChooser files = new JFileChooser(); + + return doOpenFile(parent, title, files); + } + + /** + * Prompt the user with a "Open File..." dialog. Keeps prompting them + * until they pick a file. + * + * @param parent + * The component to use as the parent for the dialog. + * @param title + * The title of the dialog to prompt with. + * @param extensions + * The list of file extensions the file should have. + * @return The file the user has chosen. + */ + public static File getOpenFile(final Component parent, final String title, final String... extensions) { + final JFileChooser files = new JFileChooser(); + + files.addChoosableFileFilter(new ExtensionFileFilter(extensions)); + + return doOpenFile(parent, title, files); + } + + /** + * Prompt the user with a "Save File..." dialog. + * + * @param parent + * The component to use as the parent for the dialog. + * @param title + * The title of the dialog to prompt with. + * @return The file the user chose. + */ + public static File getSaveFile(final Component parent, final String title) { + final JFileChooser files = new JFileChooser(); + + return doSaveFile(parent, title, files); + } + + /** + * Prompt the user with a "Save File..." dialog. + * + * @param parent + * The component to use as the parent for the dialog. + * @param title + * The title of the dialog to prompt with. + * @param extensions + * The extensions of the files the user can choose. + * @return The file the user chose. + */ + public static File getSaveFile(final Component parent, final String title, final String... extensions) { + final JFileChooser files = new JFileChooser(); + + files.addChoosableFileFilter(new ExtensionFileFilter(extensions)); + + return doSaveFile(parent, title, files); + } + + private static void maybeDoOpenFile(final Component parent, final JFileChooser files) + throws FileNotChosenException { + if (parent == null) + throw new NullPointerException("Parent must not be null"); + else if (files == null) throw new NullPointerException("File chooser must not be null"); + + final int result = files.showSaveDialog(parent); + + if (result != JFileChooser.APPROVE_OPTION) throw new FileNotChosenException(); + } + + private static void maybeDoSaveFile(final Component parent, final JFileChooser files) + throws FileNotChosenException { + if (parent == null) + throw new NullPointerException("Parent must not be null"); + else if (files == null) throw new NullPointerException("File chooser must not be null"); + + final int result = files.showSaveDialog(parent); + + if (result != JFileChooser.APPROVE_OPTION) throw new FileNotChosenException(); + } + + /** + * Prompt the user with a "Open File..." dialog. + * + * @param parent + * The component to use as the parent for the dialog. + * @param title + * The title of the dialog to prompt with. + * @return The file if the user chose one or null if they didn't. + */ + public static File maybeOpenFile(final Component parent, final String title) { + if (title == null) throw new NullPointerException("Title must not be null"); + + final JFileChooser files = new JFileChooser(); + files.setDialogTitle(title); + + try { + maybeDoOpenFile(parent, files); + } catch (final FileNotChosenException fncex) { + // We don't care about specifics + } + + return files.getSelectedFile(); + } + + /** + * Prompt the user with a "Save File..." dialog. + * + * @param parent + * The component to use as the parent for the dialog. + * @param title + * The title of the dialog to prompt with. + * @return The file if the user chose one or null if they didn't. + */ + public static File maybeSaveFile(final Component parent, final String title) { + if (title == null) throw new NullPointerException("Title must not be null"); + + final JFileChooser files = new JFileChooser(); + files.setDialogTitle(title); + + try { + maybeDoSaveFile(parent, files); + } catch (final FileNotChosenException fncex) { + // We don't care about specifics + } + + return files.getSelectedFile(); + } +} diff --git a/base/src/main/java/bjc/utils/gui/SimpleInternalDialogs.java b/base/src/main/java/bjc/utils/gui/SimpleInternalDialogs.java new file mode 100644 index 0000000..5237557 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/SimpleInternalDialogs.java @@ -0,0 +1,208 @@ +package bjc.utils.gui; + +import java.awt.Component; +import java.util.function.Function; +import java.util.function.Predicate; + +import javax.swing.JOptionPane; + +/** + * Utility class for getting simple input from the user. + * + * Modified to work with JDesktopPanes + * + * @author ben + * + */ +public class SimpleInternalDialogs { + /** + * Get a bounded integer from the user. + * + * @param parent + * The parent component for the dialogs. + * @param title + * The title for the dialogs. + * @param prompt + * The prompt to tell the user what to enter. + * @param lowerBound + * The lower integer bound to accept. + * @param upperBound + * The upper integer bound to accept. + * @return A int within the specified bounds. + */ + public static int getBoundedInt(final Component parent, final String title, final String prompt, + final int lowerBound, final int upperBound) { + return getValue(parent, title, prompt, (strang) -> { + try { + final int value = Integer.parseInt(strang); + + return value < upperBound && value > lowerBound; + } catch (final NumberFormatException nfex) { + // We don't care about the specifics of the + // exception, just + // that this value isn't good + return false; + } + }, Integer::parseInt); + } + + /** + * Get a integer from the user + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for dialogs. + * @param prompt + * The prompt to tell the user what to enter. + * @return A int. + */ + public static int getInt(final Component parent, final String title, final String prompt) { + return getValue(parent, title, prompt, strang -> { + try { + Integer.parseInt(strang); + return true; + } catch (final NumberFormatException nfex) { + // We don't care about this exception, just mark + // the value + // as not good + return false; + } + }, Integer::parseInt); + } + + /** + * Get a string from the user + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for the dialogs. + * @param prompt + * The prompt to tell the user what to enter. + * @return A string. + */ + public static String getString(final Component parent, final String title, final String prompt) { + if (parent == null) + throw new NullPointerException("Parent must not be null"); + else if (title == null) + throw new NullPointerException("Title must not be null"); + else if (prompt == null) throw new NullPointerException("Prompt must not be null"); + + return JOptionPane.showInternalInputDialog(parent, prompt, title, JOptionPane.QUESTION_MESSAGE); + } + + /** + * Get a value parsable from a string from the user. + * + * @param <E> + * The type of the value parsed from the string + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for dialogs. + * @param prompt + * The prompt to tell the user what to enter. + * @param validator + * A predicate to determine if a input is valid. + * @param transformer + * The function to transform the string into a value. + * @return The value parsed from a string. + */ + public static <E> E getValue(final Component parent, final String title, final String prompt, + final Predicate<String> validator, final Function<String, E> transformer) { + if (validator == null) + throw new NullPointerException("Validator must not be null"); + else if (transformer == null) throw new NullPointerException("Transformer must not be null"); + + String strang = getString(parent, title, prompt); + + while (!validator.test(strang)) { + showError(parent, "I/O Error", "Please enter a valid value"); + + strang = getString(parent, title, prompt); + } + + return transformer.apply(strang); + } + + /** + * Get a whole number from the user. + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for dialogs. + * @param prompt + * The prompt to tell the user what to enter. + * @return A whole number. + */ + public static int getWhole(final Component parent, final String title, final String prompt) { + return getBoundedInt(parent, title, prompt, 0, Integer.MAX_VALUE); + } + + /** + * Ask the user a Yes/No question. + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for dialogs. + * @param question + * The question to ask the user. + * @return True if the user said yes, false otherwise. + */ + public static boolean getYesNo(final Component parent, final String title, final String question) { + if (parent == null) + throw new NullPointerException("Parent must not be null"); + else if (title == null) + throw new NullPointerException("Title must not be null"); + else if (question == null) throw new NullPointerException("Question must not be null"); + + final int result = JOptionPane.showInternalConfirmDialog(parent, question, title, + JOptionPane.YES_NO_OPTION); + + return result == JOptionPane.YES_OPTION ? true : false; + } + + /** + * Show a error message to the user + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for dialogs. + * @param message + * The error to show the user. + */ + public static void showError(final Component parent, final String title, final String message) { + if (parent == null) + throw new NullPointerException("Parent must not be null"); + else if (title == null) + throw new NullPointerException("Title must not be null"); + else if (message == null) throw new NullPointerException("Error message must not be null"); + + JOptionPane.showInternalMessageDialog(parent, message, title, JOptionPane.ERROR_MESSAGE); + } + + /** + * Show an informative message to the user + * + * @param parent + * The parent for this dialog + * @param title + * Show the title for this dialog + * @param message + * Show the message for this dialog + */ + public static void showMessage(final Component parent, final String title, final String message) { + if (parent == null) + throw new NullPointerException("Parent must not be null"); + else if (title == null) + throw new NullPointerException("Title must not be null"); + else if (message == null) throw new NullPointerException("Message must not be null"); + + JOptionPane.showInternalMessageDialog(parent, title, message, JOptionPane.INFORMATION_MESSAGE); + } +} diff --git a/base/src/main/java/bjc/utils/gui/SimpleInternalFrame.java b/base/src/main/java/bjc/utils/gui/SimpleInternalFrame.java new file mode 100644 index 0000000..afb498e --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/SimpleInternalFrame.java @@ -0,0 +1,40 @@ +package bjc.utils.gui; + +import javax.swing.JInternalFrame; + +/** + * A simple internal frame class + * + * @author ben + * + */ +public class SimpleInternalFrame extends JInternalFrame { + private static final long serialVersionUID = -2966801321260716617L; + + /** + * Create a new blank internal frame + */ + public SimpleInternalFrame() { + super(); + } + + /** + * Create a new blank internal frame with a specific title + * + * @param title + * The title of the internal frame + */ + public SimpleInternalFrame(final String title) { + super(title); + } + + protected void setupFrame() { + setSize(320, 240); + + setResizable(true); + + setClosable(true); + setMaximizable(true); + setIconifiable(true); + } +}
\ No newline at end of file diff --git a/base/src/main/java/bjc/utils/gui/SimpleJList.java b/base/src/main/java/bjc/utils/gui/SimpleJList.java new file mode 100644 index 0000000..411d0db --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/SimpleJList.java @@ -0,0 +1,49 @@ +package bjc.utils.gui; + +import javax.swing.DefaultListModel; +import javax.swing.JList; +import javax.swing.ListModel; + +/** + * Utility class for making JLists and their models. + * + * @author ben + * + */ +public class SimpleJList { + /** + * Create a new JList from a given list. + * + * @param <E> + * The type of data in the JList + * + * @param source + * The list to populate the JList with. + * @return A JList populated with the elements from ls. + */ + public static <E> JList<E> buildFromList(final Iterable<E> source) { + if (source == null) throw new NullPointerException("Source must not be null"); + + return new JList<>(buildModel(source)); + } + + /** + * Create a new list model from a given list. + * + * @param <E> + * The type of data in the list model + * + * @param source + * The list to fill the list model from. + * @return A list model populated with the elements from ls. + */ + public static <E> ListModel<E> buildModel(final Iterable<E> source) { + if (source == null) throw new NullPointerException("Source must not be null"); + + final DefaultListModel<E> defaultModel = new DefaultListModel<>(); + + source.forEach(defaultModel::addElement); + + return defaultModel; + } +} diff --git a/base/src/main/java/bjc/utils/gui/SimpleTitledBorder.java b/base/src/main/java/bjc/utils/gui/SimpleTitledBorder.java new file mode 100644 index 0000000..9b01507 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/SimpleTitledBorder.java @@ -0,0 +1,25 @@ +package bjc.utils.gui; + +import javax.swing.border.EtchedBorder; +import javax.swing.border.TitledBorder; + +/** + * A simple border with a title attached to it. + * + * @author ben + * + */ +public class SimpleTitledBorder extends TitledBorder { + // Version ID for serialization + private static final long serialVersionUID = -5655969079949148487L; + + /** + * Create a new border with the specified title. + * + * @param title + * The title for the border. + */ + public SimpleTitledBorder(final String title) { + super(new EtchedBorder(), title); + } +} diff --git a/base/src/main/java/bjc/utils/gui/TextAreaOutputStream.java b/base/src/main/java/bjc/utils/gui/TextAreaOutputStream.java new file mode 100644 index 0000000..fbc58ed --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/TextAreaOutputStream.java @@ -0,0 +1,35 @@ +package bjc.utils.gui; + +import java.io.IOException; +import java.io.OutputStream; + +import javax.swing.JTextArea; + +/** + * An output stream that prints to a JTextArea + * + * @author epr + * @author Levente S\u00e1ntha (lsantha@users.sourceforge.net) + */ +public class TextAreaOutputStream extends OutputStream { + private final JTextArea textArea; + + /** + * Create a new output stream attached to a textarea + * + * @param console + * The textarea to write to + */ + public TextAreaOutputStream(final JTextArea console) { + this.textArea = console; + } + + @Override + public void write(final int b) throws IOException { + textArea.append("" + (char) b); + + if (b == '\n') { + textArea.repaint(); + } + } +} diff --git a/base/src/main/java/bjc/utils/gui/awt/ExtensionFileFilter.java b/base/src/main/java/bjc/utils/gui/awt/ExtensionFileFilter.java new file mode 100644 index 0000000..eb60ae2 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/awt/ExtensionFileFilter.java @@ -0,0 +1,50 @@ +package bjc.utils.gui.awt; + +import java.io.File; +import java.io.FilenameFilter; +import java.util.List; + +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; + +/** + * Filter a set of filenames by extension. + * + * Built for AWT + * + * @author ben + * + */ +public class ExtensionFileFilter implements FilenameFilter { + /** + * The list of extensions to filter + */ + private final IList<String> extensions; + + /** + * Create a new filter only showing files with the specified extensions. + * + * @param exts + * The extensions to show in this filter. + */ + public ExtensionFileFilter(final List<String> exts) { + if (exts == null) throw new NullPointerException("Extensions must not be null"); + + extensions = new FunctionalList<>(exts); + } + + /** + * Create a new filter only showing files with the specified extensions. + * + * @param exts + * The extensions to show in this filter. + */ + public ExtensionFileFilter(final String... exts) { + extensions = new FunctionalList<>(exts); + } + + @Override + public boolean accept(final File directory, final String name) { + return extensions.anyMatch(name::endsWith); + } +}
\ No newline at end of file diff --git a/base/src/main/java/bjc/utils/gui/awt/SimpleFileDialog.java b/base/src/main/java/bjc/utils/gui/awt/SimpleFileDialog.java new file mode 100644 index 0000000..77a4a59 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/awt/SimpleFileDialog.java @@ -0,0 +1,144 @@ +package bjc.utils.gui.awt; + +import java.awt.FileDialog; +import java.awt.Frame; +import java.io.File; +import java.io.FilenameFilter; + +import bjc.utils.gui.SimpleDialogs; + +/** + * A simple way to get the user to pick a file + * + * Built for AWT. + * + * @author ben + * + */ +public class SimpleFileDialog { + /** + * Prompt the user to pick a file to open + * + * @param parent + * The parent of the file picker + * @param title + * The title of the file picker + * @return The file the user picked + */ + public static File getOpenFile(final Frame parent, final String title) { + return getOpenFile(parent, title, (String[]) null); + } + + /** + * Prompt the user to pick a file to open + * + * @param parent + * The parent of the file picker + * @param title + * The title of the file picker + * @param extensions + * The extensions to accept as valid + * @return The file the user picked + */ + public static File getOpenFile(final Frame parent, final String title, final String... extensions) { + if (parent == null) + throw new NullPointerException("Parent must not be null"); + else if (title == null) throw new NullPointerException("Title must not be null"); + + final FileDialog chooser = new FileDialog(parent, title, FileDialog.LOAD); + + if (extensions != null) { + final FilenameFilter filter = new ExtensionFileFilter(extensions); + chooser.setFilenameFilter(filter); + } + + chooser.setVisible(true); + + while (chooser.getFile() == null) { + SimpleDialogs.showError(parent, "File I/O Error", "Please choose a file to open."); + chooser.setVisible(true); + } + + return chooser.getFiles()[0]; + } + + /** + * Prompt the user to pick a file to open + * + * @param parent + * The parent of the file picker + * @param title + * The title of the file picker + * @param extensions + * The extensions to accept as valid + * @return The file the user picked + */ + public static File[] getOpenFiles(final Frame parent, final String title, final String... extensions) { + if (parent == null) + throw new NullPointerException("Parent must not be null"); + else if (title == null) throw new NullPointerException("Title must not be null"); + + final FileDialog chooser = new FileDialog(parent, title, FileDialog.LOAD); + + if (extensions != null) { + final FilenameFilter filter = new ExtensionFileFilter(extensions); + chooser.setFilenameFilter(filter); + } + + chooser.setMultipleMode(true); + chooser.setVisible(true); + + while (chooser.getFile() == null) { + SimpleDialogs.showError(parent, "File I/O Error", "Please choose a file to open."); + chooser.setVisible(true); + } + + return chooser.getFiles(); + } + + /** + * Prompt the user to pick a file to save + * + * @param parent + * The parent of the file picker + * @param title + * The title of the file picker + * @return The file the user picked + */ + public static File getSaveFile(final Frame parent, final String title) { + return getSaveFile(parent, title, (String[]) null); + } + + /** + * Prompt the user to pick a file to save + * + * @param parent + * The parent of the file picker + * @param title + * The title of the file picker + * @param extensions + * The extensions to accept as valid + * @return The file the user picked + */ + public static File getSaveFile(final Frame parent, final String title, final String... extensions) { + if (parent == null) + throw new NullPointerException("Parent must not be null"); + else if (title == null) throw new NullPointerException("Title must not be null"); + + final FileDialog chooser = new FileDialog(parent, title, FileDialog.SAVE); + + if (extensions != null) { + final FilenameFilter filter = new ExtensionFileFilter(extensions); + chooser.setFilenameFilter(filter); + } + + chooser.setVisible(true); + + while (chooser.getFile() == null) { + SimpleDialogs.showError(parent, "File I/O Error", "Please choose a file to save to."); + chooser.setVisible(true); + } + + return chooser.getFiles()[0]; + } +} diff --git a/base/src/main/java/bjc/utils/gui/layout/AutosizeLayout.java b/base/src/main/java/bjc/utils/gui/layout/AutosizeLayout.java new file mode 100644 index 0000000..6f384f2 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/layout/AutosizeLayout.java @@ -0,0 +1,22 @@ +package bjc.utils.gui.layout; + +import java.awt.GridLayout; + +/** + * A layout that simply holds one component that it auto-resizes whenever it is + * resized. + * + * @author ben + * + */ +public class AutosizeLayout extends GridLayout { + // Version id for serialization + private static final long serialVersionUID = -2495693595953396924L; + + /** + * Create a new auto-size layout. + */ + public AutosizeLayout() { + super(1, 1); + } +} diff --git a/base/src/main/java/bjc/utils/gui/layout/HLayout.java b/base/src/main/java/bjc/utils/gui/layout/HLayout.java new file mode 100644 index 0000000..4ed1661 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/layout/HLayout.java @@ -0,0 +1,25 @@ +package bjc.utils.gui.layout; + +import java.awt.GridLayout; + +/** + * A layout manager that lays out its components horizontally, evenly sizing + * them. + * + * @author ben + * + */ +public class HLayout extends GridLayout { + // Version ID for serialization + private static final long serialVersionUID = 1244964456966270026L; + + /** + * Create a new horizontal layout with the specified number of columns. + * + * @param columns + * The number of columns in this layout. + */ + public HLayout(final int columns) { + super(1, columns); + } +} diff --git a/base/src/main/java/bjc/utils/gui/layout/VLayout.java b/base/src/main/java/bjc/utils/gui/layout/VLayout.java new file mode 100644 index 0000000..6993365 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/layout/VLayout.java @@ -0,0 +1,25 @@ +package bjc.utils.gui.layout; + +import java.awt.GridLayout; + +/** + * A layout that lays out its components vertically, evenly sharing space among + * them. + * + * @author ben + * + */ +public class VLayout extends GridLayout { + // Version ID for serializations + private static final long serialVersionUID = -6417962941602322663L; + + /** + * Create a new vertical layout with the specified number of rows. + * + * @param rows + * The number of rows. + */ + public VLayout(final int rows) { + super(rows, 1); + } +} diff --git a/base/src/main/java/bjc/utils/gui/panels/DropdownListPanel.java b/base/src/main/java/bjc/utils/gui/panels/DropdownListPanel.java new file mode 100644 index 0000000..4f71d38 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/panels/DropdownListPanel.java @@ -0,0 +1,73 @@ +package bjc.utils.gui.panels; + +import java.awt.BorderLayout; + +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.ListSelectionModel; + +import bjc.utils.funcdata.IList; +import bjc.utils.gui.layout.AutosizeLayout; +import bjc.utils.gui.layout.HLayout; + +/** + * A panel that allows you to select choices from a dropdown list + * + * @author ben + * + */ +public class DropdownListPanel extends JPanel { + private static final long serialVersionUID = 2719963952350133541L; + + /** + * Create a new dropdown list panel + * + * @param <T> + * The type of items in the dropdown list + * @param type + * The label of the type of items in the list + * @param model + * The model to put items into + * @param choices + * The items to choose from + */ + public <T> DropdownListPanel(final String type, final DefaultListModel<T> model, final IList<T> choices) { + setLayout(new AutosizeLayout()); + + final JPanel itemInputPanel = new JPanel(); + itemInputPanel.setLayout(new BorderLayout()); + + final JPanel addItemPanel = new JPanel(); + addItemPanel.setLayout(new HLayout(2)); + + final JComboBox<T> addItemBox = new JComboBox<>(); + choices.forEach(addItemBox::addItem); + + final JButton addItemButton = new JButton("Add " + type); + + addItemPanel.add(addItemBox); + addItemPanel.add(addItemButton); + + final JList<T> itemList = new JList<>(model); + itemList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + final JButton removeItemButton = new JButton("Remove " + type); + + addItemButton.addActionListener((ev) -> { + model.addElement(addItemBox.getItemAt(addItemBox.getSelectedIndex())); + }); + + removeItemButton.addActionListener((ev) -> { + model.remove(itemList.getSelectedIndex()); + }); + + itemInputPanel.add(addItemPanel, BorderLayout.PAGE_START); + itemInputPanel.add(itemList, BorderLayout.CENTER); + itemInputPanel.add(removeItemButton, BorderLayout.PAGE_END); + + add(itemInputPanel); + } +} diff --git a/base/src/main/java/bjc/utils/gui/panels/FormattedInputPanel.java b/base/src/main/java/bjc/utils/gui/panels/FormattedInputPanel.java new file mode 100644 index 0000000..2cecf0c --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/panels/FormattedInputPanel.java @@ -0,0 +1,66 @@ +package bjc.utils.gui.panels; + +import java.util.function.Consumer; + +import javax.swing.JFormattedTextField; +import javax.swing.JFormattedTextField.AbstractFormatter; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import bjc.utils.gui.layout.HLayout; + +/** + * A simple panel allowing for input of a single formatted value + * + * @author ben + * + * @param <InputVal> + * The type of value being formatted + */ +public class FormattedInputPanel<InputVal> extends JPanel { + private static final long serialVersionUID = 5232016563558588031L; + + private final JFormattedTextField field; + + /** + * Create a new formatted input panel + * + * @param label + * The label for this panel + * @param length + * The length of this panel + * @param formatter + * The formatter to use for input + * @param reciever + * The action to call whenever the value changes + */ + @SuppressWarnings("unchecked") + public FormattedInputPanel(final String label, final int length, final AbstractFormatter formatter, + final Consumer<InputVal> reciever) { + setLayout(new HLayout(2)); + + final JLabel lab = new JLabel(label); + field = new JFormattedTextField(formatter); + + field.setColumns(length); + field.setFocusLostBehavior(JFormattedTextField.COMMIT_OR_REVERT); + field.addPropertyChangeListener("value", (event) -> { + // This is safe, because InputVal should be the type of + // whatever object the formatter is returning + reciever.accept((InputVal) field.getValue()); + }); + + add(lab); + add(field); + } + + /** + * Reset the value in this panel to a specified value + * + * @param value + * The value to set the panel to + */ + public void resetValues(final InputVal value) { + field.setValue(value); + } +} diff --git a/base/src/main/java/bjc/utils/gui/panels/HolderOutputPanel.java b/base/src/main/java/bjc/utils/gui/panels/HolderOutputPanel.java new file mode 100644 index 0000000..653dace --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/panels/HolderOutputPanel.java @@ -0,0 +1,79 @@ +package bjc.utils.gui.panels; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.Timer; + +import bjc.utils.data.IHolder; +import bjc.utils.gui.layout.HLayout; + +/** + * A panel that outputs a value bound to a {@link IHolder} + * + * @author ben + * + */ +public class HolderOutputPanel extends JPanel { + private static final long serialVersionUID = 166573313903782080L; + + private Timer updater; + private final JLabel value; + private final int nDelay; + private final IHolder<String> val; + + /** + * Create a new display panel, backed by a holder + * + * @param lab + * The label to attach to this field + * @param valueHolder + * The holder to get the value from + * @param nDelay + * The delay in ms between value updates + */ + public HolderOutputPanel(final String lab, final IHolder<String> valueHolder, final int nDelay) { + this.val = valueHolder; + this.nDelay = nDelay; + + setLayout(new HLayout(2)); + + final JLabel label = new JLabel(lab); + value = new JLabel("(stopped)"); + + updater = new Timer(nDelay, (event) -> { + value.setText(valueHolder.getValue()); + }); + + add(label); + add(value); + } + + /** + * Set this panel back to its initial state + */ + public void reset() { + stopUpdating(); + + value.setText("(stopped)"); + + updater = new Timer(nDelay, (event) -> { + value.setText(val.getValue()); + }); + } + + /** + * Start updating the contents of the field from the holder + */ + public void startUpdating() { + updater.start(); + } + + /** + * Stop updating the contents of the field from the holder + */ + public void stopUpdating() { + updater.stop(); + + value.setText(value.getText() + " (stopped)"); + } +} diff --git a/base/src/main/java/bjc/utils/gui/panels/ListParameterPanel.java b/base/src/main/java/bjc/utils/gui/panels/ListParameterPanel.java new file mode 100644 index 0000000..cca73d5 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/panels/ListParameterPanel.java @@ -0,0 +1,133 @@ +package bjc.utils.gui.panels; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.ListSelectionModel; + +import bjc.utils.funcdata.IList; +import bjc.utils.gui.SimpleJList; +import bjc.utils.gui.layout.HLayout; +import bjc.utils.gui.layout.VLayout; + +/** + * A panel that has a list of objects and ways of manipulating that list + * + * @author ben + * + * @param <E> + * The type of data stored in the list + */ +public class ListParameterPanel<E> extends JPanel { + // Version id for serialization + private static final long serialVersionUID = 3442971104975491571L; + + /** + * Create a new panel using the specified actions for doing things + * + * @param add + * The action that provides items + * @param edit + * The action that edits items + * @param remove + * The action that removes items + */ + public ListParameterPanel(final Supplier<E> add, final Consumer<E> edit, final Consumer<E> remove) { + this(add, edit, remove, null); + } + + /** + * Create a new panel using the specified actions for doing things + * + * @param add + * The action that provides items + * @param edit + * The action that edits items + * @param remove + * The action that removes items + * @param defaults + * The default values to put in the list + */ + public ListParameterPanel(final Supplier<E> add, final Consumer<E> edit, final Consumer<E> remove, + final IList<E> defaults) { + setLayout(new VLayout(2)); + + JList<E> list; + + if (defaults != null) { + list = SimpleJList.buildFromList(defaults.toIterable()); + } else { + list = new JList<>(new DefaultListModel<>()); + } + + list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + final JPanel buttonPanel = new JPanel(); + + int numButtons = 0; + + if (add != null) { + numButtons++; + } + + if (edit != null) { + numButtons++; + } + + if (remove != null) { + numButtons++; + } + + buttonPanel.setLayout(new HLayout(numButtons)); + + JButton addParam = null; + + if (add != null) { + addParam = new JButton("Add..."); + addParam.addActionListener((event) -> { + final DefaultListModel<E> model = (DefaultListModel<E>) list.getModel(); + + model.addElement(add.get()); + }); + } + + JButton editParam = null; + + if (edit != null) { + editParam = new JButton("Edit..."); + editParam.addActionListener((event) -> { + edit.accept(list.getSelectedValue()); + }); + } + + JButton removeParam = null; + + if (remove != null) { + removeParam = new JButton("Remove..."); + removeParam.addActionListener((event) -> { + final DefaultListModel<E> model = (DefaultListModel<E>) list.getModel(); + + remove.accept(model.remove(list.getSelectedIndex())); + }); + } + + if (add != null) { + buttonPanel.add(addParam); + } + + if (edit != null) { + buttonPanel.add(editParam); + } + + if (remove != null) { + buttonPanel.add(removeParam); + } + + add(list); + add(buttonPanel); + } +} diff --git a/base/src/main/java/bjc/utils/gui/panels/SimpleInputPanel.java b/base/src/main/java/bjc/utils/gui/panels/SimpleInputPanel.java new file mode 100644 index 0000000..65c533d --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/panels/SimpleInputPanel.java @@ -0,0 +1,45 @@ +package bjc.utils.gui.panels; + +import java.awt.BorderLayout; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + +/** + * A simple component for text input + * + * @author ben + * + */ +public class SimpleInputPanel extends JPanel { + private static final long serialVersionUID = -4734279623645236868L; + + /** + * The text field containing the input value + */ + public final JTextField inputValue; + + /** + * Create a new input panel + * + * @param label + * The label for the field + * @param columns + * The number of columns of text input to take + */ + public SimpleInputPanel(final String label, final int columns) { + setLayout(new BorderLayout()); + + final JLabel inputLabel = new JLabel(label); + + if (columns < 1) { + inputValue = new JTextField(); + } else { + inputValue = new JTextField(columns); + } + + add(inputLabel, BorderLayout.LINE_START); + add(inputValue, BorderLayout.CENTER); + } +} diff --git a/base/src/main/java/bjc/utils/gui/panels/SimpleListPanel.java b/base/src/main/java/bjc/utils/gui/panels/SimpleListPanel.java new file mode 100644 index 0000000..edc1797 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/panels/SimpleListPanel.java @@ -0,0 +1,93 @@ +package bjc.utils.gui.panels; + +import java.awt.BorderLayout; +import java.util.function.Consumer; +import java.util.function.Predicate; + +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.ListSelectionModel; + +import bjc.utils.gui.layout.AutosizeLayout; +import bjc.utils.gui.layout.HLayout; + +/** + * A simple list of strings + * + * @author ben + * + */ +public class SimpleListPanel extends JPanel { + private static final long serialVersionUID = 2719963952350133541L; + + private static void addItem(final DefaultListModel<String> model, final Predicate<String> verifier, + final Consumer<String> onFailure, final JTextField addItemField) { + final String potentialItem = addItemField.getText(); + + if (verifier == null || verifier.test(potentialItem)) { + model.addElement(potentialItem); + } else { + onFailure.accept(potentialItem); + } + + addItemField.setText(""); + } + + /** + * Create a new list panel + * + * @param type + * The type of things in the list + * @param model + * The model to put items into + * @param verifier + * The predicate to use to verify items + * @param onFailure + * The function to call when an item doesn't verify + */ + public SimpleListPanel(final String type, final DefaultListModel<String> model, + final Predicate<String> verifier, final Consumer<String> onFailure) { + setLayout(new AutosizeLayout()); + + final JPanel itemInputPanel = new JPanel(); + itemInputPanel.setLayout(new BorderLayout()); + + final JPanel addItemPanel = new JPanel(); + addItemPanel.setLayout(new HLayout(2)); + + final JTextField addItemField = new JTextField(255); + final JButton addItemButton = new JButton("Add " + type); + + addItemPanel.add(addItemField); + addItemPanel.add(addItemButton); + + final JList<String> itemList = new JList<>(model); + itemList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + final JScrollPane listScroller = new JScrollPane(itemList); + + final JButton removeItemButton = new JButton("Remove " + type); + + addItemButton.addActionListener((ev) -> { + addItem(model, verifier, onFailure, addItemField); + }); + + addItemField.addActionListener((ev) -> { + addItem(model, verifier, onFailure, addItemField); + }); + + removeItemButton.addActionListener((ev) -> { + model.remove(itemList.getSelectedIndex()); + }); + + itemInputPanel.add(addItemPanel, BorderLayout.PAGE_START); + itemInputPanel.add(listScroller, BorderLayout.CENTER); + itemInputPanel.add(removeItemButton, BorderLayout.PAGE_END); + + add(itemInputPanel); + } +} diff --git a/base/src/main/java/bjc/utils/gui/panels/SimpleSpinnerPanel.java b/base/src/main/java/bjc/utils/gui/panels/SimpleSpinnerPanel.java new file mode 100644 index 0000000..6106182 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/panels/SimpleSpinnerPanel.java @@ -0,0 +1,42 @@ +package bjc.utils.gui.panels; + +import java.awt.BorderLayout; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSpinner; +import javax.swing.SpinnerModel; + +/** + * A simple spinner control + * + * @author ben + * + */ +public class SimpleSpinnerPanel extends JPanel { + private static final long serialVersionUID = -4734279623645236868L; + + /** + * The spinner being used + */ + public final JSpinner inputValue; + + /** + * Create a new spinner panel + * + * @param label + * The label for the spinner + * @param model + * The model to attach to the spinner + */ + public SimpleSpinnerPanel(final String label, final SpinnerModel model) { + setLayout(new BorderLayout()); + + final JLabel inputLabel = new JLabel(label); + + inputValue = new JSpinner(model); + + add(inputLabel, BorderLayout.LINE_START); + add(inputValue, BorderLayout.CENTER); + } +} diff --git a/base/src/main/java/bjc/utils/gui/panels/SliderInputPanel.java b/base/src/main/java/bjc/utils/gui/panels/SliderInputPanel.java new file mode 100644 index 0000000..e6a6da4 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/panels/SliderInputPanel.java @@ -0,0 +1,187 @@ +package bjc.utils.gui.panels; + +import java.text.ParseException; +import java.util.function.Consumer; + +import javax.swing.JFormattedTextField; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSlider; + +import bjc.utils.gui.layout.HLayout; + +/** + * A simple input panel for a slider-controlled value and a manual-input field + * for setting the slider + * + * @author ben + * + */ +public class SliderInputPanel extends JPanel { + private final class NumberFormatter extends JFormattedTextField.AbstractFormatter { + private static final long serialVersionUID = -4448291795913908270L; + + private final int minValue; + private final int maxValue; + + private final int initValue; + + public NumberFormatter(final SliderSettings settings) { + minValue = settings.minValue; + maxValue = settings.maxValue; + + initValue = settings.initValue; + } + + @Override + public Object stringToValue(final String text) throws ParseException { + try { + final int val = Integer.parseInt(text); + + if (val < minValue) + throw new ParseException("Value must be greater than " + minValue, 0); + else if (val > maxValue) + throw new ParseException("Value must be smaller than " + maxValue, 0); + else return val; + } catch (final NumberFormatException nfex) { + final ParseException pex = new ParseException("Value must be a valid integer", 0); + + pex.initCause(nfex); + + throw pex; + } + } + + @Override + public String valueToString(final Object value) throws ParseException { + if (value == null) return Integer.toString(initValue); + + return Integer.toString((Integer) value); + } + } + + /** + * Represents the settings for a slider + * + * @author ben + * + */ + public static class SliderSettings { + /** + * The minimum value of the slider + */ + public final int minValue; + /** + * The maximum value of the slider + */ + public final int maxValue; + + /** + * The initial value of the slider + */ + public final int initValue; + + /** + * Create a new slider settings, with the initial value in the + * middle + * + * @param min + * The minimum value of the slider + * @param max + * The maximum value of the slider + */ + public SliderSettings(final int min, final int max) { + this(min, max, (min + max) / 2); + } + + /** + * Create a new set of slider sttings + * + * @param min + * The minimum slider value + * @param max + * The maximum slider value + * @param init + * Th initial slider value + */ + public SliderSettings(final int min, final int max, final int init) { + minValue = min; + maxValue = max; + + initValue = init; + } + } + + private static final long serialVersionUID = 2956394160569961404L; + private final JSlider slider; + private final JFormattedTextField field; + + /** + * Create a new slider input panel + * + * @param lab + * The label for the field + * @param settings + * The settings for slider values + * @param majorTick + * The setting for where to place big ticks + * @param minorTick + * The setting for where to place small ticks + * @param action + * The action to execute for a given value + */ + public SliderInputPanel(final String lab, final SliderSettings settings, final int majorTick, + final int minorTick, final Consumer<Integer> action) { + setLayout(new HLayout(3)); + + final JLabel label = new JLabel(lab); + + slider = new JSlider(settings.minValue, settings.maxValue, settings.initValue); + field = new JFormattedTextField(new NumberFormatter(settings)); + + slider.setMajorTickSpacing(majorTick); + slider.setMinorTickSpacing(minorTick); + slider.setPaintTicks(true); + slider.setPaintLabels(true); + + slider.addChangeListener((event) -> { + if (slider.getValueIsAdjusting()) { + // Do nothing + } else { + final int val = slider.getValue(); + + field.setValue(val); + + action.accept(val); + } + }); + + field.setFocusLostBehavior(JFormattedTextField.COMMIT_OR_REVERT); + field.setColumns(15); + field.addPropertyChangeListener("value", (event) -> { + final Object value = field.getValue(); + + if (value == null) { + // Do nothing + } else { + slider.setValue((Integer) value); + } + }); + + add(label); + add(slider); + add(field); + } + + /** + * Reset the values in this panel to a specified value + * + * @param value + * The value to reset the fields to + */ + public void resetValues(final int value) { + slider.setValue(value); + + field.setValue(value); + } +} diff --git a/base/src/main/java/bjc/utils/gui/panels/package-info.java b/base/src/main/java/bjc/utils/gui/panels/package-info.java new file mode 100644 index 0000000..4361885 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/panels/package-info.java @@ -0,0 +1,5 @@ +/** + * @author ben + * + */ +package bjc.utils.gui.panels;
\ No newline at end of file |
