Strange text wrapping with styled text in JTextPane with Java 7

前端 未结 3 1437
暖寄归人
暖寄归人 2020-12-05 15:31

I have two different editors using JTextPane with strange bugs in Java 7 that did not occur with the previous JVM versions. It happens with long lines containing styled text

相关标签:
3条回答
  • 2020-12-05 15:50

    My HTMLDocument was wrapping-by-letter. I installed StanislavL's above solution. No joy, still wrapping-by-letter inside <td> elements. So I fixed a bug in StanislavL's code. Joy; wrapping-by-word everywhere. Then I discovered that my program wasn't using the StanislavL code! It seems that my tinkering fixed the same bug in my own code.

    The problem is that there are two different kinds of Element (in javax.swing.text and javax.swing.text.html.parser) and both have a tag type name with the string value "content". Method Element.getName confuses this by returning a String instead of an Object.

    So where the code says ".equals()"

        String kind = elem.getName();
        if (kind != null) {
            if (kind.equals(AbstractDocument.ContentElementName)) {
        ...
    

    it should say "=="

        Object absDocName = attrs.getAttribute(AbstractDocument.ElementNameAttribute)
        if (absDocName != null){
            if (kind == AbstractDocument.ContentElementName) {
        ...
    
    0 讨论(0)
  • 2020-12-05 16:06

    Investigated this. The reason is breakSpots caching. Looks like LabelView stores them and don't recalculate offsets on previos text edit. If I reset them manually the bug does not happen.

    A workaround (very dirty because of private breakSpots fields) is following

    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();
            }
        }
    
    }
    

    Less hack without the reflection. Based on usual reset of breakSpots on model change.

    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);
            }
        }
    }
    

    UPDATE: This one fixes TextSamplerDemo as well. I reset all spots for all labels views.

    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);
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-05 16:07

    for futures readers, bug is still present in JDK 1.7.0_04.,

    comparing Java7 and with stable Java6,

    enter image description here<------ Java7 v.s. Java6 --->enter image description here

    enter image description here<------ Java7 v.s. Java6 --->enter image description here

    enter image description here<------ Java7 v.s. Java6 --->enter image description here

    enter image description here <------ Java7 v.s. Java6 ---> enter image description here

    from code

    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();
                }
            });
        }
    }
    
    0 讨论(0)
提交回复
热议问题