0

I'm tying to make a program that acts similar to the Windows Command Prompt, or a terminal. It's basically just a JFrame with a JTextArea as output, and a JTextField as input. It looks like this:

enter image description here

I want to be able to get input from the JTextField whenever my program calls a method that returns a String, something like:

public static String getInput() {
    //Wait for user to enter something, then press the enter key
}

I added an AbstractAction so I can do stuff when the enter key is pressed, but I still could figure out how to return the input as a String whenever I call the method.

Action action = new AbstractAction(){
    @Override
    public void actionPerformed(ActionEvent e) {

        //Clears the JTextField
        input.setText("");

    }
};

I could put something like userInput = input.getText() in public void actionPerformed(), so it would just set a variable to whatever has been entered every time, and use userInput whenever I want to, but I want the user to have time to read whats on the screen, then have the program wait for a response, instead of just using the last thing they entered right away.

I tried to use a userInput variable and a boolean variable like this:

private static String userInput = "";
private static boolean enterPressed = false;

Action action = new AbstractAction(){
    @Override
    public void actionPerformed(ActionEvent e) {

        userInput = input.getText()
        enterPressed = true;
        input.setText("");

    }
};

...

public static String getInput() {

    enterPressed = false;
    while(!enterPressed){
       //Do Nothing
    }
    enterPressed = false;
    return userInput;

}

When I called output.setText(getInput());, it worked like I wanted to, except that the while(!enterPressed){} made my processor work a lot harder than it should need to. I'm pretty sure there's probably a lot better way of doing this.

Here's my whole code right now:

public class ConsoleFrame {

//Objects for Console Frame
JFrame frame = new JFrame();
JTextArea output = new JTextArea();
JTextField input = new JTextField();
BoxLayout boxLayout = new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS);
JScrollPane scrollPane = new JScrollPane(output);
DefaultCaret caret = (DefaultCaret)output.getCaret();
Action action = new AbstractAction(){
    @Override
    public void actionPerformed(ActionEvent e) {

        input.setText("");

    }
};
ConsoleFrame(){
    input.addActionListener(action);
    caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
    frame.setLayout(boxLayout);
    frame.add(scrollPane);
    frame.add(input);
    frame.pack();
    frame.setVisible(true);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(500, 250);
    frame.setLocationRelativeTo(null);
    input.setMaximumSize(new Dimension(Integer.MAX_VALUE, 10));
    output.setEditable(false);
    output.setAutoscrolls(true);
}
public static String getInput() {
    return null;
}

}

So, how could I stop the program until the user presses enter, every time I call getInput();?

hhaslam11
  • 191
  • 2
  • 7
  • 24
  • 2
    You don't. Swing, like almost all GUI frame works is event driven, that is, at some point in time (which you can't determine), something will happen and then you will respond to it. You can't think of GUI's in a linear fashion where you code moves from one point to another. Essentially, if you put your text area and text field on the screen, the program will "pause" waiting for the user to do something. You need to design you solution around this functionality – MadProgrammer Aug 07 '15 at 23:48
  • What do you want to show in the Console when `getInput` is _not_ called. If you don't do anything, why stop the program? You just want to output something, and wait for input, right? You don't even need a `getInput` method. You disable JTextField when you output stuff, and enable it when you want input. – Sweeper Aug 07 '15 at 23:49
  • 2
    *"I want to be able to get input from the JTextField whenever my program calls a method that returns a String, something like:"* - `JTextField#getText`. Instead, you could use an observer pattern instead, where when the `ActionListener` is triggered, you trigger another event to a registered listener (possibly of your own type) which tells any interested party that a new value is available... – MadProgrammer Aug 07 '15 at 23:49

3 Answers3

4

EDIT: First half removed. It was brought to my attention that it was erroneous.

The best option is to enclose a call to whatever you need your computer to execute after the user inputs text inside your actionPerformed method. So when the user inputs text and presses enter, the program automatically continues from there.

Action action = new AbstractAction(){
@Override
    public void actionPerformed(ActionEvent e) {

    userInput = input.getText()
    enterPressed = true;
    input.setText("");
    //call next method;

    }
};

This requires some more formatting work on your behalf, but it could be helpful depending on your project. This link has more information on this strategy.

Hope this helped.

Community
  • 1
  • 1
Benjamin Lowry
  • 3,730
  • 1
  • 23
  • 27
  • 1
    Welcome to the wonderful world of how to screw your program completely in Swing. Swing is a single threaded environment, using `Thread.sleep` within the EDT will cause the UI to become responsive and is generally considered a bad idea (really, you should be using a lock of some kind which reduces the CPU over head to 0). – MadProgrammer Aug 08 '15 at 03:33
  • @MadProgrammer I know this isn't a perfect solution, and I am have never claimed it to be one. I just have found that this works for me in many situations. You're probably right that it isn't a very good idea for more intricate coding, but I think it can be suitable for simple programs. – Benjamin Lowry Aug 08 '15 at 03:37
  • The next question that the OP will ask is "why does my program no longer respond to user input" - because of what've you told him, which is wasting the OP's time and leading other potential users astray, making more work for the rest of us and diminishing your reputation at the same time. It's not good advice generally (for Swing), nor is it a solution to the OP's problem. Your second half of the question would be the solution the OP needs. Instead of the OP trying to think like the program is a console/linear program, they need to think in the concept of a event driven environment. – MadProgrammer Aug 08 '15 at 03:44
  • I'm in two minds as to whether to downvote the answer as half of it is wrong, but that have is dangerously wrong – MadProgrammer Aug 08 '15 at 03:44
  • 1
    @MadProgrammer I'll take the first half off. – Benjamin Lowry Aug 08 '15 at 03:50
