Search code examples
javaswingjtextfieldkeylistener

why am i appending twice in jtextfield?


I have looked over this code and i don't know what is wrong. I keep getting incorrect outputs when i enter a command (any input). Please look at the bottom part of my code.

public class gui {

    private final static javax.swing.JFrame frame = new javax.swing.JFrame();
    private final static javax.swing.JPanel panel = new javax.swing.JPanel();
    public final static javax.swing.JTextArea outtextArea = new javax.swing.JTextArea("");
    public final static javax.swing.JTextField intextArea = new javax.swing.JTextField();

    public static void main(String[] args) {

        java.awt.Font font = new java.awt.Font(java.awt.Font.SANS_SERIF, java.awt.Font.PLAIN, 15);
        String command;

        /* Optional */
        frame.setTitle("Console");
        frame.setUndecorated(true);
        frame.getRootPane().setWindowDecorationStyle(javax.swing.JRootPane.FRAME); // COMMENT THIS OUT WHEN COMPLETE
        frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE); //if exit command, dispose() first
        frame.setBackground(java.awt.Color.BLACK);

        /* size */
        frame.setMinimumSize(new java.awt.Dimension(0b001011010100,0b000110000100));
        frame.setLocation(0,0);
        frame.setExtendedState(javax.swing.JFrame.MAXIMIZED_BOTH);

        /* Sets panel */
        panel.setBackground(java.awt.Color.PINK); // if sees pink, has error
        panel.setLayout(new java.awt.BorderLayout());
        panel.setSize(frame.getWidth(),frame.getHeight());

        /* Sets text area */
        //javax.swing.JScrollPane inscrollPane = new javax.swing.JScrollPane(intextArea);
        intextArea.setHorizontalAlignment(javax.swing.JTextField.LEFT);
        intextArea.setFont(font);
        intextArea.setBackground(java.awt.Color.BLACK);
        intextArea.setForeground(java.awt.Color.GREEN);
        intextArea.setFocusable(true);

        javax.swing.JScrollPane outscrollPane = new javax.swing.JScrollPane(outtextArea);
        outtextArea.setRows(10);
        outtextArea.setLineWrap(true);
        outtextArea.setFont(font);
        outtextArea.setBackground(java.awt.Color.BLUE);
        outtextArea.setForeground(java.awt.Color.GREEN);
        outtextArea.setEditable(false);

        /* Sets all necessary components */
        frame.add(panel);
        panel.add(outscrollPane,java.awt.BorderLayout.CENTER);
       // panel.add(inscrollPane,java.awt.BorderLayout.SOUTH);
        panel.add(intextArea,java.awt.BorderLayout.SOUTH);

        /* Adjusts components */
        frame.pack();
        frame.setVisible(true);

        //every time a command is entered, it is sent to handler and 
        //textbox should be cleared

// THIS BELOW IS WHERE THE PROBLEM LIES/////////////////////////////
        boolean keepGoing=true;
        while(keepGoing){
            command = intextArea.getText();
            String refactored;
            if(entering_a_command(command) && !command.equals("exit")){
                refactored=command.substring(0,command.length()-1);
                outtextArea.append(refactored+"\n");
                intextArea.setText("");
            }
            else if(!command.equals("exit")){//no need to read before submission
                outtextArea.append("");
                command=intextArea.getText();
            }
            else{
                outtextArea.append("EXITING\n");
                keepGoing=false;
            }
        }

    }
    /*
        Method is strictly for entering user input at appropriate time
    */
    private static boolean entering_a_command(String temp){
        //handler.print(temp);
        return temp.contains("="); //key to submit user input
    }
}

My input:

  • 12345=
  • 123456=
  • This is hell=
  • This is hello=

My EXPECTED output:

  • 12345
  • 123456
  • This is hell
  • This is hello

My ACTUAL output:

  • 12345
  • 12345
  • This is hell
  • This is hell

My problem: When i enter an input the first time, it all checks out. When i enter an input the second time, an input that has greater length than the first, it is automatically submitted just as if i had pressed the trigger key (=).

The input box is the black box in the bottom. To submit an input, press '='


Solution

  • The problem is that you're abusing the threading model. You shouldn't be accessing UI components in a thread other than the UI thread - and having a tight loop like this is pretty much always a bad idea. You should read about the Swing threading model. From that tutorial:

    Swing event handling code runs on a special thread known as the event dispatch thread. Most code that invokes Swing methods also runs on this thread. This is necessary because most Swing object methods are not "thread safe": invoking them from multiple threads risks thread interference or memory consistency errors.

    Instead, you should add an event listener to your text area. There are loads of options here, none of which is obviously ideal, unfortunately. Adding a key listener and handling keyTyped sounds good - but you get the event before the key ends up in the text area, which isn't ideal. Adding a document listener is a nice abstraction in that then it doesn't matter how the text is changed (e.g. programmatically) - but you can't mutate the document within a listener, so you can't clear it.

    As a starting point, adding a key listener and handling keyReleased works well at least for the simple case. Get rid of your current loop (and the unconventionally named entering_a_command method) and replace them with:

    intextArea.addKeyListener(new java.awt.event.KeyAdapter() {
        @Override public void keyReleased(java.awt.event.KeyEvent e) {
            String command = intextArea.getText();
            if (!command.contains("=")) {
                return;
            }
    
            command = command.substring(0, command.length() - 1);
            if (command.equals("exit")) {
                frame.setVisible(false);
                frame.dispose();
                return;
            }
            outtextArea.append(command + "\n");
            intextArea.setText("");
        }
    });