Странная упаковка текста со стилизованным текстом в JTextPane с Java 7
У меня есть два разных редактора, использующих JTextPane со странными ошибками в Java 7, которые не произошли с предыдущими версиями JVM. Это происходит с длинными строками, содержащими стилизованный текст или компоненты.
Вот пример, демонстрирующий эту ошибку. В этом примере стиль по умолчанию применяется для всего текста при каждой вставке символа. Я протестировал его с JDK 1.7.0_04.
import java.awt.BorderLayout;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
public class BugWrapJava7 extends JFrame {
JTextPane jtp;
StyledDocument doc;
public BugWrapJava7() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
jtp = new JTextPane();
add(jtp, BorderLayout.CENTER);
jtp.setText("ntype some text in the above empty line and check the wrapping behavior");
doc = jtp.getStyledDocument();
doc.addDocumentListener(new DocumentListener() {
public void insertUpdate(DocumentEvent e) {
insert();
}
public void removeUpdate(DocumentEvent e) {
}
public void changedUpdate(DocumentEvent e) {
}
});
setSize(200, 200);
setVisible(true);
}
public void insert() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Style defaultStyle = jtp.getStyle(StyleContext.DEFAULT_STYLE);
doc.setCharacterAttributes(0, doc.getLength(), defaultStyle, false);
}
});
}
public static void main(String[] args) {
new BugWrapJava7();
}
}
Мой вопрос: есть ли что-то неправильное в моем коде, или это действительно новая ошибка, введенная в Java 7 ? И если это новая ошибка JVM, есть ли обходной путь ?
Это может быть связано с вопросом 8666727, но проблема здесь заключается в неправильном обертывании, а не в появлении полосы прокрутки.
2 ответа:
Для читателей фьючерсов, ошибка все еще присутствует в JDK 1.7.0_04.,
Сравнивая Java7 и стабильный Java6,
Из кода
import java.awt.Dimension; import javax.swing.*; import javax.swing.event.*; import javax.swing.text.*; public class BugWrapJava7 { private JFrame frame = new JFrame(); private JTextPane jtp; private StyledDocument doc; public BugWrapJava7() { jtp = new JTextPane(); jtp.setText("\ntype some text in the above empty line and check the wrapping behavior"); doc = jtp.getStyledDocument(); doc.addDocumentListener(new DocumentListener() { public void insertUpdate(DocumentEvent e) { insert(); } public void removeUpdate(DocumentEvent e) { insert(); } public void changedUpdate(DocumentEvent e) { insert(); } public void insert() { SwingUtilities.invokeLater(new Runnable() { public void run() { Style defaultStyle = jtp.getStyle(StyleContext.DEFAULT_STYLE); doc.setCharacterAttributes(0, doc.getLength(), defaultStyle, false); } }); } }); JScrollPane scroll = new JScrollPane(jtp); scroll.setPreferredSize(new Dimension(200, 200)); frame.add(scroll); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { BugWrapJava7 bugWrapJava7 = new BugWrapJava7(); } }); } }
Исследовал это. Причина-кэширование точек останова. Похоже, что
LabelView
хранит их и не пересчитывает смещения при редактировании текста previos. Если я сброшу их вручную, ошибки не произойдет.Обходной путь (очень грязный из-за частных полей точек останова) следующий
import java.awt.Dimension; import java.lang.reflect.Field; import javax.swing.*; import javax.swing.event.*; import javax.swing.text.*; public class BugWrapJava7 { private JFrame frame = new JFrame(); private JTextPane jtp; private StyledDocument doc; public BugWrapJava7() { jtp = new JTextPane(); jtp.setEditorKit(new MyStyledEditorKit()); jtp.setText("\ntype some text in the above empty line and check the wrapping behavior"); doc = jtp.getStyledDocument(); doc.addDocumentListener(new DocumentListener() { public void insertUpdate(DocumentEvent e) { insert(); } public void removeUpdate(DocumentEvent e) { insert(); } public void changedUpdate(DocumentEvent e) { insert(); } public void insert() { SwingUtilities.invokeLater(new Runnable() { public void run() { Style defaultStyle = jtp.getStyle(StyleContext.DEFAULT_STYLE); doc.setCharacterAttributes(0, doc.getLength(), defaultStyle, false); } }); } }); JScrollPane scroll = new JScrollPane(jtp); scroll.setPreferredSize(new Dimension(200, 200)); frame.add(scroll); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { BugWrapJava7 bugWrapJava7 = new BugWrapJava7(); } }); } } class MyStyledEditorKit extends StyledEditorKit { private MyFactory factory; public ViewFactory getViewFactory() { if (factory == null) { factory = new MyFactory(); } return factory; } } class MyFactory implements ViewFactory { public View create(Element elem) { String kind = elem.getName(); if (kind != null) { if (kind.equals(AbstractDocument.ContentElementName)) { return new MyLabelView(elem); } else if (kind.equals(AbstractDocument.ParagraphElementName)) { return new ParagraphView(elem); } else if (kind.equals(AbstractDocument.SectionElementName)) { return new BoxView(elem, View.Y_AXIS); } else if (kind.equals(StyleConstants.ComponentElementName)) { return new ComponentView(elem); } else if (kind.equals(StyleConstants.IconElementName)) { return new IconView(elem); } } // default to text display return new LabelView(elem); } } class MyLabelView extends LabelView { public MyLabelView(Element elem) { super(elem); } public View breakView(int axis, int p0, float pos, float len) { if (axis == View.X_AXIS) { resetBreakSpots(); } return super.breakView(axis, p0, pos, len); } private void resetBreakSpots() { try { // HACK the breakSpots private fields Field f=GlyphView.class.getDeclaredField("breakSpots"); f.setAccessible(true); f.set(this, null); } catch (Exception e) { e.printStackTrace(); } } }
Меньше рубить без отражения. Основан на обычном сбросе точек пробоя при смене модели.
class MyLabelView extends LabelView { boolean isResetBreakSpots=false; public MyLabelView(Element elem) { super(elem); } public View breakView(int axis, int p0, float pos, float len) { if (axis == View.X_AXIS) { resetBreakSpots(); } return super.breakView(axis, p0, pos, len); } private void resetBreakSpots() { isResetBreakSpots=true; removeUpdate(null, null, null); isResetBreakSpots=false; // try { // Field f=GlyphView.class.getDeclaredField("breakSpots"); // f.setAccessible(true); // f.set(this, null); // } catch (Exception e) { // e.printStackTrace(); // } } public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) { super.removeUpdate(e, a, f); } public void preferenceChanged(View child, boolean width, boolean height) { if (!isResetBreakSpots) { super.preferenceChanged(child, width, height); } } }
Обновление: это исправляет TextSamplerDemo, а также. Я сбрасываю все пятна для всех меток Просмотры.
class MyStyledEditorKit extends StyledEditorKit { private MyFactory factory; public ViewFactory getViewFactory() { if (factory == null) { factory = new MyFactory(); } return factory; } } class MyFactory implements ViewFactory { public View create(Element elem) { String kind = elem.getName(); if (kind != null) { if (kind.equals(AbstractDocument.ContentElementName)) { return new MyLabelView(elem); } else if (kind.equals(AbstractDocument.ParagraphElementName)) { return new MyParagraphView(elem); } else if (kind.equals(AbstractDocument.SectionElementName)) { return new BoxView(elem, View.Y_AXIS); } else if (kind.equals(StyleConstants.ComponentElementName)) { return new ComponentView(elem); } else if (kind.equals(StyleConstants.IconElementName)) { return new IconView(elem); } } // default to text display return new LabelView(elem); } } class MyParagraphView extends ParagraphView { public MyParagraphView(Element elem) { super(elem); } public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) { super.removeUpdate(e, a, f); resetBreakSpots(); } public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) { super.insertUpdate(e, a, f); resetBreakSpots(); } private void resetBreakSpots() { for (int i=0; i<layoutPool.getViewCount(); i++) { View v=layoutPool.getView(i); if (v instanceof MyLabelView) { ((MyLabelView)v).resetBreakSpots(); } } } } class MyLabelView extends LabelView { boolean isResetBreakSpots=false; public MyLabelView(Element elem) { super(elem); } public View breakView(int axis, int p0, float pos, float len) { if (axis == View.X_AXIS) { resetBreakSpots(); } return super.breakView(axis, p0, pos, len); } public void resetBreakSpots() { isResetBreakSpots=true; removeUpdate(null, null, null); isResetBreakSpots=false; } public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) { super.removeUpdate(e, a, f); } public void preferenceChanged(View child, boolean width, boolean height) { if (!isResetBreakSpots) { super.preferenceChanged(child, width, height); } } }