Простой редактор текста при помощи java на swing

от автора

Всем привет. Для того чтобы писать код нужен удобный инструмент, кто-то пользуется 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

BorderLayout

Runtimejava-runtime-getruntime-method

swing-jtextfield

abstract, Class, subclasses


ссылка на оригинал статьи https://habr.com/ru/articles/891044/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *