Сделайте части JTextArea не редактируемыми (не весь JTextArea!)
Сейчас я работаю над консольным окном в Swing. Он основан на JTextArea и работает как обычная командная строка. Вы вводите команду в одну строку и нажимаете клавишу ввода. В следующей строке выводится вывод и под этим выходом вы можете написать следующую команду.
Теперь я хочу, чтобы вы могли редактировать только текущую строку с помощью вашей команды. Все строки выше (старые команды и результаты) должны быть недоступны для редактирования. Как я могу это сделать?
Ответы
Ответ 1
Вам не нужно создавать свой собственный компонент.
Это можно сделать (как это было сделано мной), используя пользовательский DocumentFilter.
Вы можете получить документ из textPane.getDocument()
и установить для него фильтр document.setFilter()
. Внутри фильтра вы можете проверить положение приглашения и разрешить модификации только после того, как позиция появится после подсказки.
Например:
private class Filter extends DocumentFilter {
public void insertString(final FilterBypass fb, final int offset, final String string, final AttributeSet attr)
throws BadLocationException {
if (offset >= promptPosition) {
super.insertString(fb, offset, string, attr);
}
}
public void remove(final FilterBypass fb, final int offset, final int length) throws BadLocationException {
if (offset >= promptPosition) {
super.remove(fb, offset, length);
}
}
public void replace(final FilterBypass fb, final int offset, final int length, final String text, final AttributeSet attrs)
throws BadLocationException {
if (offset >= promptPosition) {
super.replace(fb, offset, length, text, attrs);
}
}
}
Однако это не позволяет вам программно вставлять содержимое в выходной (неизменный) раздел терминала. Вместо этого вы можете использовать либо флаг "Пасха" на вашем фильтре, который вы устанавливаете, когда собираетесь добавить результат, либо (что я сделал) установили для фильтра документа значение null до добавления вывода, а затем reset, когда вы закончили.
Ответ 2
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
public class OnlyEditCurrentLineTest {
public JComponent makeUI() {
JTextArea textArea = new JTextArea(8,0);
textArea.setText("> aaa\n> ");
((AbstractDocument)textArea.getDocument()).setDocumentFilter(
new NonEditableLineDocumentFilter());
JPanel p = new JPanel(new BorderLayout());
p.add(new JScrollPane(textArea), BorderLayout.NORTH);
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override public void run() { createAndShowGUI(); }
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new OnlyEditCurrentLineTest().makeUI());
f.setSize(320,240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class NonEditableLineDocumentFilter extends DocumentFilter {
@Override public void insertString(
DocumentFilter.FilterBypass fb, int offset, String string,
AttributeSet attr) throws BadLocationException {
if(string == null) {
return;
}else{
replace(fb, offset, 0, string, attr);
}
}
@Override public void remove(
DocumentFilter.FilterBypass fb, int offset,
int length) throws BadLocationException {
replace(fb, offset, length, "", null);
}
private static final String PROMPT = "> ";
@Override public void replace(
DocumentFilter.FilterBypass fb, int offset, int length,
String text, AttributeSet attrs) throws BadLocationException {
Document doc = fb.getDocument();
Element root = doc.getDefaultRootElement();
int count = root.getElementCount();
int index = root.getElementIndex(offset);
Element cur = root.getElement(index);
int promptPosition = cur.getStartOffset()+PROMPT.length();
//As Reverend Gonzo says:
if(index==count-1 && offset-promptPosition>=0) {
if(text.equals("\n")) {
String cmd = doc.getText(promptPosition, offset-promptPosition);
if(cmd.isEmpty()) {
text = "\n"+PROMPT;
}else{
text = "\n"+cmd+"\n xxxxxxxxxx\n" + PROMPT;
}
}
fb.replace(offset, length, text, attrs);
}
}
}
Ответ 3
AFAIK, вам необходимо реализовать свой собственный контроль
Возможно, вы могли бы имитировать его со списком текстовых полей (даже включенными и нечетными) или сочетанием текстовых полей/меток
EDIT:
Я бы поставил для нередактируемого текстового поля и редактируемого текстового поля. Введите текстовое поле, нажмите enter, добавьте команду и введите в текстовое поле
Ответ 4
Как насчет того, когда " → " является началом каждой строки в командной строке, где пользователь может ввести команду:
textArea.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent event) {
int code = event.getKeyCode();
int caret = textArea.getCaretPosition();
int last = textArea.getText().lastIndexOf(">> ") + 3;
if(caret <= last) {
if(code == KeyEvent.VK_BACK_SPACE) {
textArea.append(" ");
textArea.setCaretPosition(last + 1);
}
textArea.setCaretPosition(textArea.getText().length());
}
}
});
Ответ 5
Это мое воплощение фильтра документов, действующего как консоль в java.
Однако с некоторыми изменениями, позволяющими мне иметь "область команд" и "область журнала", что означает результаты печати команд в области журнала, а фактическая команда печатает в области команд.
Область журнала - это еще одна область Jtext, которая не является допустимой.
Я нашел thisthread, чтобы быть полезным, поэтому mabey, кто-то пытается достичь чего-то похожего на эту реализацию, может найти некоторые указатели!
class NonEditableLineDocumentFilter extends DocumentFilter
{
private static final String PROMPT = "Command> ";
@Override
public void insertString(DocumentFilter.FilterBypass fb, int offset, String string,AttributeSet attr) throws BadLocationException
{
if(string == null)
{
return;
}
else
{
replace(fb, offset, 0, string, attr);
}
}
@Override
public void remove(DocumentFilter.FilterBypass fb, int offset,int length) throws BadLocationException
{
replace(fb, offset, length, "", null);
}
@Override
public void replace(DocumentFilter.FilterBypass fb, int offset, int length,String text, AttributeSet attrs) throws BadLocationException
{
Document doc = fb.getDocument();
Element root = doc.getDefaultRootElement();
int count = root.getElementCount();
int index = root.getElementIndex(offset);
Element cur = root.getElement(index);
int promptPosition = cur.getStartOffset()+PROMPT.length();
if(index==count-1 && offset-promptPosition>=0)
{
if(text.equals("\n"))
{
cmd = doc.getText(promptPosition, offset-promptPosition);
if(cmd.trim().isEmpty())
{
text = "\n"+PROMPT;
}
else
{
text = "\n" + PROMPT;
}
}
fb.replace(offset, length, text, attrs);
}
}
}