Search code examples
javaswingscrolljscrollpanejtextarea

Make JTextArea scroll so caret position is always displayed as user types more text


How to make JTextArea scroll so caret position is always displayed as user types more text?

This seems like a stupid question to me and I feel like it must have been asked before, yet I have searched and cannot find the answer.

I have a multiline text area, embedded in a JScrollPane. If the user keeps typing until he fills the text area, eventually the caret becomes invisible (below the area shown) and the user cannot see what he is typing. It seems odd that this would be the default behavior. What do I need to do to make sure the text area always scrolls to the line where the caret is.

I should mention that line wrap is enabled in the text area.


Solution

  • In my opinion, setXxXSize() methods are to be used with limited number of components and keeping in mind the Layout used behind, depending on which Layout Manager respects the sizing hints provided by the programmer.

    Hence for a component like JTextArea, whose Rows and Columns are sufficient enough, to provide a hint to the Layout concern to calculate its size, should not be associated with a hard coded size, using some setXxXSize() methods, as much as possible.

    Here I have provided a sample code for the same :

    import java.awt.*;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.beans.EventHandler;
    import javax.swing.*;
    
    public class DialogWithScroller
    {
        private JFrame frame;
        private JButton showButton;
        private MyDialog myDialog;
    
        private void displayGUI()
        {
            frame = new JFrame("Dialog ScrollPane Example");
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    
            JPanel contentPane = new JPanel(new BorderLayout(5, 5));
            showButton = new JButton("Show Dialog");
            showButton.addActionListener((ActionListener)
                    EventHandler.create(ActionListener.class,
                            DialogWithScroller.this, "buttonActions", ""));     
            contentPane.add(showButton);
    
            frame.setContentPane(contentPane);
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        }   
    
        public void buttonActions(ActionEvent ae)
        {
            myDialog = new MyDialog(frame
                    , "TextArea with ScrollPane", true);
        }
    
        public static void main(String[] args)
        {
            Runnable runnable = new Runnable()
            {
                @Override
                public void run()
                {
                    new DialogWithScroller().displayGUI();
                }
            };
            EventQueue.invokeLater(runnable);
        }
    }
    
    class MyDialog extends JDialog
    {
        private JTextArea tArea;
        private JButton hideButton;
    
        private ActionListener buttonActions =
                                new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent ae)
            {
                MyDialog.this.dispose();
            }
        };
    
        public MyDialog()
        {}
    
        public MyDialog(Frame owner, String title, boolean modal)
        {
            super(owner, title, modal);
            displayGUI();
        }
    
        private void displayGUI()
        {
            JPanel contentPane = new JPanel(
                            new BorderLayout(5, 5));
            contentPane.setBorder(
                    BorderFactory.createTitledBorder(
                                "My Personal Text Area"));
            /*
             * Here one can simply initialize the
             * JTextArea like this too, using the
             * constructor itself for specifying
             * the Rows and Columns, which will
             * help the layout concern to determine
             * its size
             */
            tArea = new JTextArea(20, 20);
            tArea.setLineWrap(true);
            tArea.setWrapStyleWord(true);
            JScrollPane textScroller = new JScrollPane(tArea);
            //textScroller.setViewportView(tArea);
    
            hideButton = new JButton("Hide Dialog");
            hideButton.addActionListener(buttonActions);
    
            contentPane.add(textScroller, BorderLayout.CENTER);
            contentPane.add(hideButton, BorderLayout.PAGE_END);
            setContentPane(contentPane);
            pack();
            setLocationByPlatform(true);
            setVisible(true);
        }   
    }
    

    It is always considered to be a wise practice to call Window.pack(), on the container, which causes this Window to be sized to fit the preferred size and layouts of its subcomponents. Though calls like this, must be made after the programmer is done adding all components to the container and before setting the container to the visible state.