Please can someone tell me if there is a convenient way of prevent JOptionPane from closing upon clicking OK unless the conditions for user input fields are met?
Or do I have no choice but to use JFrame?
My validation logic so far. Doesn't seem to work because the buttons are one-time clickable to some reason...
final JDialog dialog3 = new JDialog(OmniGUI.getFrame(), "Create new Node - id:" + newNodeID);
dialog3.setContentPane(theOPane);
dialog3.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
theOPane.addPropertyChangeListener(new PropertyChangeListener(){
public void propertyChange(PropertyChangeEvent e) {
if(e.getSource() == theOPane){
String val = (String) ((JOptionPane) e.getSource()).getValue();
if(val=="Create"){
System.out.println("Checking content");
if(!valid){
System.out.println("closing the window");
dialog3.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog3.removeAll();
dialog3.dispatchEvent(new WindowEvent(dialog3, WindowEvent.WINDOW_CLOSING));
}
}
}
}
});
dialog3.setLocation(p);
dialog3.pack();
dialog3.setVisible(true);
You can create your own Custom JDialog to check user input etc before closing or moving on. See this link:
Stopping Automatic Dialog Closing
By default, when the user clicks a JOptionPane-created button, the
dialog closes. But what if you want to check the user's answer before
closing the dialog? In this case, you must implement your own property
change listener so that when the user clicks a button, the dialog does
not automatically close.
Here is an example I made:
If you type wrong/no text and click Enter a validation message will be displayed:
If you click X to close Dialog or click Cancel a validation message will be shown also:
If correct text is entered (in this case "David") and enter is clicked a message is shown and JDialog is exited:
CustomDialog.java:
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
class CustomDialog extends JDialog
implements ActionListener,
PropertyChangeListener {
private String typedText = null;
private JTextField textField;
private String magicWord;
private JOptionPane optionPane;
private String btnString1 = "Enter";
private String btnString2 = "Cancel";
/**
* Returns null if the typed string was invalid; otherwise, returns the
* string as the user entered it.
*/
public String getValidatedText() {
return typedText;
}
/**
* Creates the reusable dialog.
*/
public CustomDialog(Frame aFrame, String aWord) {
super(aFrame, true);
magicWord = aWord.toUpperCase();
setTitle("Quiz");
textField = new JTextField(10);
//Create an array of the text and components to be displayed.
String msgString1 = "What was Dr. SEUSS's real last name?";
String msgString2 = "(The answer is \"" + magicWord
+ "\".)";
Object[] array = {msgString1, msgString2, textField};
//Create an array specifying the number of dialog buttons
//and their text.
Object[] options = {btnString1, btnString2};
//Create the JOptionPane.
optionPane = new JOptionPane(array,
JOptionPane.QUESTION_MESSAGE,
JOptionPane.YES_NO_OPTION,
null,
options,
options[0]);
//Make this dialog display it.
setContentPane(optionPane);
//Handle window closing correctly.
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
//Ensure the text field always gets the first focus.
addComponentListener(new ComponentAdapter() {
#Override
public void componentShown(ComponentEvent ce) {
textField.requestFocusInWindow();
}
});
//Register an event handler that puts the text into the option pane.
textField.addActionListener(this);
//Register an event handler that reacts to option pane state changes.
optionPane.addPropertyChangeListener(this);
pack();
}
/**
* This method handles events for the text field.
*/
#Override
public void actionPerformed(ActionEvent e) {
optionPane.setValue(btnString1);
}
/**
* This method reacts to state changes in the option pane.
*/
#Override
public void propertyChange(PropertyChangeEvent e) {
String prop = e.getPropertyName();
if (isVisible()
&& (e.getSource() == optionPane)
&& (JOptionPane.VALUE_PROPERTY.equals(prop)
|| JOptionPane.INPUT_VALUE_PROPERTY.equals(prop))) {
Object value = optionPane.getValue();
if (value == JOptionPane.UNINITIALIZED_VALUE) {
//ignore reset
return;
}
//Reset the JOptionPane's value.
//If you don't do this, then if the user
//presses the same button next time, no
//property change event will be fired.
optionPane.setValue(
JOptionPane.UNINITIALIZED_VALUE);
if (btnString1.equals(value)) {
typedText = textField.getText();
String ucText = typedText.toUpperCase();
if (magicWord.equals(ucText)) {
JOptionPane.showMessageDialog(this, "Correct answer given");
exit();
} else {
//text was invalid
textField.selectAll();
JOptionPane.showMessageDialog(this,
"Sorry, \"" + typedText + "\" "
+ "isn't a valid response.\n"
+ "Please enter "
+ magicWord + ".",
"Try again",
JOptionPane.ERROR_MESSAGE);
typedText = null;
textField.requestFocusInWindow();
}
} else { //user closed dialog or clicked cancel
JOptionPane.showMessageDialog(this, "It's OK. "
+ "We won't force you to type "
+ magicWord + ".");
typedText = null;
exit();
}
}
}
/**
* This method clears the dialog and hides it.
*/
public void exit() {
dispose();
}
public static void main(String... args) {
//create JDialog and components on EDT
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new CustomDialog(null, "David").setVisible(true);
}
});
}
}
One thing about Stop Automatic Dialog Closing is that it is only helps if you want to prevent closing or validate and then close... basing a solution on the sample code in that tutorial, I could not get it to validate and stay open if the validation failed.
In retrospect, I think the reason my first attempt did not work may be because it used JOptionPanel.createDialog() (not what the example code did). Maybe letting the JOptionPanel create it's own JDialog set up some "background" dependencies in how the event processing worked... meh. In any case, I've got what I wanted now: David Kroucamp's code was very useful to me.
I'm posting my solution because it handles PropertyChangeEvents differently than David, so it might be useful to some people. You'll see that much of the code is identical to his (thanks David)
This class checks for file existance and lets the user provide a new name or cancel. It takes some args in the constructor that it uses to validate users' input. The validation is
if(!Files.exists(rootPathArg.resolve(input))) { // close the dialog }
class GetPathNameDialog extends JDialog implements ActionListener, PropertyChangeListener {
/**
* contains the users input
*/
private JTextField textField;
/**
* the option pane that holds all fields and controls in this dialog
*/
private JOptionPane optionPane;
/**
* label for the left button that represents "OK"
*/
private String button1Str;
/**
* label for the right button that represents "Cancel"
*/
private String button2Str;
/**
* path containing the named entity to be renamed.
*/
private Path rootPath;
/**
* Creates the reusable dialog.
*/
/**
* Creates the dialog, panel and all GUI components, sets up listeners.
*
* #param rootPath the path where the file or folder we are renaming lives
* #param initialText the initial text to display in the text field (i.e. current name)
* #param title title of the JDialog itself
* #param textFieldWidth number of columns in the JTextField that will contain the file/folder name
* #param promptStr the propmt to display in the panel
* #param button1Str the label for the "OK" button
* #param button2Str the label for the "Cancel" button
*/
public GetPathNameDialog(Path rootPath, String initialText, String title, int textFieldWidth, String promptStr, String button1Str, String button2Str) {
super((Frame) null, true);
// init class variables
this.rootPath = rootPath;
this.button1Str = button1Str;
this.button2Str = button2Str;
setTitle(title);
textField = new JTextField(textFieldWidth);
textField.setText(initialText);
//Create an array of the text and components to be displayed.
Object[] array = {promptStr, textField};
//Create an array specifying the number of dialog buttons
//and their text.
Object[] options = {button1Str, button2Str};
//Create the JOptionPane.
optionPane = new JOptionPane(
array,
JOptionPane.QUESTION_MESSAGE,
JOptionPane.YES_NO_OPTION,
null,
options,
options[0]);
//Make this dialog display it.
setContentPane(optionPane);
//Handle window closing correctly.
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
//Ensure the text field always gets the first focus.
addComponentListener(new ComponentAdapter() {
#Override
public void componentShown(ComponentEvent ce) {
textField.requestFocusInWindow();
}
});
// Register an event handler that puts the text into the option pane INPUT_VALUE_PROPERTY
textField.addActionListener(this);
// Register an event handler that reacts to option pane state changes.
optionPane.addPropertyChangeListener(this);
// tell this dialog to display close to the current mouse pointer
setLocation(MouseInfo.getPointerInfo().getLocation());
pack();
}
/**
* This method handles events for the text field.
*/
#Override
public void actionPerformed(ActionEvent e) {
// this will fire a INPUT_VALUE_PROPERTY PropertyChangeEvent... takes the user input to the validaton code in the property handler
optionPane.setInputValue(textField.getText());
}
/**
* This method reacts to property changes.
*/
#Override
public void propertyChange(PropertyChangeEvent e) {
String prop = e.getPropertyName();
if (isVisible() && (e.getSource() == optionPane)) {
// the VALUE_PROPERTY is not the same as the INPUT_VALUE_PROPERTY. we make use of the INPUT_VALUE_PROPERTY to carry our data
// but the JOptionPane uses the VALUE_PROPERTY for other stuff
if (JOptionPane.VALUE_PROPERTY.equals(prop)) {
// newValues delivered by VALUE_PROPERTY PropertyChangeEvent can be the actual labels of the button clicked,
// that's sooo counter-intuitive to me, but it's how we know which button got clicked
if (button1Str.equals(e.getNewValue())) {
// "OK" functionality...
// ...this will fire the event that takes the user input to the validation code
optionPane.setInputValue(textField.getText());
} else if (button2Str.equals(e.getNewValue())) {
// "CANCEL" functionality
optionPane.setInputValue(null);
exit();
}
} else if (JOptionPane.INPUT_VALUE_PROPERTY.equals(prop)) {
Object value = optionPane.getInputValue();
// null or empty strings in the text field (ie in the INPUT_VALUE_PROPERTY) are ignored
if (null != value && ((String) value).length() > 0) {
// here is the validation code
if (Files.exists(rootPath.resolve(textField.getText()))) {
// already exists, tell user
JOptionPane.showMessageDialog(this,
"Sorry, " + rootPath.resolve(textField.getText()).toString() + " already exists.\n\n Please enter another name.",
"OK",
JOptionPane.ERROR_MESSAGE);
// Make sure PropertyChangeEvent will fire next time...
// ...PropertyChangeEvents don't fire in setInputValue(newVal)...
// ...if newVal is equal to the current value, but if the user clicks...
// ...button 1 or hits enter in the text field without changing his text,...
// ...we still want to fire another event...
// ...so we reset the property without changing the text in the textField
optionPane.setInputValue(null);
} else {
// does not exist.. we are keeping the users input...
// ... it gets delivered to the user in getInputValue()
exit();
}
}
}
}
}
/**
* returns the users's validated input. Validated means !Files.exists(rootPath.resolve(input)).
*
* #return the text entered by the user, UNINITIALIZED_VALUE if the user X closed, null the user canceled
*/
public Object getInputValue() {
return optionPane.getInputValue();
}
/**
* closes the dialog and triggers the return from setVisible()
*/
public void exit() {
dispose();
}
}
The code to invoke it is:
GetPathNameDialog tempD = new GetPathNameDialog(
someFolderPath,
"theFileNameThatMustBeChanged.txt",
"Change File Name",
50,
"someFolderPath already contains a file named theFileNameThatMustBeChanged.txt." + ".\n\nPlease enter a different file name:",
"Copy the file with the new name", "Do not copy the file");
tempD.setVisible(true);
Object inputObj = tempD.getInputValue();
String input = (inputObj == JOptionPane.UNINITIALIZED_VALUE || null == inputObj ? "" : (String) inputObj);
if (input.length() > 0) {
// we now have a new file name. go ahead and do the copy or rename or whatever...
}
Related
I have a main window called MainFrame which is a jForm to which I update the data depending on a timer, but the problem is that I cannot update the data in the same MainFrame after using the jdialog, since I end up creating another duplicate window, but with the data changed, one with the original timer and the other with the new timer, I know that I can close the first window with dispose() and then keep the second, but I would like to avoid changing windows so much
the code with which I create another window when pressing the jDialog button is the following
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
// TODO add your handling code here:
String textoFieldTimer = jTextField1.getText();
int timeUserConfig = Integer.parseInt(textoFieldTimer);
Timer timeDefault = new Timer(timeUserConfig, null);
TokenAccess token = new TokenAccess();
token.access_code = code;
MainFrame mainFrame = new MainFrame(token);
mainFrame.setVisible(true);
mainFrame.timeDefault.stop();
mainFrame.timeDefault = timeDefault;
mainFrame.setUpdateTime(timeUserConfig);
this.dispose();
}//GEN-LAST:event_jButton1ActionPerformed
Is there any alternative to update the window? something like mainFrame.update(); or maybe send the value of the jTextField from the jDialog to mainFrame? since the previous code creates another MainFrame for me.
Method main setLabel and Timer.start/stop
public void setUpdateTime(int timeUserConfig) {
this.timeUserConfig = timeUserConfig;
if (timeUserConfig == 0) {
timeDefault.start();
timeDefault.addActionListener(new java.awt.event.ActionListener() {
#Override
public void actionPerformed(java.awt.event.ActionEvent evt) {
setLabelText();
String timeUserConfigStr = Integer.toString(timeDefaultInt);
tiempoActualizado.setText("Tiempo de Actualizado: " + timeUserConfigStr+"ms");
}
});
} else {
timeDefault.stop();
timeDefault = new Timer(timeUserConfig, null);
timeDefault.start();
timeDefault.addActionListener(new java.awt.event.ActionListener() {
#Override
public void actionPerformed(java.awt.event.ActionEvent evt) {
setLabelText();
String timeUserConfigStr = Integer.toString(timeUserConfig);
tiempoActualizado.setText("Tiempo de Actualizado: " + timeUserConfigStr+"ms");
}
});
}
}
setLabelText is a method set of label
public void setLabelText() {
String humedadStr = String.valueOf(humedad);
String temperaturaStr = String.valueOf(temperatura);
String presionStr = String.valueOf(co2);
temporalHum.setText(humedadStr);
temporalTemperatura.setText(temperaturaStr);
temporalPresion.setText(presionStr);
}
Any help would be appreciated.
Thanks for the update, and I found another solution without using an OptionPane from this question: programmatically close a JPanel which is displayed in JDialog.
I cannot replicate your codings
Start with the MainFrame, assuming you opened the JDialog by clicking on a button and wants to setText() to label lbSomething:
private void btInputActionPerformed(java.awt.event.ActionEvent evt) {
// Open new JDialog when button is clicked
NewJDialog dialog = new NewJDialog(new javax.swing.JFrame, true);
dialog.setVisible(true);
// Get user input from JDialog
String temp = dialog.getInput();
if (temp != null) {
/*
* Perform jButton1ActionPerformed() content here
* Including timeUserConfig, timeDefault and setUpdateTime() here
* so that you don't have to access mainFrame in the JDialog.
*/
lbSomething.setText(temp);
}
}
Then about the JDialog (with simple input detection):
public class NewJDialog extends javax.swing.JDialog {
// Set the variable as class variable
private String textTOFieldTimer;
public NewJDialog(java.awt.Frame parent, boolean modal) {
// default contents
}
#SupressWarinings("unchecked")
private void initComponents() {
// default contents
}
private void btSaveAction Performed(java.awt.event.ActionEvent evt) {
// Check if input correct and whether to disable JDialog
if (tfInput.getText.length() != 0) {
input = tfInput.getText();
// Connect to the whole JDialog by getWindowAncestor()
Window window = SwingUtilities.getWindowAncestor(NewJDialog.this);
// Just setVisible(false) instead of dispose()
window.setVisible(false);
} else {
JOptionPane.showMessageDialog(this, "Wrong Input");
}
}
public String getInput() {
return textToFieldTimer;
}
// default variables declarations
}
Hope this answer helps you well.
Would be better if you displayed the source code, but a simple solution to update values to an existing JFrame is by using setText() and getText().
For example:
String input = JOptionPane.showInputDialog(this, "Nuevo valor");
lbPresionActual.setText(input);
If you created a self-defined JDialog, it is about to transfer the input value when closing the JDialog, and that could be a different question.
I am writing a class (UIPromptComboBox) that extends JComboBox. The combobox is editable and for one application of the class it is implemented with a controlling ActionListener.
Currently, when the combobox is edited it fires the ActionListener which is good. However this ActionListener is also fired when I deselect the combobox and I cannot distinguish between the two events nor do I want it to fire when the combobox is deselected.
Implementing Class
private void addUIField() {
// Initialise and place combobox
this.myGuiTextField = new UIPromptComboBox();
myGuiTextField.setSize(COMBO_WIDTH, defaultHeight);
GuiUtils.positionControl(myPanel, myGuiTextField, myTop, PROMPT_X_LOC);
//Add action listener
myGuiTextField.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
if (evt.getActionCommand().equals("comboBoxEdited")) {
newUIcreated((UIPromptComboBox) evt.getSource());
}
}
private void newUIcreated(UIPromptComboBox alteredGuiTextField) {
try {
UIPrompt uip = alteredGuiTextField.getUIPrompt(((PowerPointTextItem) myPPTRef).getValue());
if (!simInfo.isInPrompts(uip)) {
simInfo.addUIPrompt(uip);
alteredGuiTextField.addNewUIPrompt(uip);
}
} catch (MissingPowerpointItem ex) {
Exceptions.printStackTrace(ex);
}
}
});
}
Class that extends JComboBox
public class UIPromptComboBox extends JComboBox {
public UIPromptComboBox(UIPrompt[] items) {
super(items);
this.setEditable(true);
}
public UIPromptComboBox() {
this.setEditable(true);
this.setEnabled(false);
}
/**
* returns either the selected UI prompt or a new prompt using the example
* text
*
* #param exampleText only used if new prompt is created
* #return UI prompt selected
*/
public UIPrompt getUIPrompt(String exampleText) {
UIPrompt uIPrompt = null;
Object returnedItem = this.getSelectedItem();
if (returnedItem instanceof UIPrompt) {
uIPrompt = (UIPrompt) returnedItem;
} else if (returnedItem instanceof String) {
uIPrompt = new UIPrompt((String) returnedItem, exampleText);
}
return uIPrompt;
}
public void addNewUIPrompt(UIPrompt newPrompt) {
ActionListener[] actionListerners = this.getActionListeners();
this.removeActionListener(this);
this.addItem(newPrompt);
this.setSelectedItem(newPrompt);
for (ActionListener al : actionListerners) {
this.addActionListener(al);
}
}
/**
* Used for displaying a report value sentence
* i.e. a string that is not associated with UI Prompts
* #param newText report value sentence
*/
public void setText(String newText) {
this.removeAllItems();
this.addItem(newText);
this.setSelectedItem(newText);
}
/**
* For when the UI prompts can be added on construction
*
* #param currentUIs list of UI promts
*/
public void addItems(UIPrompt[] currentUIs) {
this.removeAllItems();
DefaultComboBoxModel boxModel = new DefaultComboBoxModel(currentUIs);
this.setModel(boxModel);
}
}
The multiple firing due to losing focus is causing multiple objects to be created and added to the list. I think I may have implemented the ActionListener incorrectly. Thank you for your help
as you stated you only want the event to fire if the user presses enter. a better way to implement that, would be using an keylistener instead of an action listener.
myGuiTextField.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
newUIcreated((UIPromptComboBox) evt.getSource());
}
}
private void newUIcreated(UIPromptComboBox alteredGuiTextField) {
try {
UIPrompt uip = alteredGuiTextField.getUIPrompt(((PowerPointTextItem) myPPTRef).getValue());
if (!simInfo.isInPrompts(uip)) {
simInfo.addUIPrompt(uip);
alteredGuiTextField.addNewUIPrompt(uip);
}
} catch (MissingPowerpointItem ex) {
Exceptions.printStackTrace(ex);
}
}
});
this should now only fire your event newUIcreated, once the users presses enter and at no other time. replace your action listener with this
I have now finally found the issue.
The displaying of the UIPrompt included the addition of a string that sometimes contained a new line character.
The action of clicking another field was triggering the render of the UIPrompt however when this contained a newline character it was triggering the ActionListener again. This what the reason for the repeated action of comboBoxEdited.
This is my code
JToolBar customizeKeys = new JToolBar();
customizeKeys.add(new ChangeKeyListen("left"));
private class ChangeKeyListen extends AbstractAction{
private JDialog myDialog;
class KeyGetter extends KeyAdapter {
#Override
public void keyPressed(KeyEvent e) {
super.keyPressed(e);
OtherPanel.this.map(
KeyEvent.getKeyText(e.getKeyCode()),
keyAction);
myDialog.setVisible(false);
myDialog.removeKeyListener(getKeyListeners()[0]);
}
};
public ChangeKeyListen(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
myDialog = new JOptionPane().createDialog("Press a key");
myDialog.setVisible(true);
myDialog.requestFocusInWindow();
System.out.println(myDialog.getFocusableWindowState());
myDialog.addKeyListener(new KeyGetter());
System.out.println( myDialog.getKeyListeners());
}
}
What I am trying to do here is when the user clicks the JButton that was added to the JToolBar with the attributes of its action, the user will be prompted with my own customized dialog box. The user can then press any key to close the dialog box.(it actually just be invisible). When I run the application, everything looks fine. The JToolBar looks right and the button looks right. When I click the button, the correct controller behavior occurs as the dialog box is popped up.(just visible) However the key adapter's keyPressed method isn't being triggered at all when i press a key.
What I've done to debug this is to first make sure that the JDialog can first of all be focusable so it can receive key events from the keyboard. I did that with this line of
System.out.println(myDialog.getFocusableWindowState());
and what I got on the console was true. Next I made sure that the key listener was being set. I did that with
System.out.println( myDialog.getKeyListeners());
and this printed out
[Ljava.awt.event.KeyListener;#350b914b
which I assumed was a correct memory address for an object allocated from the heap.
I then checked out similar threads.
My issue couldn't be Jbutton listener isn't triggered, why? because the dialog box showed up and I made sure that the key listener was added with the print key listeners line.
I couldn't use what the user said in Trying to use key Listener because I need to listen for the key press and use that key press later in my program.
And this doesn't help either Why wont this KeyEvent work?
because I need a general reaction to key presses to obtain which key was pressed.
I know that keyPressed isn't being executed because I put a breakpoint inside the method and this print statement
System.out.println(KeyEvent.getKeyText(e.getKeyCode()));
wasn't printing anything on the console.
Does anyone know how i can fix this issue?
You are adding the KeyListener to the dialog created by the JOptionPane.
However, focus is on the JButton on the dialog. KeyEvents are only dispatched to the component with focus so your key listener code is never invoked.
Why are you trying to listen for any key to close the dialog? The is NOT user friendly. The user does not know that is the way to close the dialog since this is not a standard UI convention. The user should click on the button to close the dialog.
If you really do need to listen to any key pressed while the dialog is open then check out Global Event Listeners which shows how you can use an AWTEventListener to listen for any key event regardless of which component has focus.
Having dealt with very similar issues, I encourage people who want to add listeners to the components of their JDialog to follow the approach shown here. It allows for more control over the components.
The following example demonstrates a custom dialog that validates the user's input each time the text changes using a KeyListener.
public class DialogWithListener extends JDialog {
private JTextField textField = new JTextField();
private boolean userPressedOk = false;
/**
* Creates a dialog that lets the user enter text in a text field.
* <p>
* Each time the user presses a key, the text is validated using the
* {#link Predicate}s in {#code predsAndMsgs}. If the text doesn't satisfy
* all predicates, the dialog shows the message associated with the first
* unsatisfied predicate.
*
* #param predsAndMsgs
* a map from {#link Predicate}s to the messages we'll show to
* users if the text they entered doesn't satisfy the predicates
*/
public DialogWithListener(Map<Predicate<String>, String> predsAndMsgs) {
JLabel textFieldLabel = new JLabel("Enter text:");
// Show this if the text the user entered satisfies our predicates
String okText = "All good";
JLabel statusLabel = new JLabel(okText);
Object[] paneContent = { textFieldLabel, textField, statusLabel };
JButton okButton = new JButton("OK");
okButton.addActionListener(e -> {
userPressedOk = true;
setVisible(false);
});
Object[] options = { okButton };
JOptionPane optionPane = new JOptionPane(paneContent,
JOptionPane.QUESTION_MESSAGE, JOptionPane.DEFAULT_OPTION, null,
options);
getContentPane().add(optionPane);
setLocationRelativeTo(optionPane.getParent());
setFocusTo(textField);
// Check the user input each time a key is released
textField.addKeyListener(new KeyAdapter() {
#Override
public void keyReleased(KeyEvent event) {
validate(predsAndMsgs, textField.getText(), okText,
statusLabel, okButton);
}
});
setModal(true);
setResizable(false);
pack();
}
/**
* Validates the {#code textToValidate}.
* <p>
* The {#link Predicate}s in {#link predsAndMsgs} determine whether the text
* is valid. If the text is invalid, we show the message that is associated
* with the predicate and disable this dialog's OK button.
*
* #param predsAndMsgs
* a map from {#link Predicate}s that must hold for the
* {#code textToValidate} to the messages we'll show to the user
* if a predicate is not satisfied.
* #param textToValidate
* we validate this text against the {#link Predicate}s in
* {#link predsAndMsgs}
* #param okText
* this text is shown if the {#code textToValidate} satisfies all
* predicates
* #param statusLabel
* a {#link JLabel} that either shows the {#link okText} or the
* message of the first predicate that doesn't hold true for the
* {#link textToValidate}
* #param okButton
* we enable and disable this button depending on whether the
* {#link textToValidate} is valid
*/
private void validate(Map<Predicate<String>, String> predsAndMsgs,
String textToValidate, String okText, JLabel statusLabel,
JButton okButton) {
// Get the first predicate that the text to validate doesn't satisfy
Optional<Predicate<String>> unsatisfiedPredMaybe = predsAndMsgs
.keySet().stream().filter(pred -> !pred.test(textToValidate))
.findFirst();
// At least one predicate was not satisfied
if (unsatisfiedPredMaybe.isPresent()) {
// Tell the user the text they entered can't be accepted
String msg = predsAndMsgs.get(unsatisfiedPredMaybe.get());
statusLabel.setText(msg);
okButton.setEnabled(false);
} else {
statusLabel.setText(okText);
okButton.setEnabled(true);
}
pack();
}
private void setFocusTo(JComponent comp) {
addComponentListener(new ComponentAdapter() {
#Override
public void componentShown(ComponentEvent ce) {
comp.requestFocusInWindow();
}
});
}
public Optional<String> display() {
userPressedOk = false;
// Because the dialog is modal it will block here
setVisible(true);
String dialogResult = null;
if (userPressedOk) {
dialogResult = textField.getText();
}
return Optional.ofNullable(dialogResult);
}
}
And this is how you create and show the dialog:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager
.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException
| IllegalAccessException
| UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
JFrame frame = new JFrame();
JButton showDialogButton = new JButton("Show Dialog");
// Define the predicates that the user entered-text should satisfy and
// the messages shown to the user if it doesn't
Map<Predicate<String>, String> predicatesAndMessages = new HashMap<>();
Predicate<String> dontMentionHisName = text -> !text
.contains("Voldemort");
predicatesAndMessages.put(dontMentionHisName,
"Sssh! You can't say that!");
DialogWithListener dialog = new DialogWithListener(
predicatesAndMessages);
dialog.setTitle("My dialog");
showDialogButton.addActionListener(e -> dialog.display().ifPresent(
userText -> System.out.println(userText)));
frame.getContentPane().add(showDialogButton);
frame.pack();
frame.setVisible(true);
}
I am using a vaadin TextArea as a rough console. The user can enter commands which should be executed when he presses the enter key. Is there a way to specify this with a listener on the TextArea?
The closest thing I found is to use:
TextArea textArea = new TextArea();
textArea.addTextChangeListener(this);
textArea.setTextChangeEventMode(TextChangeEventMode.EAGER);
And handle the text change event:
#Override
public void textChange(TextChangeEvent event) {
System.out.println(event.getText());
}
This is however triggered as soon as text has been entered in the TextArea. I would like to be notified only when the enter key has been pressed.
You cannot listen to shortcut keys on the textarea itself, but a simple solution would be to add a submit button and use enter as it's shortcut:
Button b = new Button("submit", new Button.ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
// handle your event
}
});
layout.addComponent(b);
b.setClickShortcut(KeyCode.ENTER);
You can hide the button itself if you don't wish it:
b.setVisible(false);
Another solution would be to use ShortcutActions and Handlers as described in here:
https://vaadin.com/book/-/page/advanced.shortcuts.html
But in either case you have to take into account that listening to enter key will cause a conflict when using a TextArea component because you also need to use the same key to get to the next line in the TextArea.
You can add a ShortcutListener to the TextArea, like this:
TextArea textArea = new TextArea();
textArea.addShortcutListener(enter);
Now you just have to initialize some ShortcutListener as follows:
ShortcutListener enter = new ShortcutListener("Enter", KeyCode.ENTER, null) {
#Override
public void handleAction(Object sender, Object target) {
// Do nice stuff
log.info("Enter pressed");
}
};
For this, we use the following utility function
/**
* Perform the specified action when the text field has focus and `ENTER` is pressed.
*
* #param tf The {#link com.vaadin.ui.TextField text field} or
* {#link com.vaadin.ui.TextArea text area)
* #param action The action to perform
*/
public static void onKeyEnter(AbstractTextField tf, Consumer<AbstractTextField> action) {
tf.addFocusListener(event -> {
final Registration r = tf.addShortcutListener(
new ShortcutListener("Enter", KeyCode.ENTER, null) {
#Override
public void handleAction(Object sender, Object target) {
// sender: UI, target: TextField
assert target == tf;
action.accept(tf);
}
});
tf.addBlurListener(e -> r.remove());
});
}
To use it:
final TextField searchField = new TextField(); // or TextArea
searchField.setPlaceholder("Search text (ENTER)...");
// ..
onKeyEnter(searchField, tf -> doSearch(tf.getValue()));
//Refactored for vaadin 7
public static void onKeyEnter(AbstractTextField tf, Consumer<AbstractTextField> action) {
tf.addFocusListener(event -> {
ShortcutListener scl = new ShortcutListener("Enter", KeyCode.ENTER, null) {
public void handleAction(Object sender, Object target) {
assert target == tf;
action.accept(tf);
}
};
tf.addShortcutListener(scl);
tf.addBlurListener(e -> tf.removeShortcutListener(scl));
});
}
When my application loads, which is made using netbeans, the first JTextField is automatically focused, and in this JTextField, I have written "Enter your Username", it will go away when the user clicks on this field, but when the application is loaded, this field is focused, means I can't see "Enter your Username", how to unfocus it on startup ?
A log-in would be best done in a modal dialog, but that introduces problems in that the method requestFocusInWindow() must be called after the component is visible, but that is blocked by the fact the dialog is modal!
This example uses Rob Camick's RequestFocusListener (as presented in Dialog Focus) to manage focus after the dialog is visible.
Note: That is how it appears before the user does anything. The password field is focused by default.
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
public class LoginRequired {
LoginRequired() {
JFrame f = new JFrame("Login Required");
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setResizable(false);
f.setSize(400, 300); // not recommended, but used here for convenience
f.setLocationByPlatform(true);
f.setVisible(true);
showLogin(f);
}
private void showLogin(JFrame frame) {
JPanel p = new JPanel(new BorderLayout(5,5));
JPanel labels = new JPanel(new GridLayout(0,1,2,2));
labels.add(new JLabel("User Name", SwingConstants.TRAILING));
labels.add(new JLabel("Password", SwingConstants.TRAILING));
p.add(labels, BorderLayout.LINE_START);
JPanel controls = new JPanel(new GridLayout(0,1,2,2));
JTextField username = new JTextField("Joe Blogs");
controls.add(username);
JPasswordField password = new JPasswordField();
password.addAncestorListener(new RequestFocusListener(false));
controls.add(password);
p.add(controls, BorderLayout.CENTER);
JOptionPane.showMessageDialog(
frame, p, "Log In", JOptionPane.QUESTION_MESSAGE);
System.out.println("User Name: " + username.getText());
System.out.println("Password: " + new String(password.getPassword()));
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new LoginRequired();
});
}
}
/**
* Convenience class to request focus on a component.
*
* When the component is added to a realized Window then component will
* request focus immediately, since the ancestorAdded event is fired
* immediately.
*
* When the component is added to a non realized Window, then the focus
* request will be made once the window is realized, since the
* ancestorAdded event will not be fired until then.
*
* Using the default constructor will cause the listener to be removed
* from the component once the AncestorEvent is generated. A second constructor
* allows you to specify a boolean value of false to prevent the
* AncestorListener from being removed when the event is generated. This will
* allow you to reuse the listener each time the event is generated.
*/
class RequestFocusListener implements AncestorListener
{
private boolean removeListener;
/*
* Convenience constructor. The listener is only used once and then it is
* removed from the component.
*/
public RequestFocusListener()
{
this(true);
}
/*
* Constructor that controls whether this listen can be used once or
* multiple times.
*
* #param removeListener when true this listener is only invoked once
* otherwise it can be invoked multiple times.
*/
public RequestFocusListener(boolean removeListener)
{
this.removeListener = removeListener;
}
#Override
public void ancestorAdded(AncestorEvent e)
{
JComponent component = e.getComponent();
component.requestFocusInWindow();
if (removeListener)
component.removeAncestorListener( this );
}
#Override
public void ancestorMoved(AncestorEvent e) {}
#Override
public void ancestorRemoved(AncestorEvent e) {}
}
textField.setFocusable(false);
textField.setFocusable(true);
If, and only if, textField had focus, the next component in TAB-order order will get focus automatically. The effect is the same as a TAB press.
(not tested in a GUI with just one focusable component :) )
Use requestFocusInWindow() to set focus on some other component rather then your JTextfield first.
But i'd suggest not to alter the native focus system, rather setText(String s) on the JTextField after initComponents() call in the constructor (assumed to be in netbeans).
Further optional reading: How to Use the Focus Subsystem
I think giving keyboard focus to the username field is the correct behavior, assuming that's what the user needs to do first. Instead of clearing on focus, why not clear only after the user types something?:
import java.awt.*;
import javax.swing.*;
import javax.swing.text.Document;
public class PlaceholderTextField extends JTextField {
public static void main(final String[] args) {
final PlaceholderTextField tf = new PlaceholderTextField("");
tf.setColumns(20);
tf.setPlaceholder("All your base are belong to us!");
final Font f = tf.getFont();
tf.setFont(new Font(f.getName(), f.getStyle(), 30));
JOptionPane.showMessageDialog(null, tf);
}
private String placeholder;
public PlaceholderTextField() {
}
public PlaceholderTextField(
final Document pDoc,
final String pText,
final int pColumns)
{
super(pDoc, pText, pColumns);
}
public PlaceholderTextField(final int pColumns) {
super(pColumns);
}
public PlaceholderTextField(final String pText) {
super(pText);
}
public PlaceholderTextField(final String pText, final int pColumns) {
super(pText, pColumns);
}
public String getPlaceholder() {
return placeholder;
}
#Override
protected void paintComponent(final Graphics pG) {
super.paintComponent(pG);
if (placeholder.length() == 0 || getText().length() > 0) {
return;
}
final Graphics2D g = (Graphics2D) pG;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(getDisabledTextColor());
g.drawString(placeholder, getInsets().left, pG.getFontMetrics()
.getMaxAscent() + getInsets().top);
}
public void setPlaceholder(final String s) {
placeholder = s;
}
}
If you really just want to remove focus, some options:
component.setFocusable(false);
KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent();
KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner();