1

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.

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Steve Cohen
  • 4,679
  • 9
  • 51
  • 89
  • 1
    Can you post an [SSCCE](http://www.sscce.org) of what you have tried? – Bnrdo Jul 11 '13 at 02:02
  • 1
    What you asking for is actually the default behaviour :-) You must be doing something wrong in your code :-) My guess is, you must be using `setBounds()/setXxXSize`() methods in your code. Have a look at this [code example](http://stackoverflow.com/a/17510427/1057230) and see if this is not the default behaviour. – nIcE cOw Jul 11 '13 at 05:56
  • Not posting the source code because it's a mess I inherited. Thanks nicE cOw! I was hoping someone would tell me that my desired behavior was the default. In your code example, I see: notesArea = new JTextArea(10, 10); notesScroller = new JScrollPane(); ... notesScroller.setViewportView(notesArea); Is there a difference between associating the text area with the scroller by calling setViewportView(textArea) or passing the text Area to the constructor? – Steve Cohen Jul 11 '13 at 13:56
  • Why can't you post source-formatted code in comments? – Steve Cohen Jul 11 '13 at 13:59
  • 1
    @SteveCohen : Do watch, how I am addressing this message to you, with @ sign and your name, previously couldn't get your message, since in the absence of the same, just visiting this page by chance :-). Actually there is no difference between the two thingies, I guess, they both are the same, I just prefer `setViewportView(...)`, to do my stuff :-). And yeah, for codes in comments, you can enclose the contents in between the Acute key(under Escape, without pressing Shift (With Shift it becomes tilde)), like this gray area `I AM CODE :-)`. For the rest You're MOST WELCOME and KEEP SMILING :-) – nIcE cOw Jul 11 '13 at 17:04
  • This time I did get your message in my inbox :-), though do watch, the space between two words, when referring, see your name in my comments and see my name in your last comment (you added a space), which might can lead to obnoxious results if two people will have that same name till that point, so no spaces are to be included while using @NameOfThePerson , even though the Person's Name is : Name Of The Person – nIcE cOw Jul 12 '13 at 03:08
  • @nIcEcOw - I think this is the right way. Ok, the problem was the use of setXxSize(). The TextArea had both setMinimumSize() and setPreferredSize() called on it (to the same size). The JScrollPane was embedded in a JDialog and on this setMinimumSize() was called. What I find is that by commenting out the JDialog.setMimimumSize(), scrolling now behaves properly, however the dialog opens at a minimum size that the user must expand before he can see it. So what is the solution to this? – Steve Cohen Jul 12 '13 at 17:23
  • @SteveCohen: Had you tried calling `JDialog.pack();` ? Do remember, this call must just come before setting `JDialog` to visible, though after adding all the components to it :-). Moreover, the rows and columns you specify while initializing `JTextArea` are sufficient enough to determine its size. – nIcE cOw Jul 12 '13 at 17:36
  • 1
    @nIcEcOw - thank you again, nIcEcOw. setRows(), pack() who'd a thunk it. We weren't calling either. If you would put this in an answer I'd be happy to give you credit for it. Problem solved! – Steve Cohen Jul 12 '13 at 20:36
  • @SteveCohen : I just remembered, that one of the Swing Gurus on stackoverflow "camickr" has once told me the advantage of `scroller.setViewportView(...)`, which being, if you are adding or removing a component from already visible `JScrollPane`, then you don't have to do `add/remove/revalidate/repaint` sort of thingies with this. Simply writing `scroller.setViewportView(newComponent)` will do everything internally for that matter. – nIcE cOw Jul 14 '13 at 05:42

1 Answers1

1

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.

nIcE cOw
  • 24,468
  • 7
  • 50
  • 143