javaswingjtextpanemiglayout

Space under JTextPane text when using .setText method


I am using MigLayout 3.5.5, as the newer updates are not compatible with my older code.

Problem

When setting text to a JTextPane in a MigLayout, the JTextPane will take double the space (according to font size) IF the text I am setting the JTextPane contains space characters. It does not happen all the time, but in the specific program I am making, it happens frequently.

The program's goal is to present information in a letter-by-letter basis, so there is a button that updates the text to the next letter. However, the text bounces around, because the JTextPane is sometimes occupying more space than usual. I identified a certain pattern to the height differences.

Pattern

A new line indicates that I added a letter.
"|" represents a space character in the text.
"Space" means JTextPane is taking double the space.
Full String: "The quick brown fox jumps over the lazy dog."

T
Th
The
The|
The|q (Space)
The|qu
The|qui (Space)
The|quic
The|quick (Space)
The|quick|

Note: I stopped the pattern here, because from this point on (starting with The|quick|b), every single letter addition resulted in the JTextPane occupying double its height.

I've already tried printing out the letter-by-letter text to the console to see if there were any new line characters within the text being added, but to no avail. I also thought it might be a problem with the automatic wrapping of the JTextPane, but the text I inserted isn't quite long enough to wrap in the JFrame's size.

Here is a short example to reproduce the behavior:

public class MainFrame extends JFrame {

    int currentLetter = 1;
    final String FULL_TEXT = "The quick brown fox jumps over the lazy dog.";
    
    JTextPane text;
    JButton addLetter;
    
    MainFrame() {
        
        setSize(500, 500);
        setLayout(new MigLayout("align center, ins 0, gap 0"));
        addElements();
        
        setVisible(true);
        
    }
    
    public static void main(String[] args) {
        
        SwingUtilities.invokeLater(new Runnable() {
        
            public void run() {
                
                MainFrame application = new MainFrame();
                
            }
            
        });
        
    }
    
    private void addElements() {
        
        text = new JTextPane();
        text.setEditable(false);
        text.setFont(new Font("Times New Roman", Font.BOLD, 19));
        text.setForeground(Color.WHITE);
        text.setBackground(Color.BLACK);
        add(text, "alignx center, wmax 80%, gapbottom 5%");
        
        addLetter = new JButton("Add Letter");
        
        addLetter.addActionListener(new ActionListener() {
        
            public void actionPerformed(ActionEvent e) {
                
                if (currentLetter != FULL_TEXT.length()) {
                    
                    currentLetter++;
                    updateText();
                    
                }
                
            }
        
        });
        
        add(addLetter, "newline, alignx center");
        
        updateText();
        
    }
    
    private void updateText() {
        
        String partialText = new String();
        
        for (int letter = 0; letter < currentLetter; letter++) {
            
            partialText += FULL_TEXT.toCharArray()[letter];
            
        }
        
        text.setText(partialText);

    }

}

Why am I using JTextPane?

I tried using JLabel for this task, and it worked well... until the text was long enough to wrap. Then, when I used HTML within the JLabel text to wrap it, every time I updated the text, it would take time for the HTML to render and result in some pretty nasty visual effects.

Next, I tried JTextArea to disguise it as a JLabel, since it not only has line wrapping, but word wrapping as well. It was a great solution, until I found out that I couldn't use a center paragraph alignment in a JTextArea.

So I settled for a JTextPane, which will work well if only I got rid of the extra space at the bottom of it.

Thanks in advance for your help!


Solution

  • The solution is to append text by using the insertString() method on the StyledDocument of the JTextPane instead of using setText() on the JTextPane itself.

    For example, instead doing this every time:

    JTextPane panel = new JTextPane();
    panel.setText(panel.getText() + "test");
    

    You should do this:

    JTextPane panel = new JTextPane();
    StyledDocument document = panel.getStyledDocument();
    document.insertString(document.getLength(), "test", null);
    

    And of course you need to catch the BadLocationException.

    Then the space disappears. Here's the question where I found my answer to the rendering problem: JTextPane appending a new string

    The answers to those questions don't address the problem with the space, but they do show the correct way to edit text in the JTextPane.