Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JDK 11 - line-wrapping JTextArea inside JScrollPane "continuously grows" #558

Closed
Sciss opened this issue Oct 13, 2019 · 14 comments
Closed

Comments

@Sciss
Copy link
Contributor

Sciss commented Oct 13, 2019

This occurs on JDK 11 not JDK 8, perhaps just because of different font sizes.

package de.sciss.submin;

import com.alee.laf.WebLookAndFeel;

import javax.swing.GroupLayout;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.WindowConstants;
import java.awt.EventQueue;

public class TextAreaRunaway implements Runnable {
    public static void main(String[] args) {
        EventQueue.invokeLater(new TextAreaRunaway());
    }

    private JTextArea textArea(int rows) {
        final JTextArea res = new JTextArea(rows, 12);
        res.setEditable(false);
        res.setWrapStyleWord(true);
        res.setLineWrap(true);
        return res;
    }

    @Override
    public void run() {
        WebLookAndFeel.install();
        final JComponent ggTags         = textArea(2);
        final JComponent ggDescription  = textArea(4);
        final JComponent lbTags         = new JLabel("Tags"       );
        final JComponent lbDescription  = new JLabel("Description");
        final JPanel p = new JPanel();
        final GroupLayout lay = new GroupLayout(p);
        lay.setAutoCreateGaps(true);
        p.setLayout(lay);
        GroupLayout.SequentialGroup hGroup = lay.createSequentialGroup();
        hGroup.addGroup(lay.createParallelGroup().
                addComponent(lbTags).addComponent(lbDescription));
        hGroup.addGroup(lay.createParallelGroup().
                addComponent(ggTags).addComponent(ggDescription));
        lay.setHorizontalGroup(hGroup);
        GroupLayout.SequentialGroup vGroup = lay.createSequentialGroup();
        vGroup.addGroup(lay.createParallelGroup(GroupLayout.Alignment.BASELINE).
                addComponent(lbTags).addComponent(ggTags));
        vGroup.addGroup(lay.createParallelGroup(GroupLayout.Alignment.BASELINE).
                addComponent(lbDescription).addComponent(ggDescription));
        lay.setVerticalGroup(vGroup);

        final JScrollPane scroll = new JScrollPane(p);

        final JFrame f = new JFrame();
        f.getContentPane().add(scroll);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        f.setVisible(true);
    }
}

Demo: https://peertube.social/videos/watch/ba775afa-f349-4f93-ba15-7f098975b4ae

@Sciss
Copy link
Contributor Author

Sciss commented Oct 13, 2019

A workaround for now is not to use GroupLayout but, for example, GridBagLayout ( :oh_no: )

        final GridBagConstraints con = new GridBagConstraints();
        final GridBagLayout lay = new GridBagLayout();
        p.setLayout(lay);
        con.gridwidth   = 1;
        con.weightx     = 0.0;
        con.fill        = GridBagConstraints.NONE;
        lay.setConstraints(lbTags, con);
        con.gridwidth   = GridBagConstraints.REMAINDER;
        con.weightx     = 1.0;
        con.fill        = GridBagConstraints.BOTH;
        lay.setConstraints(ggTags, con);
        con.gridwidth   = 1;
        con.weightx     = 0.0;
        con.fill        = GridBagConstraints.NONE;
        lay.setConstraints(lbDescription, con);
        con.gridwidth   = GridBagConstraints.REMAINDER;
        con.weightx     = 1.0;
        con.fill        = GridBagConstraints.BOTH;
        lay.setConstraints(ggDescription, con);
        p.add(lbTags);
        p.add(ggTags);
        p.add(lbDescription);
        p.add(ggDescription);

@mgarin mgarin self-assigned this Oct 13, 2019
@mgarin mgarin added this to the v1.2.11 milestone Oct 13, 2019
@mgarin
Copy link
Owner

mgarin commented Oct 13, 2019

Yep, we've already noticed this behavior on our internal project and already fixed it in v1.2.11.

I'll double-check it tomorrow, but generally this happens because of the new tricky logic behind the "extending" scrolls bars made for the hovering styles. Unfortunately there is no good way to fix it for some cases, so hovering styles will remain optional and might not be working correctly for things with variable width like JTextArea with wrapping.

