0

我正在使用 Swing 制作一个简单的 markdown 文本编辑器,它可以在其主 JTextPane 中实时呈现 markdown 格式。也就是说,markdown 格式的文本*hello*一旦检测到就会以斜体显示,但会以纯文本形式保存。

为此,我想出了一个正则表达式来获取降价标记(让我们现在仅用*italics*作示例),每隔几秒钟我的代码就会搜索 JTextPane 的文本并使用 JTextPane#setCharacterAttributes 来更改相关区域的格式.

// init
PLAIN = Document.addStyle("plain", null);
StyleConstants.setFontSize(PLAIN, 12);

ITALIC = Document.addStyle("italic", null);
StyleConstants.setItalic(ITALIC, true);
...
// every few seconds
// remove all formatting
Document.setCharacterAttributes(0, Document.getLength(), PLAIN, true);

// italicize parts that the regex matches
m = Pattern.compile("\\*([^\\n*]+)\\*").matcher(temp);
while (m.find()) {
    Document.setCharacterAttributes(m.start(), m.group().length(), ITALIC, false);
}

问题是“活跃度”——一段时间后,JTextPane 开始用characters 而不是 words换行,有时会完全失去自动换行,只显示未换行的行。

有什么办法可以修复这个/扩展 JTextPane 来修复它,还是 JTextPane 根本不适合这种实时更新?我用谷歌搜索了很长时间,但找不到任何东西;我只是不确定要搜索什么。

package test;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.lang.reflect.Field;
import java.util.logging.*;
import java.util.regex.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;

public class Test {

    private JFrame frame = new JFrame();
    private JTextPane jtp;
    private StyledDocument doc;

    // NEW LINES
    private Timer T;
    private boolean update = true;
    MarkdownRenderer m;
    // END OF NEW LINES

    public Test() {
        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();
        // NEW LINES
        m = new MarkdownRenderer(jtp);
        Timer T = new Timer(2000, new java.awt.event.ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (update) {
                    update = false;
                    m.render();
                }
            }
        });
        T.start();
        // END OF NEW LINES
        doc.addDocumentListener(new DocumentListener() {

            private boolean doUpdate = true;
            public void insertUpdate(DocumentEvent e) {
                insert();
            }

            public void removeUpdate(DocumentEvent e) {
                insert();
            }

            public void changedUpdate(DocumentEvent e) {
//                triggers every time formatting is changed
//                 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);
                        update = true;
                    }
                });
            }
        });
        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() {
                Test bugWrapJava7 = new Test();
            }
        });
    }
}

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 MarkdownRenderer {    
    private static final MutableAttributeSet PLAIN = new SimpleAttributeSet();
    private static final MutableAttributeSet BOLD = new SimpleAttributeSet();
    private static final MutableAttributeSet ITALIC = new SimpleAttributeSet();
    private static final MutableAttributeSet UNDERLINE = new SimpleAttributeSet();

    private StyledDocument Document = null;

    public MarkdownRenderer(JTextPane editor) {
        Document = (StyledDocument) editor.getDocument();

        StyleConstants.setBold(BOLD, true);
        StyleConstants.setItalic(ITALIC, true);
        StyleConstants.setUnderline(UNDERLINE, true);
    }

    void render() {
        String s = "";
        try {
            s = Document.getText(0, Document.getLength());
        } catch (BadLocationException ex) {
            Logger.getLogger(MarkdownRenderer.class.getName()).log(Level.SEVERE, null, ex);
        }
//        Document.setCharacterAttributes(0, Document.getLength(), PLAIN, true);

        String temp = s.replaceAll("\\*\\*([^\\n*]+)\\*\\*", "|`$1`|"); // can also use lazy quantifier: (.+?)

        Matcher m = Pattern.compile("\\|`.+?`\\|").matcher(temp);
        while (m.find()) {
            Document.setCharacterAttributes(m.start(), m.group().length(), BOLD, false);
        }
        m = Pattern.compile("\\*([^\\n*]+)\\*").matcher(temp);
        while (m.find()) {
            Document.setCharacterAttributes(m.start(), m.group().length(), ITALIC, false);
        }
        m = Pattern.compile("_+([^\\n*]+)_+").matcher(temp);
        while (m.find()) {
            Document.setCharacterAttributes(m.start(), m.group().length(), UNDERLINE, false);
        }
    }
}
4

0 回答 0