Всем привет. Для того чтобы писать код нужен удобный инструмент, кто-то пользуется IDE, кто-то редакторами текста Emacs, VIM. Зачастую, чтобы программисту было удобно, разработчику надо соблюсти много нюансов, тоесть в одном случае есть ускорение разработки, но решение тяжеловесное, в другом случае, разработчик, чтобы пользоваться редактором начинает писать код, чтобы писать код. Конечно это крайности, и проще поставить IDE/VSCode/etc… , но что если можно найти эту золотую середину!?
Неоднократно автор пытался делать текстовый редактор, но что-то было всё не то, пока не вспомнил, что есть старый добрый SWING, и java by design иногда круто и интересно.
В этой статье хочу показать как получилось минимальными усилиями сделать то, что на С или С++ потребует больших знаний без QT/GTK/etc…
Начнем!
Пользуюсь системой FreeBSD 14.2 Release
% uname -rv 14.2-RELEASE FreeBSD 14.2-RELEASE releng/14.2-n269506-c8918d6c7412 GENERIC
Как поставить java (на всякий случай)
% pkg install openjdk17-17.0.13+11.1 openjdk17-jre-17.0.13+11.1 коммеентарий-строка#в /etc/fstab надо дополнить следующее и перезагрузиться или примонтировать чего не достаточно fdesc/dev/fdfdescfsrw00 proc/procprocfsrw00
На всякий случай как скомпилировать программу
% cat > main.java class Main{ public static void main(String[] args){ System.out.println("Hello World"); } } ^C % javac main.java % java main.java Hello World % % cat > MANIFEST.MF Manifest-Version: 1.0 Main-Class: Main ^C % jar -cvfm app.jar MANIFEST.MF *.class; added manifest adding: Main.class(in = 413) (out= 285)(deflated 30%) % java -jar ./app.jar Hello World %
Создадим скрипт на сборку
% cat > compile.sh javac Main.java; jar -cvfm app.jar MANIFEST.MF *.class; java -jar ./app.jar ^C % cat compile.sh javac Main.java; jar -cvfm app.jar MANIFEST.MF *.class; java -jar ./app.jar % chmod +x compile.sh
Приступим к подходу шаблона над текстовым редактором
В ресурсах указано чем я пользовался java-swing-create-a-simple-text-editor.
давайте рассмотрим псевдокод того к чему надо приблизиться,
{ Scroll nScrolls; MenubarWithItemsOrCli barTop,barBottom; Frame frame; TextArea text,numbers,cli;//stringsWithMetadata Process p; Prompt prompt; Command command; }=> superClass { load barTop(MenuAndItems); load Group(Scroll(numbers,text)); load barBottom(Scroll(cli)); connectAllLoadingToFrame(barTop,GroupWithOneScroll,barBottom); setCursorInValidPostoTextArea(); setSettingsForFrame; Show(); }=> мы имеем строку куда вводятся символы и число строк {}//Events {}//ProcessIfNeedProvide { call Editor; }
давайте проинициализируем суперКласс
import java.awt.*; import javax.swing.*; import javax.swing.event.*; import java.io.*; import java.awt.event.*; import javax.swing.plaf.metal.*; import javax.swing.text.*; import java.io.IOException; class Editor extends JFrame implements ActionListener { // Text component private JTextArea t; //code private JTextArea t1; //lineNumbers private JTextArea t2; //mini cli // Frame private JFrame f; //frame-likewWindow private StringBuilder LineN; //string private String prompter=" \n % "; //prompt private String commander; //command after prompt private int LineNN=1; //number of lines on doc // Constructor public Editor() { // Create a frame JFrame.setDefaultLookAndFeelDecorated(true); f = new JFrame("editor"); try { // Set metal look and feel UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); // Set theme to ocean MetalLookAndFeel.setCurrentTheme(new OceanTheme());//set theme read in doc } catch (Exception e) { } //config jtextarea for numberLines t1 = new JTextArea("1\n",2,1);//start jtextarea with a1,a2,a3 - text , row, collumns t1.setEditable(false);//disable edit //config jtextarea for textSpace t = new JTextArea("111",21,20); t.setTabSize(1);//1 tab t.setCaretPosition(t.getDocument().getLength());//set cursor position in doc t.addKeyListener(new CounterLines());//connect listen keyboard // Create a menubar JMenuBar mb = new JMenuBar();//menubar top // Create amenu for menu JMenu m1 = new JMenu("File");//menu for file // Create menu items JMenuItem mi1 = new JMenuItem("New");//item for file JMenuItem mi2 = new JMenuItem("Open");//item for file JMenuItem mi3 = new JMenuItem("Save");//item for file JMenuItem mi9 = new JMenuItem("Print");//item for file // Add action listener mi1.addActionListener(this);//connect event mi2.addActionListener(this); mi3.addActionListener(this); mi9.addActionListener(this); m1.add(mi1);//connect to File m1.add(mi2); m1.add(mi3); m1.add(mi9); // Create amenu for menu JMenu m2 = new JMenu("Edit");//menu for Edit // Create menu items JMenuItem mi4 = new JMenuItem("cut");//item for edit JMenuItem mi5 = new JMenuItem("copy");//item for edit JMenuItem mi6 = new JMenuItem("paste");//item for edit // Add action listener mi4.addActionListener(this);//connect event mi5.addActionListener(this); mi6.addActionListener(this); m2.add(mi4);//connect to Edit m2.add(mi5); m2.add(mi6); JMenu mc = new JMenu("Close");//menu button for close instant mc.addMouseListener(new ExitAction());//connect event mb.add(m1);//connect all menu to menubar-top mb.add(m2); mb.add(mc); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//exit on button close X JMenuBar mbConsole = new JMenuBar();//create menubar-bottom for cli t2 = new JTextArea(prompter,10,20);//create jtextarea with a1 a2 a3 t2.addKeyListener(new CmrPrompter());//connect keyevent mbConsole.add(t2);//connect jtextarea-cli to bottom menubar //collors for all jtextareas t=code t1=numbers t2=cli Color c = new Color(0,0,0,255); Color cf = new Color(200,200,200,255); t.setBackground(c); t.setForeground(cf); Color c1 = new Color(50,50,50,255); Color cf1 = new Color(200,200,200,255); t1.setBackground(c1); t1.setForeground(cf1); Color c2 = new Color(60,60,60,255); Color cf2 = new Color(200,200,200,255); t2.setBackground(c2); t2.setForeground(cf2); JPanel panel = new JPanel();//create panel panel.setLayout(new BorderLayout());//create template for complians panel.add(t1, BorderLayout.WEST);//left because numbers panel.add(t, BorderLayout.CENTER);//right extend to center f.add(mb,BorderLayout.NORTH);//menubar to up f.add(new JScrollPane(mbConsole),BorderLayout.SOUTH);//set scroll to cli and set bottom position f.add(new JScrollPane(panel));//connect all panel to frame f.setSize(1200, 900);//set size wxh centreWindow(f);//set center position f.show();//show() t.requestFocus();//fix some situation with current pos and doc t.setCaretPosition(t.getText().length());//fix some situation with cursor } //используем performs // If a button is pressed public void actionPerformed(ActionEvent e)//actions for items { String s = e.getActionCommand(); if (s.equals("cut")) { t.cut(); } else if (s.equals("copy")) { t.copy(); } else if (s.equals("paste")) { t.paste(); } else if (s.equals("Save")) { // Create an object of JFileChooser class JFileChooser j = new JFileChooser("f:"); // Invoke the showsSaveDialog function to show the save dialog int r = j.showSaveDialog(null); if (r == JFileChooser.APPROVE_OPTION) { // Set the label to the path of the selected directory File fi = new File(j.getSelectedFile().getAbsolutePath()); try { // Create a file writer FileWriter wr = new FileWriter(fi, false); // Create buffered writer to write BufferedWriter w = new BufferedWriter(wr); // Write w.write(t.getText()); w.flush(); w.close(); } catch (Exception evt) { JOptionPane.showMessageDialog(f, evt.getMessage()); } } // If the user cancelled the operation else JOptionPane.showMessageDialog(f, "the user cancelled the operation"); } else if (s.equals("Print")) { try { // print the file t.print(); } catch (Exception evt) { JOptionPane.showMessageDialog(f, evt.getMessage()); } } else if (s.equals("Open")) { // Create an object of JFileChooser class JFileChooser j = new JFileChooser("f:"); // Invoke the showsOpenDialog function to show the save dialog int r = j.showOpenDialog(null); // If the user selects a file if (r == JFileChooser.APPROVE_OPTION) { // Set the label to the path of the selected directory File fi = new File(j.getSelectedFile().getAbsolutePath()); try { // String String s1 = "", sl = ""; // File reader FileReader fr = new FileReader(fi); // Buffered reader BufferedReader br = new BufferedReader(fr); // Initialize sl sl = br.readLine(); // Take the input from the file while ((s1 = br.readLine()) != null) { sl = sl + "\n" + s1; LineNN+=1;//set linenumbers while if line t1.append(""+LineNN); t1.append(" \n"); } // Set the text t.setText(sl); } catch (Exception evt) { JOptionPane.showMessageDialog(f, evt.getMessage()); } } // If the user cancelled the operation else JOptionPane.showMessageDialog(f, "the user cancelled the operation"); } else if (s.equals("New")) { t.setText(""); } else if (s.equals("Close")) { } repaint();// revalidate(); } //выход по Close //exit after clicked on Close Menu!=item class ExitAction extends MouseInputAdapter { public void mouseClicked(MouseEvent mouseEvent) { System.exit(0); } } //подсчет строк и применение в буффере счетчика строк class CounterLines extends KeyAdapter {//listen keyboard event for enter if enter add \n +i to numberLines public void keyPressed(KeyEvent e) { if(e.getKeyCode() == KeyEvent.VK_ENTER){ LineNN+=1; t1.append(""+LineNN); t1.append(" \n"); } } } //установим окно в центр public static void centreWindow(Window frame) {//set center Window Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize();//get size int x = (int) ((dimension.getWidth() - frame.getWidth()) / 2);// int y = (int) ((dimension.getHeight() - frame.getHeight()) / 2); frame.setLocation(x, y); } //поток где проводится команда class CmrPrompter extends KeyAdapter{//cmd\n - \n emulate enter public void keyPressed(KeyEvent e){ if(e.getKeyCode() == KeyEvent.VK_ENTER){ Runtime runtime = Runtime.getRuntime();//default process //get token from doc https://godbolt.org/z/9e5W3zf81 String s1 = t2.getText(); String parts[] = s1.split("\n"); int sz = parts.length; String part1 = parts[sz-1]; String parts2[] = part1.split("% "); int sz1 = parts2.length; commander=parts2[sz1-1];//set command token try {//try Process pR = runtime.exec(commander);//call program BufferedReader cmdLineOut = new BufferedReader(new InputStreamReader(pR.getInputStream())); PrintWriter cmdLineIn = new PrintWriter(pR.getOutputStream()); String s = null; while((s=cmdLineOut.readLine())!=null){//read line by line t2.append("\n"); t2.append(s); //System.out.println(cmdLineIn.toString());//commentary dont touching } } catch (IOException e1){ e1.printStackTrace(); } commander= "";//free command string t2.append("\n");//set to the end//combo div-s t2.append(" % ");//set to the end//combo div-s t2.setText(t2.getText().substring(0,t2.getText().lastIndexOf("\r\n")));//set cursor after prompt System.gc();//just } } } // Main class public static void main(String[] args) { Editor e = new Editor();//jump to editor } }
Этот код решает на стадии инициализации группирование обьектов, а именно — выставление двух елементов — буффер числа строк и текстовое поле пользователя в 1 скролл, выставление верхнего бара, применение в нижнем баре текстовое поле для использования в нём проведение пользовательских комманд.
В стадии использования как редактор он пока простенький, на первых подходах свою задачу выполняет.
если скомпилировать этот код и запустить
% ./compile.sh Note: Editor.java uses or overrides a deprecated API. Note: Recompile with -Xlint:deprecation for details. added manifest adding: Editor.class(in = 6437) (out= 3606)(deflated 43%) adding: Editor$1.class(in = 767) (out= 521)(deflated 32%) adding: Editor$CmrPrompter.class(in = 1966) (out= 1124)(deflated 42%) adding: Editor$CounterLines.class(in = 1129) (out= 658)(deflated 41%) adding: Editor$ExitAction.class(in = 489) (out= 347)(deflated 29%) adding: Editor$SMenuListener.class(in = 626) (out= 386)(deflated 38%) adding: Editor$TextAreaOutputStream.class(in = 951) (out= 566)(deflated 40%) adding: Editor$TextAreaOutputStream$1.class(in = 781) (out= 461)(deflated 40%) adding: SMenuListener.class(in = 606) (out= 356)(deflated 41%)

Важный момент автор заметил при проведении процесса выпадение без остановки
Exception in thread "AWT-EventQueue-0" java.lang.StringIndexOutOfBoundsException: begin 0, end -1, length 73
это происходит в строке, где я решаю очистить строку от последнего елемента чтобы после проведения комманды курсор был как в терминале на своём месте, я пока пропустил это предупреждение.
t2.setText(t2.getText().substring(0,t2.getText().lastIndexOf("\r\n"))); //set cursor after prompt
Теперь мы знаем как сделать простой редактор. А вам приходилось делать текстовый редактор? Поделитесь об этом в комментарии
Ресурсы:
JTextArea — разбор java-swing-create-a-simple-text-editor
ссылка на оригинал статьи https://habr.com/ru/articles/891044/
Добавить комментарий