Как установить стиль вводимого текста таким, который сразу следует за положением курсора в JTextPane

#java #swing #jtextpane #styleddocument #word-processor

Вопрос:

Я работаю над программой базы данных, в которой графический интерфейс имеет несколько областей ввода, некоторые из которых являются областями JTextPanes, где пользователь может устанавливать стили полужирного, подчеркивания и курсива в своем тексте. Например, когда пользователь помещает курсор непосредственно рядом с областью, которая уже выделена жирным шрифтом, я хочу, чтобы следующий текст, который они вводят, также был выделен жирным шрифтом. Пока единственное, что происходит, — это то, что текст, набранный сразу после выделенного жирным шрифтом текста, выделен жирным шрифтом, но я не могу сделать так, чтобы текст, набранный непосредственно перед выделенным жирным шрифтом, был также выделен жирным шрифтом.

Я не уверен, что все это было менее чем запутанно, поэтому вот пример: предположим, что предложение «Java-это весело». уже находится в одной из областей JTextPanes.

Если пользователь помещает курсор слева или справа от «есть», я хочу, чтобы все, что они печатают рядом, также было выделено жирным шрифтом. Вот так, «Веселая Javaпанель».

До сих пор я только получаю: «Java foo-это весело».

Ниже приведен метод, который я использую для добавления функций определения стиля в каретку JTextPane, и то, что, как я думал, сработает для этого.

Я также попытался добавить дополнительный новый StyledEditorKit.BoldAction().actionPerformed(null) до или после boldButton.setSelected(true), но это не возымело никакого эффекта. Я также попытался tp.getStyledDocument().setCharacterAttributes(caretPosition -1, 1, asPrev, true) , и хотя это сделало следующий текст выделенным жирным шрифтом, результаты были непредсказуемыми, со случайными разрывами строк и пропущенными символами. Changing boldButton.setSelected(true) чтобы boldButton.doClick() не имело никакого эффекта, равно как и использование моего класса ButtonAction для выделения текста жирным шрифтом.

         int lastCaretPosition;
        public void formatAndAddFunctions(Component c) {
            Font normalFont = new Font("Tahoma", Font.PLAIN, 11);
            c.setFont(normalFont);
            
            if (c instanceof JTextField) {
                ((JTextField) c).setAlignmentX(Component.LEFT_ALIGNMENT);
                ((JTextField) c).addFocusListener(new java.awt.event.FocusAdapter() {
                     @Override
                     public void focusGained(java.awt.event.FocusEvent evt) {
                         ((JTextField) c).selectAll();
                         activeComponent = c;
                     }
                });
                UndoTool.addUndoFunctionality((JTextField) c);
            }
            
            if (c instanceof JScrollPane) {
                ((JScrollPane) c).setAlignmentX(Component.LEFT_ALIGNMENT);
                JViewport vp = ((JScrollPane) c).getViewport();
                JTextPane tp = (JTextPane) vp.getView();
                tp.setContentType("text/html");
                tp.putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, true);
                tp.addFocusListener(new java.awt.event.FocusAdapter() {
                     @Override
                     public void focusGained(java.awt.event.FocusEvent evt) {
                         activeComponent = tp;
                     }
                });
                UndoTool.addUndoFunctionality(tp);
                
                //look at the character attibutes before and after the caret to see if they are
                //formatted bold, underline, and/or italic
                lastCaretPosition = -1;
                tp.addCaretListener((CaretEvent ce) -> {
                    int caretPosition = tp.getCaretPosition();
                    if(caretPosition != lastCaretPosition) {
                        lastCaretPosition = caretPosition;
                        
                        Element charElementPrev = tp.getStyledDocument().getCharacterElement(caretPosition - 1);
                        AttributeSet asPrev = charElementPrev.getAttributes();
                        Element charElementAfter = tp.getStyledDocument().getCharacterElement(caretPosition);
                        AttributeSet asAfter = charElementAfter.getAttributes();

                        if ((StyleConstants.isBold(asPrev) || StyleConstants.isBold(asAfter)) amp;amp; !boldButton.isSelected()) {
                            boldButton.setSelected(true);
                        } else if((!StyleConstants.isBold(asPrev) amp;amp; !StyleConstants.isBold(asAfter)) amp;amp; boldButton.isSelected()) {
                            boldButton.setSelected(false);
                        }

                        if ((StyleConstants.isUnderline(asPrev) || StyleConstants.isUnderline(asAfter)) amp;amp; !ulButton.isSelected()) {
                            ulButton.setSelected(true);
                        } else if ((!StyleConstants.isUnderline(asPrev) amp;amp; !StyleConstants.isUnderline(asAfter)) amp;amp; ulButton.isSelected()) {
                            ulButton.setSelected(false);
                        }

                        if ((StyleConstants.isItalic(asPrev) || StyleConstants.isItalic(asAfter)) amp;amp; !itButton.isSelected()) {
                            itButton.setSelected(true);
                        } else if ((!StyleConstants.isItalic(asPrev) amp;amp; !StyleConstants.isItalic(asAfter)) amp;amp; itButton.isSelected()) {
                            itButton.setSelected(false);
                        }
                    }
                });

                tp.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_B, java.awt.event.InputEvent.CTRL_DOWN_MASK), "boldKeystroke");
                tp.getActionMap().put("boldKeystroke", new ButtonAction("bold"));

                tp.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_U, java.awt.event.InputEvent.CTRL_DOWN_MASK), "underlineKeystroke");
                tp.getActionMap().put("underlineKeystroke", new ButtonAction("underline"));

                tp.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_I, java.awt.event.InputEvent.CTRL_DOWN_MASK), "italicKeystroke");
                tp.getActionMap().put("italicKeystroke", new ButtonAction("italic"));
            }
        }
 

И, для справки, код boldButton:

 boldButton = new JToggleButton();
            
boldButton.setIcon(allIcons[39]);
boldButton.setRolloverIcon(allIcons[40]);
boldButton.setSelectedIcon(allIcons[41]);
boldButton.setToolTipText("Bold");
boldButton.setBorderPainted(false);
boldButton.setContentAreaFilled(false);
boldButton.setFocusable(false);
boldButton.setBorder(null);
boldButton.setMargin(noInset);
boldButton.addActionListener((java.awt.event.ActionEvent e) -> {
    new StyledEditorKit.BoldAction().actionPerformed(e);
    activeComponent.requestFocusInWindow();
});
 

Странно то, что если я установлю курсор прямо перед выделенным жирным шрифтом текстом, а затем нажму на кнопку переключения жирного шрифта в пользовательском интерфейсе, следующий набранный текст будет выделен жирным шрифтом. Но я, кажется, не могу добиться этого программно. Есть какие-нибудь идеи?

Комментарии:

1. Именно так работают редакторы, подобные Word. Типизированный символ наследует атрибуты предыдущего символа. … затем нажмите на переключатель, выделенный жирным шрифтом… — это временная настройка атрибута ввода. Да, если вы наберете сразу, вы получите текст, выделенный жирным шрифтом. Однако, если вы нажмете кнопку, выделенную жирным шрифтом, затем щелкните в другом месте, а затем вернетесь в предыдущее место, вы не получите текст, выделенный жирным шрифтом. Есть какие- нибудь идеи? — Я бы не стал пытаться изменить это поведение. Это не интуитивно понятно.

2. Хм…ты прав. Я только что проверил в Word, и это действительно так. Типизированный символ наследует атрибуты предыдущего символа, но не следующего. Исключением из этого правила является, если строка начинается с отформатированного символа; тогда ввод в начале строки наследует атрибуты следующего символа. Возможно, стоило бы просто оставить все как есть.