@mgarin
Copy link
Owner

mgarin commented Oct 13, 2019

Also, just a side-note, it's not a JDK issue - it's a scroll pane & viewport layout issue.

@mgarin
Copy link
Owner

mgarin commented Oct 14, 2019

I re-checked this on current v1.2.11 code - it does seem to be fixed for default JScrollPane style as it doesn't use width extension anymore.

@mgarin mgarin closed this as completed Oct 14, 2019
@Sciss
Copy link
Contributor Author

Sciss commented Oct 16, 2019

I'm still seeing the same behaviour, so it's not fixed. My guess is it depends a bit on actual font sizes and such, so it may be that you don't see it any longer on your system.

@mgarin mgarin reopened this Oct 16, 2019
@mgarin
Copy link
Owner

mgarin commented Oct 16, 2019

I'll try it out with some different settings

@mgarin
Copy link
Owner

mgarin commented Oct 16, 2019

And just so we're clear - you're not talking about the fact that text areas/scroll pane from the example, when frame resized bigger and then made smaller, do not return to original size? Because this is a common Swing behavior, here is the same example from your post without WebLaF:

public class TextAreaRunaway implements Runnable
{
    public static void main ( String[] args )
    {
        EventQueue.invokeLater ( new TextAreaRunaway () );
    }

    private JTextArea textArea ( int rows )
    {
        final JTextArea res = new JTextArea ( rows, 12 );
        res.setEditable ( false );
        res.setWrapStyleWord ( true );
        res.setLineWrap ( true );
        return res;
    }

    @Override
    public void run ()
    {
        final JComponent ggTags = textArea ( 2 );
        final JComponent ggDescription = textArea ( 4 );
        final JComponent lbTags = new JLabel ( "Tags" );
        final JComponent lbDescription = new JLabel ( "Description" );
        final JPanel p = new JPanel ();
        final GroupLayout lay = new GroupLayout ( p );
        lay.setAutoCreateGaps ( true );
        p.setLayout ( lay );
        GroupLayout.SequentialGroup hGroup = lay.createSequentialGroup ();
        hGroup.addGroup ( lay.createParallelGroup ().
                addComponent ( lbTags ).addComponent ( lbDescription ) );
        hGroup.addGroup ( lay.createParallelGroup ().
                addComponent ( ggTags ).addComponent ( ggDescription ) );
        lay.setHorizontalGroup ( hGroup );
        GroupLayout.SequentialGroup vGroup = lay.createSequentialGroup ();
        vGroup.addGroup ( lay.createParallelGroup ( GroupLayout.Alignment.BASELINE ).
                addComponent ( lbTags ).addComponent ( ggTags ) );
        vGroup.addGroup ( lay.createParallelGroup ( GroupLayout.Alignment.BASELINE ).
                addComponent ( lbDescription ).addComponent ( ggDescription ) );
        lay.setVerticalGroup ( vGroup );

        final JScrollPane scroll = new JScrollPane ( p );

        final JFrame f = new JFrame ();
        f.getContentPane ().add ( scroll );
        f.pack ();
        f.setLocationRelativeTo ( null );
        f.setDefaultCloseOperation ( WindowConstants.EXIT_ON_CLOSE );
        f.setVisible ( true );
    }
}

Result:
image
image
image
This particular problem happens due to how JTextArea interacts with such layouts. Kind of "it's not a bug, it's a feature" case for the Swing.

You can easily fix it by removing preferred width of the component inside the scroll:

public class TextAreaRunaway implements Runnable
{
    public static void main ( String[] args )
    {
        EventQueue.invokeLater ( new TextAreaRunaway () );
    }

    private JTextArea textArea ( int rows )
    {
        final JTextArea res = new JTextArea ( rows, 12 );
        res.setEditable ( false );
        res.setWrapStyleWord ( true );
        res.setLineWrap ( true );
        return res;
    }

