突出显示 JTextPane 中的当前行

发布于 2024-10-26 18:45:26 字数 4878 浏览 2 评论 0原文

我尝试了 2 天多的时间来实现文本编辑器窗口的特定要求...不幸的是到目前为止没有成功:(

目标是获得一个文本编辑器窗口,它将突出显示当前行,就像其他文本编辑器一样对于当前行,我的意思是当前光标/插入符所在的行,

但不幸的是我无法采用它们,因此它们按预期工作

。 (http://snippets.dzone.com/posts/show/6688)。 在第二种方法中, HighlighterPainter 将被覆盖 (http://www.jroller.com/ santhosh/date/20050622)。

现在我正在尝试在我的项目中采用第一种方法,但正如我所说,它没有按预期工作。

在这篇文章的末尾,我发布了一个小型示例应用程序来演示该问题。

  • 如果我启动该程序,插入符号将放置在第一行的开头。但是,该行并未突出显示。
  • 现在我输入一些字符。这些字符将突出显示,但那些字符而不是
  • 我按 Enter 移动到下一行的完整行。第一行不再突出显示正确的内容。第二行也没有突出显示,这是不正确的。同样,当我输入一些字符时,这些字符将突出显示,但不会突出显示完整的行。
  • 现在,当我通过光标向上键或鼠标单击将插入符号移回到第一行时,完整的第一行将突出显示,而不仅仅是现有的字符。这就是我从一开始就想要的行为。

我希望任何人都可以告诉我我在这里做错了什么......或者解释为什么根本不可能解决这个问题。任何我可以实现线条突出显示的替代解决方案也受到高度赞赏!

预先非常感谢 干杯 布道者

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;

import javax.swing.JFrame;
import javax.swing.JTextPane;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;

public class HighlightProblem extends JFrame {
    private static final long serialVersionUID = 1L;
    private final JTextPane textPane;
    private final Highlighter.HighlightPainter cyanPainter;

    public HighlightProblem() {
        cyanPainter = new DefaultHighlighter.DefaultHighlightPainter(Color.CYAN);

        textPane = new JTextPane();
        textPane.setPreferredSize(new Dimension(500, 300));
        textPane.setHighlighter(new LineHighlighter());
        textPane.addCaretListener(new CaretListener() {
            @Override
            public void caretUpdate(CaretEvent e) {
                setHighlight(e);
            }
        });
        getContentPane().add(textPane);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
        setVisible(true);
    }

    public static void main(String[] args) {
        new HighlightProblem();
    }

    public void setHighlight(CaretEvent e) {
        textPane.getHighlighter().removeAllHighlights();
        int currentLine = getLineFromOffset(textPane, e.getDot());
        int startPos = getLineStartOffsetForLine(textPane, currentLine);
        int endOffset = getLineEndOffsetForLine(textPane, currentLine);

        try {
            textPane.getHighlighter().addHighlight(startPos, endOffset, cyanPainter);           
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        textPane.repaint();
    }

    public int getLineFromOffset(JTextComponent component, int offset) {
        return component.getDocument().getDefaultRootElement().getElementIndex(offset);
    }

    public int getLineStartOffsetForLine(JTextComponent component, int line) {
        return component.getDocument().getDefaultRootElement().getElement(line).getStartOffset();
    }

    public int getLineEndOffsetForLine(JTextComponent component, int line) {
        return component.getDocument().getDefaultRootElement().getElement(line).getEndOffset();
    }

    public class LineHighlighter extends DefaultHighlighter {
        private JTextComponent component;

        @Override
        public final void install(final JTextComponent c) {
            super.install(c);
            this.component = c;
        }

        @Override
        public final void deinstall(final JTextComponent c) {
            super.deinstall(c);
            this.component = null;
        }

        @Override
        public final void paint(final Graphics g) {
            final Highlighter.Highlight[] highlights = getHighlights();
            final int len = highlights.length;
            for (int i = 0; i < len; i++) {
                Highlighter.Highlight info = highlights[i];
                if (info.getClass().getName().indexOf("LayeredHighlightInfo") > -1) {
                    // Avoid allocing unless we need it.
                    final Rectangle a = this.component.getBounds();
                    final Insets insets = this.component.getInsets();
                    a.x = insets.left;
                    a.y = insets.top;
                    // a.width -= insets.left + insets.right + 100;
                    a.height -= insets.top + insets.bottom;
                    final Highlighter.HighlightPainter p = info.getPainter();
                    p.paint(g, info.getStartOffset(), info.getEndOffset(), a, this.component);
                }
            }
        }

        @Override
        public void removeAllHighlights() {
            textPane.repaint(0, 0, textPane.getWidth(), textPane.getHeight());
            super.removeAllHighlights();
        }
    }
}

I'm trying for more than 2 days to implement a specific requirement for a text editor window... unfortunately without success so far :(

The goal is to get a text editor window which will highlight the current row, like other text editors do. With current row I mean the row where currently the cursor/caret is positioned.

I already found two different approaches but unfortunately I'm not able to adopt them so they work as expected.

The first approach is to overwrite the DefaultHighlighter (http://snippets.dzone.com/posts/show/6688).
In the second approach the HighlighterPainter will be overwritten instead (http://www.jroller.com/santhosh/date/20050622).

Right now I'm trying to adopt the first approach in my project but as I said it is not working as desired.

At the end of this post I'm posting a small sample application which demonstrates the problem.

  • If I start the program, the caret is placed at the beginning of the first line. However, the line is not highlighted.
  • Now I type in some characters. Those chars will be highlighted but only those chars not the complete line
  • I hit enter to move to the next line. The first line is not highlighted anymore what is correct. The second line isn't highlighted as well, what is not correct. Again, when I type in some chars, those will be higlighted but not the complete row.
  • When I now move back the caret to the first line, either by cursor up key or mouse clicking, the complete first line will be highlighted, not only the existing chars. This is the behavior I want right from the start.

I hope anybody can tell me what I'm doing wrong here... or explain why it is not possible to resolve that issue at all. Any alternative solutions how I could realize the line highlighting are also highly appreciated!

Thanks a lot in advance
Cheers
Preachie

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;

import javax.swing.JFrame;
import javax.swing.JTextPane;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;

public class HighlightProblem extends JFrame {
    private static final long serialVersionUID = 1L;
    private final JTextPane textPane;
    private final Highlighter.HighlightPainter cyanPainter;

    public HighlightProblem() {
        cyanPainter = new DefaultHighlighter.DefaultHighlightPainter(Color.CYAN);

        textPane = new JTextPane();
        textPane.setPreferredSize(new Dimension(500, 300));
        textPane.setHighlighter(new LineHighlighter());
        textPane.addCaretListener(new CaretListener() {
            @Override
            public void caretUpdate(CaretEvent e) {
                setHighlight(e);
            }
        });
        getContentPane().add(textPane);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
        setVisible(true);
    }

    public static void main(String[] args) {
        new HighlightProblem();
    }

    public void setHighlight(CaretEvent e) {
        textPane.getHighlighter().removeAllHighlights();
        int currentLine = getLineFromOffset(textPane, e.getDot());
        int startPos = getLineStartOffsetForLine(textPane, currentLine);
        int endOffset = getLineEndOffsetForLine(textPane, currentLine);

        try {
            textPane.getHighlighter().addHighlight(startPos, endOffset, cyanPainter);           
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        textPane.repaint();
    }

    public int getLineFromOffset(JTextComponent component, int offset) {
        return component.getDocument().getDefaultRootElement().getElementIndex(offset);
    }

    public int getLineStartOffsetForLine(JTextComponent component, int line) {
        return component.getDocument().getDefaultRootElement().getElement(line).getStartOffset();
    }

    public int getLineEndOffsetForLine(JTextComponent component, int line) {
        return component.getDocument().getDefaultRootElement().getElement(line).getEndOffset();
    }

    public class LineHighlighter extends DefaultHighlighter {
        private JTextComponent component;

        @Override
        public final void install(final JTextComponent c) {
            super.install(c);
            this.component = c;
        }

        @Override
        public final void deinstall(final JTextComponent c) {
            super.deinstall(c);
            this.component = null;
        }

        @Override
        public final void paint(final Graphics g) {
            final Highlighter.Highlight[] highlights = getHighlights();
            final int len = highlights.length;
            for (int i = 0; i < len; i++) {
                Highlighter.Highlight info = highlights[i];
                if (info.getClass().getName().indexOf("LayeredHighlightInfo") > -1) {
                    // Avoid allocing unless we need it.
                    final Rectangle a = this.component.getBounds();
                    final Insets insets = this.component.getInsets();
                    a.x = insets.left;
                    a.y = insets.top;
                    // a.width -= insets.left + insets.right + 100;
                    a.height -= insets.top + insets.bottom;
                    final Highlighter.HighlightPainter p = info.getPainter();
                    p.paint(g, info.getStartOffset(), info.getEndOffset(), a, this.component);
                }
            }
        }

        @Override
        public void removeAllHighlights() {
            textPane.repaint(0, 0, textPane.getWidth(), textPane.getHeight());
            super.removeAllHighlights();
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梅倚清风 2024-11-02 18:45:26

http://tips4java.wordpress.com/2008/10/29/line-画家/

我想这就是你要找的。我采用了 LinePainter 类,并将构造函数复制到主方法中,取出荧光笔部分并添加了 new LinePainter(textPane);

http://tips4java.wordpress.com/2008/10/29/line-painter/

I think this is what you are looking for. I took that LinePainter class and copied your constructor over into a main method, took out your highlighter parts and added a new LinePainter(textPane); Works like a charm

∞琼窗梦回ˉ 2024-11-02 18:45:26

下面是从当前行提取文本的代码。
您可以使用相同的逻辑来获取所需的索引并突出显示文本

    private String getCurrentEditLine() {
        int readBackChars = 100;
        int caretPosition = scriptEditor.getCaretPosition();

        if (caretPosition == 0) {
            return null;
        }

        StyledDocument doc = scriptEditor.getStyledDocument();

        int offset = caretPosition <= readBackChars ? 0 : caretPosition
                - readBackChars;

        String text = null;
        try {
            text = doc.getText(offset, caretPosition);
        } catch (BadLocationException e) {
        }

        if (text != null) {
            int idx = text.lastIndexOf("\n");
            if(idx != -1) {
                return text.substring(idx);
            }else {
                return text;
            }
        }

        return null;
    }

Below is the code to extract text from current line.
You can use same logic to get required indexes and highlight text

    private String getCurrentEditLine() {
        int readBackChars = 100;
        int caretPosition = scriptEditor.getCaretPosition();

        if (caretPosition == 0) {
            return null;
        }

        StyledDocument doc = scriptEditor.getStyledDocument();

        int offset = caretPosition <= readBackChars ? 0 : caretPosition
                - readBackChars;

        String text = null;
        try {
            text = doc.getText(offset, caretPosition);
        } catch (BadLocationException e) {
        }

        if (text != null) {
            int idx = text.lastIndexOf("\n");
            if(idx != -1) {
                return text.substring(idx);
            }else {
                return text;
            }
        }

        return null;
    }
手心的海 2024-11-02 18:45:26

我认为使用荧光笔可能很难实现这一点 - 我认为这不是它们的设计目的。您可能需要使用自定义绘画代码来完成此操作:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;

import javax.swing.JFrame;
import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;

public class HighlightLineTest {
    private static class HighlightLineTextPane extends JTextPane {
        public HighlightLineTextPane() {
            // Has to be marked as transparent so the background is not replaced by 
            // super.paintComponent(g);
            setOpaque(false);
        }

        @Override
        protected void paintComponent(Graphics g) {
            g.setColor(getBackground());
            g.fillRect(0, 0, getWidth(), getHeight());
            try {
                Rectangle rect = modelToView(getCaretPosition());
                if (rect != null) {
                    g.setColor(Color.CYAN);
                    g.fillRect(0, rect.y, getWidth(), rect.height);
                }
            } catch (BadLocationException e) {
            }
            super.paintComponent(g);
        }

        @Override
        public void repaint(long tm, int x, int y, int width, int height) {
            // This forces repaints to repaint the entire TextPane.
            super.repaint(tm, 0, 0, getWidth(), getHeight());
        }
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("Highlight test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new HighlightLineTextPane());
        frame.setBounds(100, 100, 300, 400);
        frame.setVisible(true);
    }
}

I think this might be difficult to achieve using highlighters - I don't think it is what they were designed for. You may need to do it with custom painting code:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;

import javax.swing.JFrame;
import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;

public class HighlightLineTest {
    private static class HighlightLineTextPane extends JTextPane {
        public HighlightLineTextPane() {
            // Has to be marked as transparent so the background is not replaced by 
            // super.paintComponent(g);
            setOpaque(false);
        }

        @Override
        protected void paintComponent(Graphics g) {
            g.setColor(getBackground());
            g.fillRect(0, 0, getWidth(), getHeight());
            try {
                Rectangle rect = modelToView(getCaretPosition());
                if (rect != null) {
                    g.setColor(Color.CYAN);
                    g.fillRect(0, rect.y, getWidth(), rect.height);
                }
            } catch (BadLocationException e) {
            }
            super.paintComponent(g);
        }

        @Override
        public void repaint(long tm, int x, int y, int width, int height) {
            // This forces repaints to repaint the entire TextPane.
            super.repaint(tm, 0, 0, getWidth(), getHeight());
        }
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("Highlight test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new HighlightLineTextPane());
        frame.setBounds(100, 100, 300, 400);
        frame.setVisible(true);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文