4

I want to be able to get input from the JTextField whenever my program calls a method that returns a String, something like:

I added an AbstractAction so I can do stuff when the enter key is pressed, but I still could figure out how to return the input as a String whenever I call the method.

public String getInput() {
    return input.getText();
}

So, how could I stop the program until the user presses enter, every time I call getInput();?

You don't. Swing, like most UI frame works is event driven, that is, something happens and your respond to it.

So, with that in mind you should consider using some kind Observer Pattern, where you use a call back to be notified of some kind of change which you are interested in, like your ActionListener for example.

Instead, you could provide some kind of listener, which interested parties would register with and when the field changes you would notify them, for example...

import java.util.EventListener;
import java.util.EventObject;

public class InputEvent extends EventObject {

    private final String text;

    public InputEvent(Object source, String text) {
        super(source);
        this.text = text;
    }

    public String getText() {
        return text;
    }

}

public interface InputObsever extends EventListener {

    public void inputChanged(InputEvent evt);

}

So, we now have an observer who will be notified when ever the input is changed/updated.

Then we simply need a way to un/register and fire the event...

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.event.EventListenerList;
import javax.swing.text.DefaultCaret;

public class ConsoleFrame {

//Objects for Console Frame
    JFrame frame = new JFrame();
    JTextArea output = new JTextArea();
    JTextField input = new JTextField();
    BoxLayout boxLayout = new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS);
    JScrollPane scrollPane = new JScrollPane(output);
    DefaultCaret caret = (DefaultCaret) output.getCaret();
    Action action = new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent e) {

            fireInputChanged(input.getText());
            input.selectAll();

        }
    };

    private EventListenerList listenerList = new EventListenerList();

    ConsoleFrame() {
        input.addActionListener(action);
        caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
        frame.setLayout(boxLayout);
        frame.add(scrollPane);
        frame.add(input);
        frame.pack();
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(500, 250);
        frame.setLocationRelativeTo(null);
        input.setMaximumSize(new Dimension(Integer.MAX_VALUE, 10));
        output.setEditable(false);
        output.setAutoscrolls(true);
    }

    public String getInput() {
        return input.getText();
    }

    public void addInputObsever(InputObsever obsever) {
        listenerList.add(InputObsever.class, obsever);
    }

    public void removeInputObsever(InputObsever obsever) {
        listenerList.remove(InputObsever.class, obsever);
    }

    protected void fireInputChanged(String text) {

        InputObsever[] listeners = listenerList.getListeners(InputObsever.class);
        if (listeners.length > 0) {

            InputEvent evt = new InputEvent(this, text);
            for (InputObsever obsever : listeners) {
                obsever.inputChanged(evt);
            }

        }

    }

}

Now, the point here is, when you want to know when the text has been input/changed, you register an observer to the instance of the ConsoleFrame

console.addInputObsever(new InputObsever() {
    @Override
    public void inputChanged(InputEvent evt) {
        // Do what ever you need to do...
    }
});
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • I found something else that seems to work, too. Using a synchronized block(?) as discussed here: http://stackoverflow.com/a/5999146/3291305 I'm not sure if this is good practice, would it cause the same problems as you mentioned with Thead.sleep? Anyways, I'm marking yours as the answer because it seems to be the closest answer to what I was looking for. (Not to mention your comments on other post helped me too) Thanks! – hhaslam11 Aug 08 '15 at 15:17
  • *"Using a synchronized block(?) .. I'm not sure if this is good practice, would it cause the same problems as you mentioned with Thead.sleep?"* - Short answer is, yes. Anything which blocks the main thread will stop the UI, dead. – MadProgrammer Nov 20 '19 at 21:10
-1
    jTextField1.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
                yourFunction(jTextField1.getText());
                jTextField1.setText("");
        }
    });

Btw just a tip, you may append the JTextArea to get the feel of a console window

Rahul
  • 289
  • 2
  • 7
  • 1
    WHY!? The OP already has an `ActionListener` which will the same job and do in a cross platform manner!? As a general rule of thumb, it's a bad idea to attach `KeyListener`s` to text components, as there are any number of better solutions available to monitor for changes and provide filtering functionality! – MadProgrammer Aug 08 '15 at 03:57
  • @MadProgrammer I changed the code , is this okay now ? – Rahul Aug 08 '15 at 04:38
  • Okay, now you might want to explain to the OP "why" this method is any better the any other approach – MadProgrammer Aug 08 '15 at 04:39
  • I by mistake used the `KeyListener` cause I saw there was some checking functionality if `Enter` was pressed. Using `ActionListener` ,like it was in original code, it is not necessary to check if `Enter` was pressed or not. This approach is better because it is compact and nothing unnecessary is there. – Rahul Aug 08 '15 at 04:47