Поведение переноса строк в JTextPane
Недавно я работал над проектом текстового редактора Java, и я хотел бы использовать JTextPane
заменить старое JTextArea
для того, чтобы реализовать подсветку синтаксиса. Тем не менее, JTextPane
не хватает методов в JTextArea
(такие как append()
, getLineStartOffset()
и т.д.) и я хочу переопределить их в моем классе MyTextPane
(подкласс JTextPane
) но столкнулись с неприятностями.
Мой текущий код (только небольшая отдельная часть):
import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
public class MyTextPane extends JTextPane
{
public MyTextPane()
{
super();
}
public void append(String text)
{
try
{
Document doc = this.getDocument();
doc.insertString(doc.getLength(),text,null);
}
catch (BadLocationException ex)
{
//must succeed
throw new InternalError(ex.getMessage());
}
}
public void insert(String text, int pos)
{
try
{
this.getStyledDocument().insertString(pos,text,null);
}
catch (BadLocationException ex)
{
throw new IllegalArgumentException(ex);
}
}
public void replaceRange(String str, int start, int end)
{
try
{
Document doc = this.getDocument();
doc.remove(start,end-start);
doc.insertString(start,str,null);
}
catch (BadLocationException ex)
{
throw new IllegalArgumentException(ex);
}
}
public void setLineWrap(boolean isLineWrap)
{
/*
* implements later
*/
}
public boolean getLineWrap()
{
/*
* implements later
*/
return true;
}
public void setWrapStyleWord(boolean isWrapStyleWord)
{
/*
* implements later
*/
}
public boolean getWrapStyleWord()
{
/*
* implements later
*/
return true;
}
public void setTabSize(int size)
{
/*
* implements later
*/
}
public int getTabSize()
{
/*
* implements later
*/
return 4;
}
public int getLineCount()
{
//follow JTextArea implementation
Element root = this.getDocument().getDefaultRootElement();
return root.getElementCount();
}
public int getLineStartOffset(int line) throws BadLocationException
{
//follow JTextArea implementation
int count = this.getLineCount();
Document doc = this.getDocument();
if (line < 0)
{
throw new BadLocationException("Negative line", -1);
}
if (line >= count)
{
throw new BadLocationException("No such line", doc.getLength() + 1);
}
return doc.getDefaultRootElement().getElement(line).getStartOffset();
}
public int getLineEndOffset(int line) throws BadLocationException
{
//follow JTextArea implementation
int count = this.getLineCount();
Document doc = this.getDocument();
if (line < 0)
{
throw new BadLocationException("Negative line", -1);
}
if (line >= count)
{
throw new BadLocationException("No such line", doc.getLength() + 1);
}
int end = doc.getDefaultRootElement().getElement(line).getEndOffset();
return (line==count-1)?(end-1):end;
}
public int getLineOfOffset(int off) throws BadLocationException
{
//follow JTextArea implementation
Document doc = this.getDocument();
if (off < 0)
{
throw new BadLocationException("Can't translate offset to line", -1);
}
if (off > doc.getLength())
{
throw new BadLocationException("Can't translate offset to line", doc.getLength() + 1);
}
return doc.getDefaultRootElement().getElementIndex(off);
}
public static void main(String[] args)
{
final SimpleAttributeSet BOLD_SET = new SimpleAttributeSet();
StyleConstants.setBold(BOLD_SET, true);
StyleConstants.setForeground(BOLD_SET, new Color(0,0,125));
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
MyTextPane textPane = new MyTextPane();
frame.add(new JScrollPane(textPane), BorderLayout.CENTER);
frame.setSize(200,200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
Как вы можете видеть, я уже добавил некоторые методы, такие как append()
, Однако я не могу придумать какие-либо способы управления политикой переноса строк.
Поведение по умолчанию довольно странно: когда есть одно короткое слово и одно длинное слово,
если я последовательно введу символы без пробела,
сначала это выглядит как завертывание словами. Однако, когда я продолжаю вводить символы,
это не обернуть вообще.
Есть ли какой-нибудь элегантный способ контролировать политику упаковки JTextPane
? Другими словами, может ли JTextPane
обернуть слова как JTextArea
? Я нашел так много дубликатов (как это, это и это), но не смог найти решение. Заранее спасибо.
2 ответа
Это обсуждение той же проблемы: поведение переноса слов в JTextPane начиная с Java 7. Решение, предложенное пользователем StanislavL (который также, кажется, очень активен в Stack Overflow: StanislavL) для поддержки переноса слов, работает для меня с помощью Java 8. Он использует пользовательский WrapEditorKit
в качестве редактора для JTextPane
(и WrapEditorKit
класс в свою очередь использует WrapColumnFactory
а также WrapLabelView
классы).
Объединение этого с примером NonWrappingTextPane (из книги Core Swing: Advanced Programming by Kim Topley) позволяет отключить перенос строк:
import java.awt.*;
import javax.swing.*;
public class WrapTestApp extends JFrame {
public static void main(final String[] arguments) {
new WrapTestApp();
}
public WrapTestApp() {
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setBounds(800, 400, 200, 200);
getContentPane().setLayout(new BorderLayout());
final CustomTextPane textPane = new CustomTextPane(true);
final JScrollPane scrollPane = new JScrollPane(textPane);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
getContentPane().add(scrollPane, BorderLayout.CENTER);
textPane.setText("ExampleOfTheWrapLongWordWithoutSpaces");
setVisible(true);
}
}
CustomTextPane
учебный класс:
import javax.swing.*;
import javax.swing.text.*;
public class CustomTextPane extends JTextPane {
private boolean lineWrap;
public CustomTextPane(final boolean lineWrap) {
this.lineWrap = lineWrap;
if (lineWrap)
setEditorKit(new WrapEditorKit());
}
@Override
public boolean getScrollableTracksViewportWidth() {
if (lineWrap)
return super.getScrollableTracksViewportWidth();
else
return getParent() == null
|| getUI().getPreferredSize(this).width <= getParent().getSize().width;
}
private class WrapEditorKit extends StyledEditorKit {
private final ViewFactory defaultFactory = new WrapColumnFactory();
@Override
public ViewFactory getViewFactory() {
return defaultFactory;
}
}
private class WrapColumnFactory implements ViewFactory {
@Override
public View create(final Element element) {
final String kind = element.getName();
if (kind != null) {
switch (kind) {
case AbstractDocument.ContentElementName:
return new WrapLabelView(element);
case AbstractDocument.ParagraphElementName:
return new ParagraphView(element);
case AbstractDocument.SectionElementName:
return new BoxView(element, View.Y_AXIS);
case StyleConstants.ComponentElementName:
return new ComponentView(element);
case StyleConstants.IconElementName:
return new IconView(element);
}
}
// Default to text display.
return new LabelView(element);
}
}
private class WrapLabelView extends LabelView {
public WrapLabelView(final Element element) {
super(element);
}
@Override
public float getMinimumSpan(final int axis) {
switch (axis) {
case View.X_AXIS:
return 0;
case View.Y_AXIS:
return super.getMinimumSpan(axis);
default:
throw new IllegalArgumentException("Invalid axis: " + axis);
}
}
}
}
Мне потребовалось некоторое время, чтобы найти решение этой проблемы, и, поскольку я не нашел его в stackoverflow , я публикую ссылку на решение .
На случай, если этот сайт когда-нибудь перестанет существовать, скопируйте код, который там находится:
import javax.swing.*;
import javax.swing.text.*;
import java.awt.*;
public class WrapTestApp extends JFrame {
public static void main(String[] args) {
new WrapTestApp();
}
public WrapTestApp() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(200, 200);
getContentPane().setLayout(new BorderLayout());
JTextPane jtp = new JTextPane();
jtp.setEditorKit(new WrapEditorKit());
JScrollPane jsp = new JScrollPane(jtp);
jsp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
getContentPane().add(jsp, BorderLayout.CENTER);
jtp.setText("ExampleOfTheWrapLongWordWithoutSpaces");
setVisible(true);
}
class WrapEditorKit extends StyledEditorKit {
ViewFactory defaultFactory = new WrapColumnFactory();
public ViewFactory getViewFactory() {
return defaultFactory;
}
}
class WrapColumnFactory implements ViewFactory {
public View create(Element elem) {
String kind = elem.getName();
if (kind != null) {
if (kind.equals(AbstractDocument.ContentElementName)) {
return new WrapLabelView(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 WrapLabelView extends LabelView {
public WrapLabelView(Element elem) {
super(elem);
}
public float getMinimumSpan(int axis) {
switch (axis) {
case View.X_AXIS:
return 0;
case View.Y_AXIS:
return super.getMinimumSpan(axis);
default:
throw new IllegalArgumentException("Invalid axis: " + axis);
}
}
}
}
Все кредиты принадлежат user301607 Л.