I want to implement a TextArea
with a sidebar that shows the index of the leftmost character in each line of a wrapped text. This is implemented with a JScrollPane
using RowHeader
showing a JTextPane
for the numbers and Viewport
showing a JTextArea
for the sequence.
All works well except that the RowHeader
and Viewport
are out of sync when resizing horizontally. In the following SSCCE it also happens at startup.
The picture illustrates this: The cursor of the JTextArea
is at the first position while the lower end of JTextPane
is shown.
I know it has something to do with the JTextPane
changing its content dynamically because it works with static content.
Is it possible to prevent this odd behaviour?
package main;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Collections;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.Utilities;
public class SequenceAreaExample {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(150, 200);
SequenceArea sequenceArea = new SequenceArea();
frame.add(sequenceArea);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.setVisible(true);
sequenceArea.setText(String.join("", Collections.nCopies(200, "a")));
}
}
class SequenceArea extends JScrollPane implements ComponentListener {
private static final long serialVersionUID = 1L;
private JTextArea text;
private JTextPane numbers;
// FIXME text and numbers are out of sync when resizing horizontally
SequenceArea() {
text = new JTextArea();
text.setLineWrap(true);
text.setEnabled(true);
text.addComponentListener(this);
numbers = new JTextPane();
numbers.setEnabled(false);
setViewportView(text);
setRowHeaderView(numbers);
}
void updateNumbers() {
StringBuilder newNumbers = new StringBuilder();
int length = text.getText().length();
int index = 0;
while (index < length) {
try {
int start = Utilities.getRowStart(text, index);
int end = Utilities.getRowEnd(text, index);
newNumbers.append(start);
newNumbers.append('\n');
index = end + 1;
} catch (BadLocationException e) {
break;
}
}
numbers.setText(newNumbers.toString());
}
void setText(String t) {
text.setText(t);
}
@Override
public void componentResized(ComponentEvent e) {
updateNumbers();
}
@Override
public void componentMoved(ComponentEvent e) {}
@Override
public void componentShown(ComponentEvent e) {}
@Override
public void componentHidden(ComponentEvent e) {}
}