    @Override
    public void run ()
    {
        final JComponent ggTags = textArea ( 2 );
        final JComponent ggDescription = textArea ( 4 );
        final JComponent lbTags = new JLabel ( "Tags" );
        final JComponent lbDescription = new JLabel ( "Description" );
        final JPanel p = new JPanel ()
        {
            public Dimension getPreferredSize ()
            {
                Dimension ps = super.getPreferredSize ();
                ps.width = 0;
                return ps;
            }
        };
        final GroupLayout lay = new GroupLayout ( p );
        lay.setAutoCreateGaps ( true );
        p.setLayout ( lay );
        GroupLayout.SequentialGroup hGroup = lay.createSequentialGroup ();
        hGroup.addGroup ( lay.createParallelGroup ().
                addComponent ( lbTags ).addComponent ( lbDescription ) );
        hGroup.addGroup ( lay.createParallelGroup ().
                addComponent ( ggTags ).addComponent ( ggDescription ) );
        lay.setHorizontalGroup ( hGroup );
        GroupLayout.SequentialGroup vGroup = lay.createSequentialGroup ();
        vGroup.addGroup ( lay.createParallelGroup ( GroupLayout.Alignment.BASELINE ).
                addComponent ( lbTags ).addComponent ( ggTags ) );
        vGroup.addGroup ( lay.createParallelGroup ( GroupLayout.Alignment.BASELINE ).
                addComponent ( lbDescription ).addComponent ( ggDescription ) );
        lay.setVerticalGroup ( vGroup );

        final JScrollPane scroll = new JScrollPane ( p );

        final JFrame f = new JFrame ();
        f.getContentPane ().add ( scroll );
        f.pack ();
        f.setLocationRelativeTo ( null );
        f.setDefaultCloseOperation ( WindowConstants.EXIT_ON_CLOSE );
        f.setVisible ( true );
    }
}

Although that might cause some other issues depending on what you want out of that particular layout of components.

@mgarin
Copy link
Owner

mgarin commented Oct 16, 2019

Also this behavior is not limited to JTextArea only an might happen in any component that has a specific variable preferred width calculation.

@Sciss
Copy link
Contributor Author

Sciss commented Oct 16, 2019

Yes, it's not with me interacting, I just run the program, the frame opens, the component starts to grow and grow, like shown in the video link.

@mgarin
Copy link
Owner

mgarin commented Oct 16, 2019

I've tried that sample on a few different systems on the latest snapshot version of v1.2.11 and it works exactly the same everywhere. I tried a different Font as well and result is the same, although that should never really have any effect on how layout is working in the first place, only on the calculated sizes.

@mgarin
Copy link
Owner

mgarin commented Oct 16, 2019

Actually, I take it back, it seems that it doesn't work the same way on different JDK 11 versions. I've got it reproduced now, will try to find what exactly is different there.

@mgarin mgarin added the JDK 11 label Oct 16, 2019
@Sciss
Copy link
Contributor Author

Sciss commented Oct 16, 2019

My JDK is 11.0.4+11-post-Debian-1deb10u1 - that's the OpenJDK package for Debian stable. Haven't tried with AdoptJDK yet.

macstrace added a commit that referenced this issue Oct 17, 2019
- AbstractTextEditorPainter.java - Fixed size issue for JDK9+
@mgarin
Copy link
Owner

mgarin commented Oct 17, 2019

We've tracked the source of this issue - there was a single "runaway" pixel missing in text component view painting bounds that caused this particular behavior (endless expansion) in particular layout/constraints (GroupLayout in this case) and on a particular Java version (Java 9+).

Take even one of the conditions mentioned above and everything would work perfectly fine even though bounds were assigned incorrectly (1px more than necessary was given to View element width).

The reason that happened was addition of caret width setting in JDK 9 and change of some internal layouting happening within View. AbstractTextEditorPainter that paints the View in WebLaF (instead of UIs in other L&Fs) was still using older bounds calculation and that caused the problem.

Fix is now out and snapshot build should be available shortly.

@mgarin mgarin closed this as completed Oct 17, 2019
@mgarin
Copy link
Owner

mgarin commented Oct 17, 2019

Actually, snapshot version is already available.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants