0

I have a custom layout where the principal behavior is to grow and shrink a child JTextArea when the JScrollPane it's in changes in width. The scroll pane has the horizontal scroll bar disabled and the text area is supposed to expand and contract so as to avoid needing a horizontal scroll bar. For a number of months, I worked around this using one of the standard layout managers, but now I need some different functionality.

What's happening is that when the user expands horizontally the scroll pane, the layout manager layoutContainer method is called. It resizes the text area and the text reflows properly. However, when you shrink the scroll pane, layoutContainer is not called and the text area stays fixed. I've put some printlns in the layoutContainer method to make it obvious when it's working and not.

The essential thing to note is that the problem happens when JTextArea.setColumns() is called. I can comment it out and the layoutContainer gets called during resizing (of course, then the text area doesn't get resized.) I've tried also using JTextArea.setSize(), with the same results.

Here's the code:

//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
@SuppressWarnings("serial")
class XTextArea extends JTextArea
{
    XTextArea (String text)
    {
        super (text);
    }

    public int getColumnWidth()
    {
        return super.getColumnWidth();
    }
}

//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
class PackLeftLayout implements LayoutManager
{
    Component viewPort;
    Component flexWidthComponent;

    int preferredWidth = 0;
    int preferredHeight = 0;

    //----------------------------------------------------------------------------
    // viewPort - if null, compute width as sum of component's preferred width;
    //  otherwise width will be the viewPort's width.
    // flexWidthComponent - if not null, this component width will be sized to right
    //   justify rightmost component.
    public PackLeftLayout (Component viewPort, Component flexWidthComponent)
    {
        super ();
        this.viewPort = viewPort;
        this.flexWidthComponent = flexWidthComponent;
    }

    //----------------------------------------------------------------------------
    public void addLayoutComponent(String name, Component comp)
    {
    }

    //----------------------------------------------------------------------------
    public void removeLayoutComponent(Component comp)
    {
    }

    //----------------------------------------------------------------------------
    // Calculates the preferred size dimensions for the specified container, given the
    // components it contains.
    // parent - the container to be laid out
    // Components layed out left-to-right with no additional spacing.
    public Dimension preferredLayoutSize (Container parent)
    {
        Insets insets = parent.getInsets();
        int width = 0;
        int height = 0; // will become max of all component's preferred height

        // calculate sum of fixed width components - skip the flexwidth component
        width = insets.left + insets.right;
        for (int i = 0, limit = parent.getComponentCount();  i < limit;  i++)
        {
            Component c = parent.getComponent(i);
            if (c.isVisible())
            {
                if (c != flexWidthComponent)
                {
                    Dimension size = c.getPreferredSize();
                    if (size.height > height)
                    height = size.height;
                    width += size.width;
                }
            }
        }

        // determine width of flex width component
        if (flexWidthComponent != null)
        {
            int flexWidth = viewPort.getWidth() - width;
            if (flexWidth < 1)
                flexWidth = 1;

            if (flexWidthComponent instanceof XTextArea)
            {
                // some trickery here to get the xtextarea to tell us its preferred height
                // given a specific width.
                int colWidth = ((XTextArea)flexWidthComponent).getColumnWidth();

                // the following line causes the failure:
                ((XTextArea)flexWidthComponent).setColumns (flexWidth / colWidth);

                Dimension taSize = flexWidthComponent.getPreferredSize();
                width += taSize.width;

                if (taSize.height > height)
                    height = taSize.height;
            }
            else
            {
                Dimension size = flexWidthComponent.getPreferredSize();
                width += flexWidth;
                if (size.height > height)
                    height = size.height;
            }
        }

        preferredWidth = width; // already include insets
        preferredHeight = height + insets.top + insets.bottom;

        return new Dimension (preferredWidth, preferredHeight);
    }

    //----------------------------------------------------------------------------
    // Calculates the minimum size dimensions for the specified container, given the
    // components it contains.
    // parent - the component to be laid out
    public Dimension minimumLayoutSize(Container parent)
    {
      return new Dimension (10, 10); //???
    }

    static int k = 0;
    //----------------------------------------------------------------------------
    public void layoutContainer(Container parent)
    {
        System.out.println ("layout" + (k++));
        Insets insets = parent.getInsets();
        int left = insets.left;

        if (preferredWidth == 0 || preferredHeight == 0)
            preferredLayoutSize (parent);

        for (int i = 0, limit = parent.getComponentCount();  i < limit;  i++)
        {
            Component c = parent.getComponent(i);
            Dimension size = c.getPreferredSize();

            c.setBounds (left, insets.top, size.width, preferredHeight);

            left += size.width;
        }

        // force another layout calc
        preferredWidth = 0;
    }
}

public class ResizablePane extends JFrame
{

    public static void main(String[] args)
    {
        javax.swing.SwingUtilities.invokeLater(
            new Runnable() {public void run()
            {
                new ResizablePane();
            }});
    }

    ResizablePane ()
    {
        super ("ResizableDemo");

        // put a button and text area into a panel, then into a scroll pane
        JButton button = new JButton ("button");
        XTextArea text = new XTextArea (
            "For three years I ran as fast as I could, trying to live and love and learn at " +
            "double speed to make up for what Anne-Marie lost.  Trying to anesthetize myself " +
            "from what Id lost.  When I decided to read a book a day and write about it, Id " +
            "finally stopped running away.");

        text.setLineWrap (true);
        text.setWrapStyleWord (true);

        JScrollPane scroll = new JScrollPane();
        JPanel panel = new JPanel();
        panel.setLayout (new PackLeftLayout(scroll.getViewport(), text));
        panel.add (button);
        panel.add (text);

        scroll.setHorizontalScrollBarPolicy (ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        scroll.setViewportView (panel);

        getContentPane().add(scroll);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
        setVisible(true);
    }
}
Dada
  • 6,313
  • 7
  • 24
  • 43
Peri Hartman
  • 19,314
  • 18
  • 55
  • 101
  • *"The essential thing to note is that"* there is no question in that mess of words. – Andrew Thompson Nov 01 '11 at 04:42
  • See also [`TextPanePerfectSize`](http://stackoverflow.com/questions/3315681/how-can-i-measure-calculate-the-size-a-document-needs-to-render-itself/3318949#3318949). – trashgod Nov 01 '11 at 05:51
  • I think this may be a bug somewhere in the interaction between the text area and JScrollPane. I discovered that the ta will shrink if a horizontal scroll bar is allowed. In otherwords, in my original code I have the line setHorizontalScrollBarPolicy (ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); If I remove this line the ta starts working... sort of. It works as long as the scroll bar is hidden. – Peri Hartman Nov 08 '11 at 15:58
  • Andrew, the question is "why won't the ta recalc its height and width when the enclosing viewport shrinks horizontally". Sorry if that wasn't clear. – Peri Hartman Nov 08 '11 at 16:49
  • I want to have my code do something very similar - I'm making a JScrollPane that will hold multiple text areas in my case, and I would like the text areas to automagically size to the width of the window, then have a static height, so my scroll pane only has a vertical scroll bar. I was hoping there might be a built in layout manager for this... – gcode Sep 17 '12 at 17:55

2 Answers2

2

JTextArea wont shrink inside JPanel

You need to set the minimum size of the text area:

textArea.SetMinimumSize(new Dimension(100,100));

ps. I'm Using GridLayout in my panel with just the one component.

bob
  • 35
  • 6
0

This is old question, but i hope that it will be usefull to someone. For me worked:

jScrollPane = new JScrollPane(textArea); jScrollPane.setPreferredSize(jScrollPane.getPreferredSize());

paka
  • 434
  • 1
  • 4
  • 9