# Alternate Processing Editor Using processing-java.exe on the Command line

**URL:** <https://discourse.processing.org/t/alternate-processing-editor-using-processing-java-exe-on-the-command-line/47530>\
**Category:** Gallery\
**Created:** [November 12, 2025, 2:32pm UTC](https://discourse.processing.org/t/alternate-processing-editor-using-processing-java-exe-on-the-command-line/47530 "2025-11-12T14:32:36Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![svan](https://avatars.discourse-cdn.com/v4/letter/s/82dd89/32.png) [@svan](https://discourse.processing.org/u/svan)\
**Post date:** [November 12, 2025, 2:32pm UTC](https://discourse.processing.org/t/alternate-processing-editor-using-processing-java-exe-on-the-command-line/47530/1 "2025-11-12T14:32:36Z")

</div>

**Intro:** The demo below will create an entry level code editor for running Processing source code from the command line using the _processing-java.exe_ file. It was developed and tested mainly on MacOS(M2); I was unable to get it to run on other platforms due to difficulty finding either processing-java or processing-cli for the other operating systems. There are five Tabs with additional code which are also shown below. This project was undertaken for proof of concept and for those users who, for one reason or another, do not want to use the official Processing IDE. My presumption is that _processing-java.exe_ from the command line will function even if the official Processing IDE is not on the user’s system; this may or may not be correct (I have no way to test since I don’t want to give up my official Processing IDE and do not have a new computer to experiment with). The exported app is smaller in size than the official Processing IDE (4.4.10) at 254.3 MB (including embedded Java) compared to 660.6 MB for the official editor. **Without Java**  **embedded** this app’s size falls to **13.8 MB** and still runs on my system.

**Dependency:** The editor uses **RSyntaxTextArea** for syntax highlighting which may be downloaded here: [https://sourceforge.net/projects/rsyntaxtextarea/files%2Frsyntaxtextarea%2F1.5.1%2Frsyntaxtextarea\_1.5.1.zip/download](https://sourceforge.net/projects/rsyntaxtextarea/files%2Frsyntaxtextarea%2F1.5.1%2Frsyntaxtextarea_1.5.1.zip/download) . To install on your system, first create a folder in your _Processing/libraries_ folder entitled ‘_RSyntaxtTextArea_’ and inside of that create another folder entitled ‘_library_’. Place the downloaded _'rsyntaxtextarea.jar_’ file in the ‘_library_’ folder and then you should be able to use syntax highlighting. If you want to use _code folding_ you will additionally need ‘_rsyntaxtextarea-3.6.0.jar_’ downloaded to your Processing/libraries/RSyntaxTextArea/library folder, available at: [https://repo1.maven.org/maven2/com/fifesoft/rsyntaxtextarea/3.6.0/](https://repo1.maven.org/maven2/com/fifesoft/rsyntaxtextarea/3.6.0/) . You can get by with only the earlier version if you don’t care about code folding. Source code line number 412 is REMmed out to avoid an error message from not having the latter jar file available. If you choose to use _code folding_ (after downloading and installing _rsyntaxtextarea-3.6.0.jar)_ then just unREM that line of code (the editor was initially setup to include code folding).

**Setup:** 1) The first thing to do after installing RSyntaxTextArea is set the path to the _processing-java.exe_ file. I found its location on my system by entering '_whereis processing-java’_ on the Terminal command line. It was at /usr/local/bin; I presume that is where the Processing IDE installed it after selecting 'Tools/Install “processing-java” on the menubar. There may be other ways to obtain this .exe file; I didn’t have much luck running another copy from my home directory.

1. The second thing to do is set the folder which you want JFileChooser to open each time you select File/Open. This is handy to keep from having to migrate to the desired folder each time you want to open another file; the chooser will automatically take you to that folder once set. Both settings are stored in a separate file in the sketch folder.

**Design Features:** 1) Each time you open a new file a new tab is created using the file’s name, and a new instance of RSyntaxTextArea is embedded into the JTabbedPane. There is a menubar item to remove unwanted tabs (except for the first one which may not be removed). This makes it possible to copy/paste code from one tab to another.

1. Note that the **folder**  **path** is used in the _–sketch=_ string (not the full path of the .pde file).

2. On MacOS the menubar is placed at the top of the screen after the project is exported.

3. This demo editor uses only the _–run_ option in ProcessBuilder but others, including _–export_, may also be used but were not fully tested.

4. There is no sketch ‘Stop’ button on this app. To stop a running sketch simply hit the sketch’s window close button or hit cmd-Q with the sketch in focus.

**Command line edition for Processing 4.4.10 (Java Mode):**

```auto
--help Show this help text. Congratulations.

--sketch=<name> Specify the sketch folder (required)

--output=<name> Specify the output folder (optional and

                     cannot be the same as the sketch folder.)

--force The sketch will not build if the output

                     folder already exists, because the contents

                     will be replaced. This option erases the

                     folder first. Use with extreme caution!

--build Preprocess and compile a sketch into .class files.

--run Preprocess, compile, and run a sketch.

--present Preprocess, compile, and run a sketch in presentation mode.

--export Export an application.

--variant Specify the platform and architecture (Export only).

--no-java Do not embed Java.

Starting with 4.0, the --platform option has been removed because of the variety of platforms and architectures now available.

Use the --variant option instead, for instance:

variant platform

------------- ---------------------------

macos-x86_64 macOS (Intel 64-bit)

macos-aarch64 macOS (Apple Silicon)

windows-amd64 Windows (Intel 64-bit)

linux-amd64 Linux (Intel 64-bit)

linux-arm Linux (Raspberry Pi 32-bit)

linux-aarch64 Linux (Raspberry Pi 64-bit)

The --build, --run, --present, or --export must be the final parameter

passed to Processing. Arguments passed following one of those four will

be passed through to the sketch itself, and therefore available to the

sketch via the 'args' field. To pass options understood by PApplet.main(),

write a custom main() method so that the preprocessor does not add one.

```

**Source code:**

```processing
import java.awt.*;
import java.awt.event.*;
import java.awt.print.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.io.*;
import java.io.File;
import java.nio.file.Path;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import javax.swing.text.BadLocationException;
import static java.awt.event.InputEvent.META_DOWN_MASK;

import org.fife.ui.rsyntaxtextarea.*;
import org.fife.ui.rtextarea.*;

import java.awt.event.MouseEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseListener;

import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

import java.util.ArrayList;
import java.util.List;

ArrayList<String> files = new ArrayList<String>();
ArrayList<RSyntaxTextArea> txtAreas = new ArrayList<RSyntaxTextArea>();

JFrame frame;
JScrollPane txtScrlPane;
RSyntaxTextArea txtArea;
JTextArea logArea;
JTabbedPane tabbedPane;
JSplitPane splitPane;
JScrollPane logScrlPane;
JSpinner fontSize;
JTextField findField;
JTextField replaceField;
JLabel folderLabel;
JLabel cmdStrLabel;

final int _wndW = 750;
final int _wndH = 700;

String codeDir = "";
String cmdStrExe = "";

Process process = null;
File file;

void newFileAction() {
  String folderPath = "";
  String folderName = "";
  String fileName = "";
  String newFilePath = "";
  
  JFileChooser fileChooser = new JFileChooser(codeDir);
  fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
  fileChooser.setDialogTitle("Create sketch folder as:");
  int result = fileChooser.showSaveDialog(frame);
  if (result == JFileChooser.APPROVE_OPTION) {
    File newDir = fileChooser.getSelectedFile();
    folderName = newDir.getName();
    fileName = folderName + ".pde";
    boolean created = newDir.mkdir();
    logArea.append("Dir created:" + created + "\n");   
    Path folder = newDir.toPath();
    folderPath = folder.toString();
    newFilePath = folderPath + "/" + fileName;
    frame.setTitle(fileName);
  } else {
    logArea.append("New file cancelled.");
  }
  txtArea();
  tabbedPane.addTab(fileName, txtScrlPane);
  int tabIndex = tabbedPane.indexOfTab(fileName);
  tabbedPane.setSelectedIndex(tabIndex);
  files.add(tabIndex, newFilePath);
  txtAreas.add(tabIndex, txtArea);  
}

String fileExtension(String fileName) {
  int lastDotIndex = fileName.lastIndexOf('.');
  if (lastDotIndex == -1 || lastDotIndex == 0) {
    return ""; // No extension
  }
  return fileName.substring(lastDotIndex + 1);
}

void openAction() {
  JFileChooser fileChooser = new JFileChooser(codeDir);
  int i = fileChooser.showOpenDialog(fileChooser);
  if (i == JFileChooser.APPROVE_OPTION) {
    file = fileChooser.getSelectedFile();
    String filePath = file.getPath();
    if (!fileExtension(filePath).equals("pde")) {
      Object[] options = {"CANCEL"};
      JOptionPane.showOptionDialog(frame, "Please make another selection. \n Editor requires .py extension.", "Extension Not Supported.",
        JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]);
      return;
    }

    frame.setTitle(filePath);
    txtArea();
    tabbedPane.addTab(file.getName(), txtScrlPane);
    int tabIndex = tabbedPane.indexOfTab(file.getName());
    tabbedPane.setSelectedIndex(tabIndex);
    files.add(tabIndex, filePath);
    txtAreas.add(tabIndex, txtArea);
    try {
      BufferedReader buffer = new BufferedReader(new FileReader(filePath));
      String s = "", s1 = "";
      while ((s = buffer.readLine())!= null) {
        s1 += s + "\n";
      }
      txtArea.setText(s1);
      buffer.close();
    }
    catch (Exception ex) {
      logArea.append(ex + "\n");
    }
  } else {
    logArea.append("Open cancelled.");
  }
}

void saveAction() {
  String saveFilePath = "";
  int selectedIndex = tabbedPane.getSelectedIndex();
  for (int i = 0; i < files.size(); i++) {
    String path = files.get(i);
    if (i == selectedIndex) {
      saveFilePath = path;
    }
  }
  for (int i = 0; i < txtAreas.size(); i++) {
    RSyntaxTextArea textArea = txtAreas.get(i);
    if (i == selectedIndex) {
      txtArea = textArea;
    }
  }
  if (saveFilePath != null) {
    try {
      String content = txtArea.getText();
      FileWriter fw = new FileWriter(saveFilePath) ;
      BufferedWriter bw = new BufferedWriter(fw);
      bw.write(content);
      bw.close();
    }
    catch (IOException e) {
      logArea.append(e + "\n");
    }
  } else {
    saveAsAction();
  }
}

void saveAsAction() {
  String folderPath = "";
  String folderName = "";
  String fileName = "";
  String saveAsFilePath = "";

  JFileChooser fileChooser = new JFileChooser(codeDir);
  fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
  fileChooser.setDialogTitle("Save sketch folder as:");
  int result = fileChooser.showSaveDialog(frame);
  if (result == JFileChooser.APPROVE_OPTION) {
    File newDir = fileChooser.getSelectedFile();
    folderName = newDir.getName();
    fileName = folderName + ".pde";
    boolean created = newDir.mkdir();
    logArea.append("Dir created:" + created + "\n");
    Path folder = newDir.toPath();
    folderPath = folder.toString();
    saveAsFilePath = folderPath + "/" + fileName;
    frame.setTitle(fileName);
    try {
      // Make certain we have the selected text area
      int selectedIndex = tabbedPane.getSelectedIndex();
      for (int i = 0; i < txtAreas.size(); i++) {
        RSyntaxTextArea textArea = txtAreas.get(i);
        if (i == selectedIndex) {
          txtArea = textArea;
        }
      }
      String content = txtArea.getText();
      txtArea(); // create a new textArea
      tabbedPane.addTab(fileName, txtScrlPane);
      int tabIndex = tabbedPane.indexOfTab(fileName);
      tabbedPane.setSelectedIndex(tabIndex);

      txtAreas.add(tabIndex, txtArea); // Add to ArrayList
      files.add(tabIndex, saveAsFilePath); //Add to ArrayList

      FileWriter fw = new FileWriter(saveAsFilePath) ;
      BufferedWriter bw = new BufferedWriter(fw);
      bw.write(content);
      bw.close();
      // Save it and then read it back into tab
      BufferedReader buffer = new BufferedReader(new FileReader(saveAsFilePath));
      String s = "", s1 = "";
      while ((s = buffer.readLine())!= null) {
        s1 += s + "\n";
      }
      txtArea.setText(s1);
      buffer.close();
    }
    catch (IOException e) {
      logArea.append("Error:" + e + "\n");
    }
  } else {
    logArea.append("No directory selected.");
  }
}

void printerAction() {
  PrinterJob job = PrinterJob.getPrinterJob();
  PageFormat orient = job.defaultPage();
  orient.setOrientation(PageFormat.PORTRAIT);
  job.setPrintable(new MyPrinter(), job.defaultPage());
  if (job.printDialog()) {
    try {
      job.print();
    }
    catch (Exception e) {
      println("Printer error: ",e);
    }
  }
}

void findAction() {
  findDialog = new FindDialog();
}

void runAction() {
  String runFilePath = "";
  String folderPath = "";
  int selectedIndex = tabbedPane.getSelectedIndex();
  for (int i = 0; i < files.size(); i++) {
    String path = files.get(i);
    if (i == selectedIndex) {
      runFilePath = path;
     // logArea.append("runAction : runFilePath = " + runFilePath + "\n");
    }
  }
  int lastSlashIndex = runFilePath.lastIndexOf('/');
  if (lastSlashIndex != -1) {
    folderPath = runFilePath.substring(0, lastSlashIndex);
   // logArea.append("runAction : folderPath = " + folderPath + "\n");
  } else {
    logArea.append("No path separator found.");
  }

  String sketchStr = "--sketch=" + folderPath;

  ProcessBuilder processBuilder = new ProcessBuilder(cmdStrExe, sketchStr, " --run");
  try {
    process = processBuilder.start();
    BufferedReader stdIn = new BufferedReader(new InputStreamReader(process.getInputStream()));
    BufferedReader stdErr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
    String outStr = "";
    while ((outStr = stdIn.readLine()) != null) {
      logArea.append(outStr + "\n");
    }
    String errStr = "";
    while ((errStr = stdErr.readLine()) != null) {
      logArea.append(errStr + "\n");
    }
    // Wait for the process to complete and get its exit value
    int exitCode = process.waitFor();
    logArea.append("\nProcess exited with code: " + exitCode + "\n");
  }
  catch (IOException | InterruptedException e) {
    logArea.append(e + "\n");
  }
}

void clearLogAction() {
  logArea.setText("");
}

void runBtnAction() {
  thread("runAction");
}

void removeTabProcedure() {
  int selectedIndex = tabbedPane.getSelectedIndex();
  if (selectedIndex > 0) {
    tabbedPane.removeTabAt(selectedIndex);
    files.remove(selectedIndex); // Also remove selectedIndex from ArrayLists
    txtAreas.remove(selectedIndex);
  } else {
    JOptionPane.showMessageDialog(frame, "Unable to remove baseline tab.");
  }
}

void quitBtnAction() {
  exit();
}

void openExecutableChooser() {
  JFileChooser fileChooser = new JFileChooser();
  int result = fileChooser.showOpenDialog(frame); // Or showSaveDialog(frame)
  if (result == JFileChooser.APPROVE_OPTION) {
    File selectedDirectory = fileChooser.getSelectedFile();
    cmdStrExe = selectedDirectory.getAbsolutePath();
    cmdStrLabel.setText("cmdStr:"+cmdStrExe);
    String[] settingsStr = {cmdStrExe, codeDir};
    saveStrings(sketchPath()+"/settings.txt", settingsStr);
  } else {
    cmdStrLabel.setText("No file selected.");
  }
}

void openFolderChooser() {
  JFileChooser fileChooser = new JFileChooser();
  fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); // Set to select directories only
  int result = fileChooser.showOpenDialog(frame); // Or showSaveDialog(frame)
  if (result == JFileChooser.APPROVE_OPTION) {
    File selectedDirectory = fileChooser.getSelectedFile();
    codeDir = selectedDirectory.getAbsolutePath();
    folderLabel.setText("fldr:"+codeDir);
    String[] settingsStr = {cmdStrExe, codeDir};
    saveStrings(sketchPath()+"/settings.txt", settingsStr);
  } else {
    folderLabel.setText("No directory selected.");
  }
}

void runBtn(int x, int y, int w, int h) {
  runBtn = new RunButton();
  runBtn.setBounds(x, y, w, h);
  runBtn.setToolTipText("Run code.");
  runBtn.addMouseListener(new MouseAdapter() {
    public void mousePressed(MouseEvent me) {
      runBtnAction();
    }
  }
  );
  frame.add(runBtn);
  runBtn.repaint();
}

void quitBtn(int x, int y, int w, int h) {
  quitBtn = new QuitButton();
  quitBtn.setBounds(x, y, w, h);
  quitBtn.setToolTipText("Quit editor.");
  quitBtn.addMouseListener(new MouseAdapter() {
    public void mousePressed(MouseEvent me) {
      quitBtnAction();
    }
  }
  );
  frame.add(quitBtn);
  quitBtn.repaint();
}

void fontSizeLabel(int x, int y, int w, int h) {
  JLabel fontSizeLabel = new JLabel("Font size:");
  fontSizeLabel.setHorizontalAlignment(JLabel.LEFT);
  fontSizeLabel.setBounds(x, y, w, h);
  frame.add(fontSizeLabel);
}

void cmdStrLabel(int x, int y, int w, int h) {
  cmdStrLabel = new JLabel("cmdStr:"+cmdStrExe);
  cmdStrLabel.setHorizontalAlignment(JLabel.LEFT);
  cmdStrLabel.setForeground(Color.BLUE);
  Font myFont = new Font("Menlo", Font.ITALIC, 12);
  cmdStrLabel.setFont(myFont);
  cmdStrLabel.setBounds(x, y, w, h);
  frame.add(cmdStrLabel);
}

void folderLabel(int x, int y, int w, int h) {
  folderLabel = new JLabel("fldr:"+codeDir);
  folderLabel.setHorizontalAlignment(JLabel.LEFT);
  folderLabel.setForeground(Color.RED);
  Font myFont = new Font("Menlo", Font.ITALIC, 12);
  folderLabel.setFont(myFont);
  folderLabel.setBounds(x, y, w, h);
  frame.add(folderLabel);
}

void fontSizeSpinner(int x, int y, int w, int h) {
  //syntax: (init, min, max, step)
  SpinnerModel value = new SpinnerNumberModel(13, 8, 30, 1);
  fontSize = new JSpinner(value);
  fontSize.setBounds(x, y, w, h);
  fontSize.setToolTipText("<html>For current and <br>successive files.</html>");
  ((JSpinner.DefaultEditor) fontSize.getEditor()).getTextField().setEditable(false);
  fontSize.addChangeListener(e -> {
    int fntSizeValue = (Integer)fontSize.getValue();
    Font currentFont = txtArea.getFont();
    Font newFont = currentFont.deriveFont((float)fntSizeValue);
    txtArea.setFont(newFont);
  }
  );
  frame.add(fontSize);
}

void txtArea() {
  txtArea = new RSyntaxTextArea();
  txtArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
  // txtArea.setCodeFoldingEnabled(true);
  txtArea.setBracketMatchingEnabled(true);
  txtArea.setFont(new Font("Menlo", Font.PLAIN, (Integer)fontSize.getValue()));
  txtScrlPane = new RTextScrollPane(txtArea);
  txtArea.setEditable(true);
  txtArea.setLineWrap(false);
  txtArea.setWrapStyleWord(true);
  txtArea.repaint();
}

void logArea() {
  logArea = new JTextArea();
  logScrlPane = new JScrollPane(logArea);
  logArea.setEditable(true);
  logArea.setFont(new Font("Menlo", Font.PLAIN, 12));
  logArea.setLineWrap(false);
  logArea.setWrapStyleWord(true);
  logArea.repaint();
}

void tabbedPane() {
  tabbedPane = new JTabbedPane();
  tabbedPane.addChangeListener(e -> {
    int index = tabbedPane.getSelectedIndex();
    String selectedFileStr = tabbedPane.getTitleAt(index);
    frame.setTitle(selectedFileStr);
  }
  );
}

//Create a split pane with two scroll panes in it.
void splitPane() {
  splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, tabbedPane, logScrlPane);
  splitPane.setBounds(0, 50, _wndW, _wndH - 80);
  splitPane.setOneTouchExpandable(true);
  frame.add(splitPane);
  splitPane.setDividerLocation(_wndH - 210);
  splitPane.repaint();
  //Provide minimum sizes for the two components in the split pane
  Dimension minimumSize = new Dimension(_wndW, 50);
  txtScrlPane.setMinimumSize(minimumSize);
  logScrlPane.setMinimumSize(minimumSize);
}

void checkForSettings() {
  String filePath = sketchPath()+"/settings.txt";
  File file = new File(filePath);
  if (file.exists()) {
    String[] s = loadStrings(sketchPath()+"/settings.txt");
    for (int i = 0; i < s.length; i++) {
      if (i == 0) {
        cmdStrExe = s[0];
      }
      if (i == 1) {
        codeDir = s[1];
      }
    }
  } else {
    println("The file does not exist but will be created.");
    String[] settingsStr = {"null", "null"};
    saveStrings(sketchPath()+"/settings.txt", settingsStr);
  }
}

void buildWnd() {
  checkForSettings();
  menu = new Menu();
  fontSizeSpinner(90, 20, 50, 24);
  tabbedPane();
  txtArea(); // RSyntaxTextArea
  logArea();
  splitPane();
  runBtn(30, 5, 34, 34);
  fontSizeLabel(85, 0, 75, 24);
  cmdStrLabel(170, 0, 510, 24);
  folderLabel(170, 20, 510, 24);
  quitBtn(_wndW - 50, 5, 34, 34);

  frame.setVisible(true);
}

void setup() {
  surface.setVisible(false);
  frame = new JFrame();
  frame.setBounds(100, 100, _wndW, _wndH);
  frame.setTitle("Processing Editor");
  frame.setLayout(null);
  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  frame.addComponentListener(new java.awt.event.ComponentAdapter() {
    void componentResized(ComponentEvent e) {
      JFrame tmp = (JFrame)e.getSource(); // For window resizing
      splitPane.setBounds(0, 50, tmp.getWidth(), tmp.getHeight() - 80);
    }
  }
  );
  SwingUtilities.invokeLater(() -> {
    buildWnd(); // Build components on EventDispatchThread(EDT).
  }
  );
}

```

**“MenuBar” Tab:**

```processing
class Menu implements ActionListener {
  
  JMenu fileMenu, editMenu, cmdMenu, settingsMenu, clearMenu, tabMenu;
  JMenuItem newMenuItem,openMenuItem, saveMenuItem, saveAsMenuItem, printMenuItem;
  JMenuItem cut, copy, paste, selectAll, findMenuItem, runPython;
  JMenuItem folderMenuItem, cmdStrMenuItem, clearLog, removeTabMenuItem;

  JMenuBar menuBar = new JMenuBar();

  Menu() {
    fileMenu = new JMenu("File");
    newMenuItem = new JMenuItem("New");
    openMenuItem = new JMenuItem("Open...");
    openMenuItem.setAccelerator(KeyStroke.getKeyStroke('O', META_DOWN_MASK));
    saveMenuItem = new JMenuItem("Save");
    saveMenuItem.setAccelerator(KeyStroke.getKeyStroke('S', META_DOWN_MASK));
    saveAsMenuItem = new JMenuItem("SaveAs...");
    printMenuItem = new JMenuItem("Print...");
    printMenuItem.setAccelerator(KeyStroke.getKeyStroke('P', META_DOWN_MASK));    
    
    editMenu = new JMenu("Edit");
    cut = new JMenuItem("Cut");
    cut.setAccelerator(KeyStroke.getKeyStroke('X', META_DOWN_MASK));
    copy = new JMenuItem("Copy");
    copy.setAccelerator(KeyStroke.getKeyStroke('C', META_DOWN_MASK));
    paste = new JMenuItem("Paste");
    paste.setAccelerator(KeyStroke.getKeyStroke('V', META_DOWN_MASK));
    selectAll = new JMenuItem("SelectAll");
    selectAll.setAccelerator(KeyStroke.getKeyStroke('A', META_DOWN_MASK));
    findMenuItem = new JMenuItem("Find");
    findMenuItem.setAccelerator(KeyStroke.getKeyStroke('F', META_DOWN_MASK));
     
    cmdMenu = new JMenu("Cmd");
    runPython = new JMenuItem("Run");
    runPython.setAccelerator(KeyStroke.getKeyStroke('R', META_DOWN_MASK));
    
    clearMenu = new JMenu("Clear");
    clearLog = new JMenuItem("Log");
    
    settingsMenu = new JMenu("Settings");
    cmdStrMenuItem = new JMenuItem("CmdStr exe...");
    folderMenuItem = new JMenuItem("Code Folder...");
    
    tabMenu = new JMenu("Tab");
    removeTabMenuItem = new JMenuItem("Remove Selected Tab");
        
    fileMenu.add(newMenuItem);
    fileMenu.add(openMenuItem);
    fileMenu.add(saveMenuItem);
    fileMenu.add(saveAsMenuItem);
    fileMenu.addSeparator();
    fileMenu.add(printMenuItem);
    
    editMenu.add(cut);
    editMenu.add(copy);
    editMenu.add(paste);
    editMenu.add(selectAll);
    editMenu.addSeparator();
    editMenu.add(findMenuItem);
    
    cmdMenu.add(runPython);

    clearMenu.add(clearLog);
    
    settingsMenu.add(cmdStrMenuItem);
    settingsMenu.add(folderMenuItem);
    
    tabMenu.add(removeTabMenuItem);
        
    menuBar.add(fileMenu);
    menuBar.add(editMenu);
    menuBar.add(cmdMenu);
    menuBar.add(clearMenu);
    menuBar.add(settingsMenu);
    menuBar.add(tabMenu);
     
    newMenuItem.addActionListener(this);
    openMenuItem.addActionListener(this);
    saveMenuItem.addActionListener(this);
    saveAsMenuItem.addActionListener(this);
    printMenuItem.addActionListener(this);
        
    cut.addActionListener(this);
    copy.addActionListener(this);
    paste.addActionListener(this);
    selectAll.addActionListener(this);
    findMenuItem.addActionListener(this);
    
    runPython.addActionListener(this);
    
    clearLog.addActionListener(this);
    
    cmdStrMenuItem.addActionListener(this);
    folderMenuItem.addActionListener(this);
    removeTabMenuItem.addActionListener(this);
    
    frame.setJMenuBar(menuBar);   
  }
  
  public void actionPerformed(ActionEvent evnt) {
    if (evnt.getSource() == newMenuItem)
      newFileAction();
    if (evnt.getSource() == openMenuItem)
      openAction();
    if (evnt.getSource() == saveMenuItem)
      saveAction();
    if (evnt.getSource() == saveAsMenuItem)
      saveAsAction();
    if (evnt.getSource() == printMenuItem)
      printerAction();    
      
    if (evnt.getSource() == cut)
      txtArea.cut();
    if (evnt.getSource() == paste)
      txtArea.paste();
    if (evnt.getSource() == copy)
      txtArea.copy();
    if (evnt.getSource() == selectAll)
      txtArea.selectAll();
     if (evnt.getSource() == findMenuItem)
      findAction(); 
      
    if (evnt.getSource() == runPython)
      runBtnAction();

    if (evnt.getSource() == clearLog)
      clearLogAction();  
      
    if (evnt.getSource() == cmdStrMenuItem)
      openExecutableChooser();
    if (evnt.getSource() == folderMenuItem)
      openFolderChooser();
      
    if (evnt.getSource() == removeTabMenuItem)
      removeTabProcedure();  
  }

}
Menu menu;

```

**“Find” Tab:**

```processing
class FindDialog {
  JDialog dlg;
  JTextField findFld;
  JTextField replaceFld;

  void cancelBtnAction() {
    dlg.setVisible(false);
  }

  void replaceBtnAction() {
    // Make certain we have the selected text area
    int selectedIndex = tabbedPane.getSelectedIndex();
    for (int i = 0; i < txtAreas.size(); i++) {
      RSyntaxTextArea textArea = txtAreas.get(i);
      if (i == selectedIndex) {
        txtArea = textArea;
      }
    }
    String textToFind = findFld.getText();
    String textToReplace = replaceFld.getText();
    String currentText = txtArea.getText();
    String newText = currentText.replaceAll(textToFind, textToReplace);
    txtArea.setText(newText);
  }

  FindDialog() {
    dlg = new JDialog(frame, "Find:");
    dlg.setLayout(null);
    dlg.setBounds(50, 50, 400, 200);
    JLabel findLabel = new JLabel("Find:");
    findLabel.setBounds(20, 20, 40, 24);
    dlg.add(findLabel);
    findFld = new JTextField();
    findFld.setBounds(60, 20, 230, 24);
    dlg.add(findFld);
    JLabel replaceLabel = new JLabel("Replace:");
    replaceLabel.setBounds(20, 60, 65, 24);
    dlg.add(replaceLabel);
    replaceFld = new JTextField();
    replaceFld.setBounds(85, 60, 240, 24);
    dlg.add(replaceFld);

    JButton replaceBtn = new JButton("Replace All");
    replaceBtn.setBounds(30, 120, 130, 24);
    dlg.add(replaceBtn);
    replaceBtn.addActionListener(e -> {
      replaceBtnAction();
    }
    );

    JButton cancelBtn = new JButton("Cancel");
    cancelBtn.setBounds(180, 120, 130, 24);
    dlg.add(cancelBtn);
    cancelBtn.addActionListener(e -> {
      cancelBtnAction();
    }
    );
    dlg.setVisible(true);
  }
}

FindDialog findDialog;

```

**“Printer” Tab:**

```processing
class MyPrinter implements Printable {
  
  String filePath;
  int[] pageBreaks; // array of page break line positions.
  ArrayList<String> txtLines = new ArrayList<String>();

  void initTextLines() {
    int selectedIndex = tabbedPane.getSelectedIndex();
    for (int i = 0; i < files.size(); i++) {
      String path = files.get(i);
      if (i == selectedIndex) {
        filePath = path;
      }
    }

    try {
      BufferedReader buffer = new BufferedReader(new FileReader(filePath));
      String s1 = "", s2 = "";
      while ((s1 = buffer.readLine())!= null) {
        s2 = s1 + "\n";
        txtLines.add(s2); // add to ArrayList
      }
      buffer.close();
    }
    catch (Exception ex) {
      ex.printStackTrace();
    }
  }

  public int print(Graphics g, PageFormat pf, int pageIndex) throws PrinterException {

    Font font = new Font("Menlo", Font.PLAIN, 12);
    FontMetrics metrics = g.getFontMetrics(font);
    int lineHeight = metrics.getHeight();

    if (pageBreaks == null) {
      initTextLines();
      int linesPerPage = 44; // Hard coded to make it fit on page better.
      // int linesPerPage = (int)(pf.getImageableHeight()/lineHeight);
      int numBreaks = (txtLines.size()-1)/linesPerPage;
      pageBreaks = new int[numBreaks];
      for (int b=0; b<numBreaks; b++) {
        pageBreaks[b] = (b+1)*linesPerPage;
      }
    }

    if (pageIndex > pageBreaks.length) {
      return NO_SUCH_PAGE;
    }

    // User (0,0) is typically outside the imageable area.
    // Translate by the X and Y values in the PageFormat to avoid clipping
     
    Graphics2D g2d = (Graphics2D)g;
    g2d.translate(pf.getImageableX(), pf.getImageableY());
    
     // Increment 'y' position by lineHeight for each line.     
    int x = 40; // left margin
    int y = 40; // top Margin
    int start = (pageIndex == 0) ? 0 : pageBreaks[pageIndex-1];
    int end = (pageIndex == pageBreaks.length)
      ? txtLines.size() : pageBreaks[pageIndex];
    for (int line=start; line<end; line++) {
      y += lineHeight;
      g.drawString(txtLines.get(line), x, y);
    }

    // Tell caller that this page is part of the printed document
    return PAGE_EXISTS;
  }
}

```

**“Quit” Tab:**

```processing
class QuitButton extends JPanel {
  
  int radius = 13; // Size of Btn

  QuitButton() {
    setOpaque(false); 
  }

  void paintComponent(Graphics g) {
    Graphics2D g2 = (Graphics2D)g;
    super.paintComponent(g);
    BasicStroke bs = new BasicStroke(6.0);
    Font myFont = new Font("Menlo",Font.BOLD,22);
    g2.setStroke(bs);
    g2.setColor(Color.lightGray);
    g2.drawOval(getWidth()/2 - radius, getHeight()/2 - radius, radius * 2, radius * 2);
// g2.setColor(new Color(209, 209, 209));
    g2.setColor(new Color(66,66,66));
    g2.fillOval(getWidth()/2 - radius, getHeight()/2 - radius, radius * 2, radius * 2);
    g2.setColor(Color.white);
    g2.setFont(myFont);
    g2.drawString("Q",11,25);
  }
}

QuitButton quitBtn;

```

**“Run” Tab:**

```processing
class RunButton extends JPanel {
  
  int radius = 13; // Size of Btn
  
  // Coordinates for triangle
  int x[] = {16, 16, 18};
  int y[] = {16, 18, 17};

  RunButton() {
    setOpaque(false);   
  }

  void paintComponent(Graphics g) {
    Graphics2D g2 = (Graphics2D)g;
    super.paintComponent(g);
    BasicStroke bs = new BasicStroke(6.0);
    g2.setStroke(bs);
    g2.setColor (Color.lightGray);
    g2.drawOval(getWidth()/2 - radius, getHeight()/2 - radius, radius * 2, radius * 2);
    g2.setColor (new Color(24, 70, 183));
    g2.fillOval(getWidth()/2 - radius, getHeight()/2 - radius, radius * 2, radius * 2);
    g2.setColor(Color.white);
    g2.drawPolygon(x, y, 3);
  }
}

RunButton runBtn;

```

**processing-java.exe (MacOS)**

 ![exe](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/e/6/e6bc2be39a4dc0f39e3ca21a3605a1d57a0c46e5.png)

**Output:**

 ![out](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/f/6/f6525d89f81ee39e681b297e6a24521977cd65cb.jpeg)

---

<div class="post-metadata">

**Author:** ![glv](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/glv/32/18785_2.png) [@glv](https://discourse.processing.org/u/glv)\
**Post date:** [November 12, 2025, 6:54pm UTC](https://discourse.processing.org/t/alternate-processing-editor-using-processing-java-exe-on-the-command-line/47530/2 "2025-11-12T18:54:33Z")

</div>

Hello @svan,

Cool beans!

I managed to get this to run with Windows with some necessary modifications:

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/f/c/fcc49a963e7bd1a19a5050e06b4dcfd4cd2ef0c8.png)

Changes made here:

> **void runAction()**
>
> ```processing
> void runAction() {
> String runFilePath = "";
> String folderPath = "";
> int selectedIndex = tabbedPane.getSelectedIndex();
> for (int i = 0; i < files.size(); i++) {
> String path = files.get(i);
> if (i == selectedIndex) {
> runFilePath = path;
> println(path);
> // logArea.append("runAction : runFilePath = " + runFilePath + "\n");
> }
> }
> int lastSlashIndex = runFilePath.lastIndexOf(File.separator);
> if (lastSlashIndex != -1) {
> folderPath = runFilePath.substring(0, lastSlashIndex);
> // logArea.append("runAction : folderPath = " + folderPath + "\n");
> } else {
> logArea.append("No path separator found.");
> }
> 
> if (lastSlashIndex != -1) {
> folderPath = runFilePath.substring(0, lastSlashIndex);
> } else {
> folderPath = System.getProperty("user.dir"); // Default to current directory
> println(folderPath);
> logArea.append("No path separator found. Using current directory: " + folderPath + "\n");
> }
> println(codeDir);
> String sketchStr = "--sketch=" + codeDir;
>   
> logArea.append("Generated command: " + cmdStrExe + " cli " + sketchStr + " --run\n");
> 
> ProcessBuilder processBuilder = new ProcessBuilder(cmdStrExe, "cli", sketchStr, "--run");
>   
> try {
> process = processBuilder.start();
> BufferedReader stdIn = new BufferedReader(new InputStreamReader(process.getInputStream()));
> BufferedReader stdErr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
> String outStr = "";
> while ((outStr = stdIn.readLine()) != null) {
> logArea.append(outStr + "\n");
> println(outStr);  
> }
> String errStr = "";
> while ((errStr = stdErr.readLine()) != null) {
> logArea.append(errStr + "\n");
> }
> // Wait for the process to complete and get its exit value
> int exitCode = process.waitFor();
> logArea.append("\nProcess exited with code: " + exitCode + "\n");
> }
> catch (IOException | InterruptedException e) {
> logArea.append(e + "\n");
> }
> }
> 
> ```

This was a quick challenge. I did not comment changes. You can do a file compare. `:)`

> [@svan](#):
>
> My presumption is that _processing-java.exe_ from the command line will function even if the official Processing IDE is not on the user’s system;

The Processing PDE files are required.

> [@svan](#):
>
> I was unable to get it to run on other platforms due to difficulty finding either processing-java or processing-cli

Processing 4.3.4 is the last one to use `processing-java.exe` for Windows and works with that version. It did not work with Processing 4.4.10 or standalone.

This does not exist as a file:

```auto
processing-cli

```

The correct usage of the `cli` command is in example I provided.

> **Windows Terminal (command prompt) output**
>
> ```auto
> C:\Users\GLV>processing --help | more
> Usage: processing [<options>] [<sketches>]... <command> [<args>]...
> 
> Start the Processing IDE
> 
> Options:
> -v, --version Print version information
> -h, --help Show this message and exit
> 
> Arguments:
> <sketches> Sketches to open
> 
> Commands:
> lsp Start the Processing Language Server
> cli
> contributions Manage Processing contributions
> sketchbook Manage the sketchbook
> sketch Manage a Processing sketch
> 
> C:\Users\GLV>processing cli --help | more
> 
> Command line edition for Processing 4.4.10 (Java Mode)
> 
> --help Show this help text. Congratulations.
> 
> --sketch=<name> Specify the sketch folder (required)
> --output=<name> Specify the output folder (optional and
> cannot be the same as the sketch folder.)
> 
> --force The sketch will not build if the output
> folder already exists, because the contents
> will be replaced. This option erases the
> folder first. Use with extreme caution!
> 
> --build Preprocess and compile a sketch into .class files.
> --run Preprocess, compile, and run a sketch.
> --present Preprocess, compile, and run a sketch in presentation mode.
> 
> --export Export an application.
> --variant Specify the platform and architecture (Export only).
> --no-java Do not embed Java.
> 
> Starting with 4.0, the --platform option has been removed
> because of the variety of platforms and architectures now available.
> Use the --variant option instead, for instance:
> 
> variant platform
> ------------- ---------------------------
> macos-x86_64 macOS (Intel 64-bit)
> macos-aarch64 macOS (Apple Silicon)
> windows-amd64 Windows (Intel 64-bit)
> linux-amd64 Linux (Intel 64-bit)
> linux-arm Linux (Raspberry Pi 32-bit)
> linux-aarch64 Linux (Raspberry Pi 64-bit)
> 
> The --build, --run, --present, or --export must be the final parameter
> passed to Processing. Arguments passed following one of those four will
> be passed through to the sketch itself, and therefore available to the
> sketch via the 'args' field. To pass options understood by PApplet.main(),
> write a custom main() method so that the preprocessor does not add one.
> https://github.com/processing/processing/wiki/Command-Line
> 
> ```

`:)`

---

<div class="post-metadata">

**Author:** ![svan](https://avatars.discourse-cdn.com/v4/letter/s/82dd89/32.png) [@svan](https://discourse.processing.org/u/svan)\
**Post date:** [November 12, 2025, 7:18pm UTC](https://discourse.processing.org/t/alternate-processing-editor-using-processing-java-exe-on-the-command-line/47530/3 "2025-11-12T19:18:33Z")

</div>

> [@glv](#):
>
> It is required.

I’ve never understood why some users are reluctant to download the Processing IDE; I suspect that it is because they want to use Processing code with a prototype board such as Raspberry and don’t have a lot of storage space and memory, but I have no direct proof of this and a lot of these users are reluctant to share the details of their setup. If that were the case, the command line technique that we are using would not work for them. _command-java.exe_ is an extremely small file that it compressible and can easily be passed from system to system, but it won’t work for Windows; its platform specific. Apparently going forward the command line code is contained in the Processing IDE code and will work cross platform, but I can’t see how this is beneficial for those reluctant to use the IDE to start with; by definition they won’t be able to use it for whatever project they are working on.

Thanks for porting this demo to Windows. You can center the ‘Q‘ on the quit button by tweaking that code under its tab.

---

<div class="post-metadata">

**Author:** ![glv](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/glv/32/18785_2.png) [@glv](https://discourse.processing.org/u/glv)\
**Post date:** [November 12, 2025, 8:42pm UTC](https://discourse.processing.org/t/alternate-processing-editor-using-processing-java-exe-on-the-command-line/47530/4 "2025-11-12T20:42:45Z")

</div>

Does this work on your macOS?

See the CLI section:  
_[Environment / Processing.org](https://processing.org/environment/#command-line-interface-cli)_

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/a/7/a7cf91f866f9a16c39d178b37a53e87af8d60bce.png)

And then try:

`Processing cli --help`

I am just curious!

References:  
_[Processing 4 processing-java binary? - #3 by stefterv](https://discourse.processing.org/t/processing-4-processing-java-binary/47498/3)_  
_[Processing 4 processing-java binary? - #10 by stefterv](https://discourse.processing.org/t/processing-4-processing-java-binary/47498/10)_

`:)`

---

<div class="post-metadata">

**Author:** ![svan](https://avatars.discourse-cdn.com/v4/letter/s/82dd89/32.png) [@svan](https://discourse.processing.org/u/svan)\
**Post date:** [November 12, 2025, 8:51pm UTC](https://discourse.processing.org/t/alternate-processing-editor-using-processing-java-exe-on-the-command-line/47530/5 "2025-11-12T20:51:01Z")

</div>

The first one works, the second one doesn’t. ‘processing-java‘ still works in Terminal on Mac. ‘Processing cli‘ does not.

---

<div class="post-metadata">

**Author:** ![svan](https://avatars.discourse-cdn.com/v4/letter/s/82dd89/32.png) [@svan](https://discourse.processing.org/u/svan)\
**Post date:** [November 13, 2025, 3:30am UTC](https://discourse.processing.org/t/alternate-processing-editor-using-processing-java-exe-on-the-command-line/47530/6 "2025-11-13T03:30:40Z")

</div>

Based on your post, I now have it working on Windows 11. The command line code is now in processing.exe and is different from MacOS; hence the use of processing.exe in lieu of processing-java.exe. Another difference is Windows uses back slashes in file paths instead of forward slashes like the Unix systems; therefore I had to change both newFileAction() and runAction() to use back slashes and use folderPath with _sketch=_ in runAction(). Actually when used in a string in Windows you have to use double back slashes as shown below.

The runAction() that works on my system follows:

```processing
void runAction() {
  String runFilePath = "";
  String folderPath = "";
  int selectedIndex = tabbedPane.getSelectedIndex();
  for (int i = 0; i < files.size(); i++) {
    String path = files.get(i);
    if (i == selectedIndex) {
      runFilePath = path;
      println(path);
      logArea.append("runAction : runFilePath = " + runFilePath + "\n");
    }
  }
  int lastSlashIndex = runFilePath.lastIndexOf('\\');
  if (lastSlashIndex != -1) {
    folderPath = runFilePath.substring(0, lastSlashIndex);
    logArea.append("runAction : folderPath = " + folderPath + "\n");
  } else {
    logArea.append("No path separator found.");
  }
  String sketchStr = "--sketch=" + folderPath;  
  logArea.append("Generated command: " + cmdStrExe + " cli " + sketchStr + " --run\n");
  ProcessBuilder processBuilder = new ProcessBuilder(cmdStrExe, "cli", sketchStr, "--run"); 
  try {
    process = processBuilder.start();
    BufferedReader stdIn = new BufferedReader(new InputStreamReader(process.getInputStream()));
    BufferedReader stdErr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
    String outStr = "";
    while ((outStr = stdIn.readLine()) != null) {
      logArea.append(outStr + "\n");
    println(outStr);  
  }
    String errStr = "";
    while ((errStr = stdErr.readLine()) != null) {
      logArea.append(errStr + "\n");
    }
    // Wait for the process to complete and get its exit value
    int exitCode = process.waitFor();
    logArea.append("\nProcess exited with code: " + exitCode + "\n");
  }
  catch (IOException | InterruptedException e) {
    logArea.append(e + "\n");
  }
}

```

**Output Windows11:**

 ![w11](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/0/e/0ee518e1cda2c6105e10080387f343e9d11fb8dd.png)

Any idea how to get that vertical scrollbar to not be clipped?

---

<div class="post-metadata">

**Author:** ![svan](https://avatars.discourse-cdn.com/v4/letter/s/82dd89/32.png) [@svan](https://discourse.processing.org/u/svan)\
**Post date:** [November 13, 2025, 5:34pm UTC](https://discourse.processing.org/t/alternate-processing-editor-using-processing-java-exe-on-the-command-line/47530/8 "2025-11-13T17:34:29Z")

</div>

$ Processing cli --help  
-bash: Processing: command not found

**Addendum:**

It’s two separate commands in Terminal:

1. cd /Applications/Processing.app/Contents/MacOS

2. ./Processing cli –help

---

<div class="post-metadata">

**Author:** ![glv](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/glv/32/18785_2.png) [@glv](https://discourse.processing.org/u/glv)\
**Post date:** [November 14, 2025, 8:58pm UTC](https://discourse.processing.org/t/alternate-processing-editor-using-processing-java-exe-on-the-command-line/47530/10 "2025-11-14T20:58:01Z")

</div>

> [@svan](#):
>
> Any idea how to get that vertical scrollbar to not be clipped?

Yes.

Minimal code:

```auto
import javax.swing.*;
import java.awt.*;
import org.fife.ui.rsyntaxtextarea.*;

RSyntaxTextArea txtArea;

void setup() {
  size(600, 400); // Set the Processing window size

  txtArea = new RSyntaxTextArea();
    
  RTextScrollPane scrollPane = new RTextScrollPane(txtArea);
  
  JFrame editorFrame = new JFrame("Editor");

  JPanel emptyPanel = new JPanel();
  emptyPanel.setPreferredSize(new Dimension(600, 100)); // 100px height for top panel
  
  editorFrame.add(emptyPanel, BorderLayout.NORTH); // Add top panel (100px high)
  editorFrame.add(scrollPane, BorderLayout.CENTER); // Take up remaining space
  editorFrame.setSize(600, 400); // Total window size 
  editorFrame.setVisible(true); // Show the window
}

```

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/0/1/019f38504387ac55ba876107bbd3ac94e0954c98.png)

`:)`

---

<div class="post-metadata">

**Author:** ![svan](https://avatars.discourse-cdn.com/v4/letter/s/82dd89/32.png) [@svan](https://discourse.processing.org/u/svan)\
**Post date:** [November 14, 2025, 10:50pm UTC](https://discourse.processing.org/t/alternate-processing-editor-using-processing-java-exe-on-the-command-line/47530/11 "2025-11-14T22:50:18Z")

</div>

Thanks, that’s what I get for not using layout:

> [@svan](#):
>
> `frame.setLayout(null);`

This works on Mac and Windows; replaced panel with a toolbar(no layout) other components used BorderLayout():

```processing
import javax.swing.*;
import java.awt.*;

import org.fife.ui.rsyntaxtextarea.*;
import org.fife.ui.rtextarea.*;

RSyntaxTextArea txtArea;
RTextScrollPane txtScrlPane;
JFrame frame;
JToolBar toolbar;

final int _wndW = 750;
final int _wndH = 700;

void setup() {
  surface.setVisible(false);
  txtArea = new RSyntaxTextArea();
  txtArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
  txtScrlPane = new RTextScrollPane(txtArea);
  frame = new JFrame("Editor");
  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  toolbar = new JToolBar();
  toolbar.setFloatable(false);
  toolbar.setLayout(null);
  toolbar.setRollover(true);
  toolbar.setPreferredSize(new Dimension(_wndW, 50));
  frame.add(toolbar, BorderLayout.NORTH);
  frame.add(txtScrlPane, BorderLayout.CENTER);
  frame.setBounds(100, 100, _wndW, _wndH);
  frame.setVisible(true);
  SwingUtilities.invokeLater(() -> {
    //buildWnd(); // Build components on EventDispatchThread(EDT).
  }
  );
}

```

This obviates the need for component listeners to handle resizes and vertical scroller is full width on Windows 11. Thanks for the suggestion.
