Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
2e9db0e
Update README.md
Stefterv Mar 25, 2025
9e241c7
added the image comparator which is the pixel matching algorithm
Vaivaswat2244 Sep 16, 2025
a5ded6a
added build.gradle file
Vaivaswat2244 Sep 16, 2025
8e7f7d0
added the test runner
Vaivaswat2244 Sep 16, 2025
2f18f21
added the simple test
Vaivaswat2244 Sep 16, 2025
c51a8e4
Revise README for Jetpack Compose migration strategy
Stefterv Sep 24, 2025
4e7183f
fixing the build issues
Vaivaswat2244 Oct 6, 2025
5934138
added junit as dependency
Vaivaswat2244 Oct 6, 2025
c4915dd
removing custom class implementation
Vaivaswat2244 Oct 6, 2025
d549a18
inclding visual-tests in settings
Vaivaswat2244 Oct 6, 2025
cac895a
fixed the overlapping cmd
Vaivaswat2244 Oct 6, 2025
803516f
cleaning
Vaivaswat2244 Oct 6, 2025
ace6f7b
adding packages
Vaivaswat2244 Oct 6, 2025
5e7f798
added updated screenshot structure
Vaivaswat2244 Oct 6, 2025
25a80ce
refactoring
Vaivaswat2244 Oct 6, 2025
4fcd95b
added tests in suits
Vaivaswat2244 Oct 6, 2025
f778c99
removed simple test
Vaivaswat2244 Oct 7, 2025
6224f8c
deleting earlier files
Vaivaswat2244 Oct 16, 2025
bf21131
updated the core/gradle file
Vaivaswat2244 Oct 16, 2025
86f8847
added the infrastructure
Vaivaswat2244 Oct 16, 2025
5a0b5e7
added some tests ported by p5js
Vaivaswat2244 Oct 16, 2025
fa72d2d
removing test rendering suite and its test file
Vaivaswat2244 Oct 16, 2025
d435e62
added screenshots
Vaivaswat2244 Oct 16, 2025
0d91001
config files
Vaivaswat2244 Oct 16, 2025
66071ac
fixed the pixeldensity to 1
Vaivaswat2244 Oct 21, 2025
fd72f79
Revert "fixed the pixeldensity to 1"
Vaivaswat2244 Oct 21, 2025
a342a77
fixed pixeldensity to 1
Vaivaswat2244 Oct 21, 2025
c080196
Merge branch 'visual-testing' into pr05-visualtests
Vaivaswat2244 Oct 21, 2025
4c190b3
Configure dependencyUpdates task in build.gradle.kts
Vaivaswat2244 Oct 22, 2025
60af862
removing rendering gradient screenshot
Vaivaswat2244 Oct 23, 2025
64c213f
Merge pull request #1012 from processing/textarea-research
SableRaf Oct 24, 2025
e03c099
Merge branch 'processing:main' into pr05-visualtests
Vaivaswat2244 Oct 24, 2025
eacaf77
General cleanup of `Base`
Stefterv Oct 22, 2025
59a7657
Move contributor list to CONTRIBUTORS.md (#1313)
SableRaf Nov 6, 2025
603b404
Update BUILD.md with build failure troubleshooting
SableRaf Nov 7, 2025
be2adb7
fixing the build issues
Vaivaswat2244 Oct 6, 2025
4f2c4dc
inclding visual-tests in settings
Vaivaswat2244 Oct 6, 2025
47992b3
updated the core/gradle file
Vaivaswat2244 Oct 16, 2025
a56d789
config files
Vaivaswat2244 Oct 16, 2025
2f56c69
Configure dependencyUpdates task in build.gradle.kts
Vaivaswat2244 Oct 22, 2025
a6bd275
fix rebasing
Vaivaswat2244 Nov 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"projectName": "processing4",
"projectOwner": "processing",
"files": [
"README.md"
"CONTRIBUTORS.md"
],
"imageSize": 120,
"contributorsPerLine": 6,
Expand Down
13 changes: 13 additions & 0 deletions BUILD.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,16 @@ You may see this warning in IntelliJ:
> `Duplicate content roots detected: '.../processing4/java/src'`

This happens because multiple modules reference the same source folder. It’s safe to ignore.


### Build Failed

If the build fails with `Permission denied` or `Could not copy file` errors, try cleaning the project.

Run:

```bash
./gradlew clean
```

Then, rebuild the project.
258 changes: 258 additions & 0 deletions CONTRIBUTORS.md

Large diffs are not rendered by default.

261 changes: 2 additions & 259 deletions README.md

Large diffs are not rendered by default.

366 changes: 178 additions & 188 deletions app/src/processing/app/Base.java

Large diffs are not rendered by default.

33 changes: 31 additions & 2 deletions app/src/processing/app/Processing.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,14 @@ import java.util.prefs.Preferences
import kotlin.concurrent.thread



/**
* This function is the new modern entry point for Processing
* It uses Clikt to provide a command line interface with subcommands
*
* If you want to add new functionality to the CLI, create a new subcommand
* and add it to the list of subcommands below.
*
*/
suspend fun main(args: Array<String>){
Processing()
.subcommands(
Expand All @@ -32,6 +39,10 @@ suspend fun main(args: Array<String>){
.main(args)
}

/**
* The main Processing command, will open the ide if no subcommand is provided
* Will also launch the `updateInstallLocations` function in a separate thread
*/
class Processing: SuspendingCliktCommand("processing"){
val version by option("-v","--version")
.flag()
Expand Down Expand Up @@ -61,7 +72,10 @@ class Processing: SuspendingCliktCommand("processing"){
}
}


/**
* A command to start the Processing Language Server
* This is used by IDEs to provide language support for Processing sketches
*/
class LSP: SuspendingCliktCommand("lsp"){
override fun help(context: Context) = "Start the Processing Language Server"
override suspend fun run(){
Expand All @@ -79,6 +93,11 @@ class LSP: SuspendingCliktCommand("lsp"){
}
}

/**
* A command to invoke the legacy CLI of Processing
* This is mainly for backwards compatibility with existing scripts
* that use the old CLI interface
*/
class LegacyCLI(val args: Array<String>): SuspendingCliktCommand("cli") {
override val treatUnknownOptionsAsArgs = true

Expand All @@ -99,6 +118,16 @@ class LegacyCLI(val args: Array<String>): SuspendingCliktCommand("cli") {
}
}

/**
* Update the install locations in preferences
* The install locations are stored in the preferences as a comma separated list of paths
* Each path is followed by a caret (^) and the version of Processing at that location
* This is used by other programs to find all installed versions of Processing
* works from 4.4.6 onwards
*
* Example:
* /path/to/processing-4.0^4.0,/path/to/processing-3.5.4^3.5.4
*/
fun updateInstallLocations(){
val preferences = Preferences.userRoot().node("org/processing/app")
val installLocations = preferences.get("installLocations", "")
Expand Down
3 changes: 3 additions & 0 deletions app/src/processing/app/UpdateCheck.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ public class UpdateCheck {

static private final long ONE_DAY = 24 * 60 * 60 * 1000;

public static void doCheck(Base base) {
new UpdateCheck(base);
}

public UpdateCheck(Base base) {
this.base = base;
Expand Down
6 changes: 0 additions & 6 deletions app/src/processing/app/contrib/ContributionManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -694,15 +694,9 @@ static private void clearRestartFlags(File root) {


static public void init(Base base) throws Exception {
// long t1 = System.currentTimeMillis();
// Moved here to make sure it runs on EDT [jv 170121]
contribListing = ContributionListing.getInstance();
// long t2 = System.currentTimeMillis();
managerFrame = new ManagerFrame(base);
// long t3 = System.currentTimeMillis();
cleanup(base);
// long t4 = System.currentTimeMillis();
// System.out.println("ContributionManager.init() " + (t2-t1) + " " + (t3-t2) + " " + (t4-t3));
}


Expand Down
12 changes: 0 additions & 12 deletions app/src/processing/app/contrib/ManagerFrame.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,27 +61,15 @@ public class ManagerFrame {
public ManagerFrame(Base base) {
this.base = base;

// TODO Optimize these inits... unfortunately it needs to run on the EDT,
// and Swing is a piece of s*t, so it's gonna be slow with lots of contribs.
// In particular, load everything and then fire the update events.
// Also, don't pull all the colors over and over again.
// long t1 = System.currentTimeMillis();
librariesTab = new ContributionTab(this, ContributionType.LIBRARY);
// long t2 = System.currentTimeMillis();
modesTab = new ContributionTab(this, ContributionType.MODE);
// long t3 = System.currentTimeMillis();
toolsTab = new ContributionTab(this, ContributionType.TOOL);
// long t4 = System.currentTimeMillis();
examplesTab = new ContributionTab(this, ContributionType.EXAMPLES);
// long t5 = System.currentTimeMillis();
updatesTab = new UpdateContributionTab(this);
// long t6 = System.currentTimeMillis();

tabList = new ContributionTab[] {
librariesTab, modesTab, toolsTab, examplesTab, updatesTab
};

// System.out.println("ManagerFrame.<init> " + (t2-t1) + " " + (t3-t2) + " " + (t4-t3) + " " + (t5-t4) + " " + (t6-t5));
}


Expand Down
14 changes: 13 additions & 1 deletion app/src/processing/app/syntax/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
# 🐉 Fixing this code: here be dragons. 🐉
# Replacing our custom version of JEditTextArea

Since 2025 we have started a migration of Swing to Jetpack Compose and we will eventually need to replace the JEditTextArea as well.

I think a good current strategy would be to start using `RSyntaxTextArea` for an upcoming p5.js mode. `RSyntaxTextArea` is a better maintained and well rounded library. As noted below, a lot of the current state management of the PDE is interetwined with the JEditTextArea implementation. This will force us to decouple the state management out of the `JEditTextArea` whilst also trying to keep backwards compatibility alive for Tweak Mode and the current implementation of autocomplete.

I also did some more research into the potential of using a JS + LSP based editor within Jetpack Compose but as of writing (early 2025) the only way to do so would be to embed chromium into the PDE through something like [Java-CEF]([url](https://github.com/chromiumembedded/java-cef)) and it looks like a PoC for Jetpack Compose Desktop exists [here](https://github.com/JetBrains/compose-multiplatform/blob/9cd413a4ed125bee5b624550fbd40a05061e912a/experimental/cef/src/main/kotlin/org/jetbrains/compose/desktop/browser/BrowserView.kt). Moving the entire PDE into an electron app would be essentially a rewrite which currrently is not the target.

Considering the current direction of the build-in LSP within Processing, I would say that creating a LSP based editor would be a good strategy going forward.

Research needs to be done on how much the Tweak Mode and autocompletion are _actually_ being used. Currently both these features are quite hidden and I suspect that most users actually move on to more advanced use-cases before they even discover such things. I would like to make both of these features much more prominent within the PDE to test if they are a good value add.

### Ben Fry's notes

Every few years, we've looked at replacing this package with [RSyntaxArea](https://github.com/bobbylight/RSyntaxTextArea), most recently with two attempts during the course of developing [Processing 4](https://github.com/processing/processing4/wiki/Processing-4), but probably dating back to the mid-2000s.

Expand Down
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ plugins {

// Set the build directory to not /build to prevent accidental deletion through the clean action
// Can be deleted after the migration to Gradle is complete

layout.buildDirectory = file(".build")

// Configure the dependencyUpdates task
Expand All @@ -27,4 +28,4 @@ tasks {
isNonStable(candidate.version) && !isNonStable(currentVersion)
}
}
}
}
45 changes: 30 additions & 15 deletions core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@ repositories {
maven { url = uri("https://jogamp.org/deployment/maven") }
}

sourceSets{
main{
java{
sourceSets {
main {
java {
srcDirs("src")
}
resources{
resources {
srcDirs("src")
exclude("**/*.java")
}
}
test{
java{
test {
java {
srcDirs("test")
}
}
Expand All @@ -33,13 +33,32 @@ dependencies {
implementation(libs.gluegen)

testImplementation(libs.junit)
testImplementation(libs.junitJupiter)
testImplementation(libs.junitJupiterParams)
testImplementation(libs.junitPlatformSuite)
testImplementation(libs.assertjCore)
}

mavenPublishing{
// Simple JUnit 5 configuration - let JUnit handle everything
tasks.test {
useJUnitPlatform() // JUnit discovers and runs all tests

// Only configuration, not orchestration
outputs.upToDateWhen { false }
maxParallelForks = 1

testLogging {
events("passed", "skipped", "failed", "started")
showStandardStreams = true
}
}

mavenPublishing {
publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL, automaticRelease = true)

signAllPublications()

pom{
pom {
name.set("Processing Core")
description.set("Processing Core")
url.set("https://processing.org")
Expand All @@ -59,21 +78,17 @@ mavenPublishing{
name.set("Ben Fry")
}
}
scm{
scm {
url.set("https://github.com/processing/processing4")
connection.set("scm:git:git://github.com/processing/processing4.git")
developerConnection.set("scm:git:ssh://git@github.com/processing/processing4.git")
}
}
}


tasks.test {
useJUnit()
}
tasks.withType<Jar> {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
tasks.compileJava{
tasks.compileJava {
options.encoding = "UTF-8"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 38 additions & 0 deletions core/test/processing/visual/src/core/BaselineManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package processing.visual.src.core;

import processing.core.PImage;

import java.util.List;

// Baseline manager for updating reference images
public class BaselineManager {
private VisualTestRunner tester;

public BaselineManager(VisualTestRunner tester) {
this.tester = tester;
}

public void updateBaseline(String testName, ProcessingSketch sketch, TestConfig config) {
System.out.println("Updating baseline for: " + testName);

// Capture new image
SketchRunner runner = new SketchRunner(sketch, config);
runner.run();
PImage newImage = runner.getImage();

// Save as baseline
String baselinePath = "__screenshots__/" +
testName.replaceAll("[^a-zA-Z0-9-_]", "-") +
"-" + detectPlatform() + ".png";
newImage.save(baselinePath);

System.out.println("Baseline updated: " + baselinePath);
}

private String detectPlatform() {
String os = System.getProperty("os.name").toLowerCase();
if (os.contains("mac")) return "darwin";
if (os.contains("win")) return "win32";
return "linux";
}
}
9 changes: 9 additions & 0 deletions core/test/processing/visual/src/core/ProcessingSketch.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package processing.visual.src.core;

import processing.core.PApplet;

// Interface for user sketches
public interface ProcessingSketch {
void setup(PApplet p);
void draw(PApplet p);
}
33 changes: 33 additions & 0 deletions core/test/processing/visual/src/core/TestConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package processing.visual.src.core;

// Test configuration class
public class TestConfig {
public int width = 800;
public int height = 600;
public int[] backgroundColor = {255, 255, 255}; // RGB
public long renderWaitTime = 100; // milliseconds
public double threshold = 0.1;

public TestConfig() {}

public TestConfig(int width, int height) {
this.width = width;
this.height = height;
}

public TestConfig(int width, int height, int[] backgroundColor) {
this.width = width;
this.height = height;
this.backgroundColor = backgroundColor;
}

public TestConfig setThreshold(double threshold) {
this.threshold = threshold;
return this;
}

public TestConfig setRenderWaitTime(long waitTime) {
this.renderWaitTime = waitTime;
return this;
}
}
45 changes: 45 additions & 0 deletions core/test/processing/visual/src/core/TestResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package processing.visual.src.core;

// Enhanced test result with detailed information
public class TestResult {
public String testName;
public boolean passed;
public double mismatchRatio;
public String error;
public boolean isFirstRun;
public ComparisonDetails details;

public TestResult(String testName, ComparisonResult comparison) {
this.testName = testName;
this.passed = comparison.passed;
this.mismatchRatio = comparison.mismatchRatio;
this.isFirstRun = comparison.isFirstRun;
this.details = comparison.details;
}

public static TestResult createError(String testName, String error) {
TestResult result = new TestResult();
result.testName = testName;
result.passed = false;
result.error = error;
return result;
}

private TestResult() {} // For error constructor

public void printResult() {
System.out.print(testName + ": ");
if (error != null) {
System.out.println("ERROR - " + error);
} else if (isFirstRun) {
System.out.println("BASELINE CREATED");
} else if (passed) {
System.out.println("PASSED");
} else {
System.out.println("FAILED (mismatch: " + String.format("%.4f", mismatchRatio * 100) + "%)");
if (details != null) {
details.printDetails();
}
}
}
}
Loading