Merge branch 'main' into gradle-plugin

This commit is contained in:
Stef Tervelde
2025-03-17 13:43:21 +01:00
52 changed files with 2841 additions and 258 deletions
+36 -15
View File
@@ -1,4 +1,4 @@
name: Pre-releases with Gradle
name: Branch Builds
on:
push:
paths-ignore:
@@ -6,31 +6,58 @@ on:
- '.all-contributorsrc'
jobs:
test:
runs-on: ubuntu-latest
name: Test Processing
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Install Java
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Build with Gradle
run: ./gradlew test
build:
name: Create Pre-release for ${{ matrix.os_prefix }} (${{ matrix.arch }})
name: (${{ matrix.os_prefix }}/${{ matrix.arch }}) Create Processing Build
runs-on: ${{ matrix.os }}
permissions:
contents: write
needs: test
strategy:
fail-fast: false
matrix:
include:
- os: [self-hosted, linux, ARM64]
- os: ubuntu-24.04-arm
os_prefix: linux
arch: aarch64
binary: processing*.snap
- os: ubuntu-latest
os_prefix: linux
arch: x64
binary: processing*.snap
- os: windows-latest
os_prefix: windows
arch: x64
binary: msi/Processing-*.msi
- os: macos-latest
os_prefix: macos
arch: x64
binary: dmg/Processing-*.dmg
- os: macos-latest
os_prefix: macos
arch: aarch64
binary: dmg/Processing-*.dmg
steps:
- name: Install Snapcraft
if: runner.os == 'Linux'
uses: samuelmeuli/action-snapcraft@v3
- name: Install LXD
if: runner.os == 'Linux'
uses: canonical/setup-lxd@main
- name: Checkout Repository
uses: actions/checkout@v4
- name: Install Java
@@ -41,19 +68,13 @@ jobs:
architecture: ${{ matrix.arch }}
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Build with Gradle
run: ./gradlew packageDistributionForCurrentOS
- name: Add instructions
if: ${{ matrix.os_prefix == 'macos' }}
run: |
echo "run 'xattr -d com.apple.quarantine Processing-${version}.dmg' to remove the quarantine flag" > ./app/build/compose/binaries/main/dmg/INSTRUCTIONS_FOR_TESTING.txt
- name: Add artifact
uses: actions/upload-artifact@v4
with:
name: processing-${{ github.ref_name }}-${{github.sha}}-${{ matrix.os_prefix }}-${{ matrix.arch }}-gradle
path: |
./app/build/compose/binaries/main/dmg/Processing-*.dmg
./app/build/compose/binaries/main/dmg/INSTRUCTIONS_FOR_TESTING.txt
./app/build/compose/binaries/main/msi/Processing-*.msi
./app/build/compose/binaries/main/deb/processing*.deb
name: processing-${{ matrix.os_prefix }}-${{ matrix.arch }}-br_${{ github.ref_name }}
retention-days: 1
path: app/build/compose/binaries/main/${{ matrix.binary }}
-37
View File
@@ -1,37 +0,0 @@
name: Maven Release
on:
workflow_dispatch:
inputs:
version:
description: 'Version to release'
required: true
default: '1.0.0'
jobs:
publish:
name: Create Processing Core Release on Maven Central
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Build with Gradle
run: ./gradlew publish
env:
MAVEN_CENTRAL_USERNAME: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
MAVEN_CENTRAL_PASSWORD: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_IN_MEMORY_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_IN_MEMORY_KEY_PASSWORD }}
ORG_GRADLE_PROJECT_version: ${{ github.event.inputs.version }}
+35 -15
View File
@@ -7,12 +7,26 @@ on:
- main
jobs:
build:
name: Create Pull Request Build for ${{ matrix.os_prefix }} (${{ matrix.arch }})
test:
runs-on: ubuntu-latest
name: Test Processing
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Install Java
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Build with Gradle
run: ./gradlew test
build:
name: (${{ matrix.os_prefix }}/${{ matrix.arch }}) Create Processing Build
runs-on: ${{ matrix.os }}
permissions:
pull-requests: write
contents: read
needs: test
strategy:
fail-fast: false
matrix:
@@ -20,19 +34,31 @@ jobs:
- os: ubuntu-24.04-arm
os_prefix: linux
arch: aarch64
binary: processing*.snap
- os: ubuntu-latest
os_prefix: linux
arch: x64
binary: processing*.snap
- os: windows-latest
os_prefix: windows
arch: x64
binary: msi/Processing-*.msi
- os: macos-latest
os_prefix: macos
arch: x64
binary: dmg/Processing-*.dmg
- os: macos-latest
os_prefix: macos
arch: aarch64
binary: dmg/Processing-*.dmg
steps:
- name: Install Snapcraft
if: runner.os == 'Linux'
uses: samuelmeuli/action-snapcraft@v3
- name: Install LXD
if: runner.os == 'Linux'
uses: canonical/setup-lxd@main
- name: Checkout Repository
uses: actions/checkout@v4
- name: Install Java
@@ -43,19 +69,13 @@ jobs:
architecture: ${{ matrix.arch }}
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Build with Gradle
run: ./gradlew packageDistributionForCurrentOS
- name: Add instructions
if: ${{ matrix.os_prefix == 'macos' }}
run: |
echo "run 'xattr -d com.apple.quarantine Processing-${version}.dmg' to remove the quarantine flag" > ./app/build/compose/binaries/main/dmg/INSTRUCTIONS_FOR_TESTING.txt
- name: Add artifact
uses: actions/upload-artifact@v4
with:
name: processing-pr${{ github.event.pull_request.number }}-${{github.sha}}-${{ matrix.os_prefix }}-${{ matrix.arch }}-gradle
path: |
./app/build/compose/binaries/main/dmg/Processing-*.dmg
./app/build/compose/binaries/main/dmg/INSTRUCTIONS_FOR_TESTING.txt
./app/build/compose/binaries/main/msi/Processing-*.msi
./app/build/compose/binaries/main/deb/processing*.deb
name: processing-${{ matrix.os_prefix }}-${{ matrix.arch }}-pr_${{ github.event.pull_request.number }}
retention-days: 5
path: app/build/compose/binaries/main/${{ matrix.binary }}
+106 -28
View File
@@ -7,22 +7,50 @@ jobs:
version:
runs-on: ubuntu-latest
outputs:
build_number: ${{ steps.tag_info.outputs.build_number }}
revision: ${{ steps.tag_info.outputs.revision }}
version: ${{ steps.tag_info.outputs.version }}
steps:
- name: Extract version and build number
- name: Extract version and revision
id: tag_info
shell: bash
run: |
TAG_NAME="${GITHUB_REF#refs/tags/}"
BUILD_NUMBER=$(echo "$TAG_NAME" | cut -d'-' -f2)
REVISION=$(echo "$TAG_NAME" | cut -d'-' -f2)
VERSION=$(echo "$TAG_NAME" | cut -d'-' -f3)
# Set outputs for use in later jobs or steps
echo "build_number=$BUILD_NUMBER" >> $GITHUB_OUTPUT
echo "revision=$REVISION" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> $GITHUB_OUTPUT
reference:
name: Publish Processing Reference to release
runs-on: ubuntu-latest
permissions:
contents: write
needs: version
steps:
- name: Checkout Website Repository
uses: actions/checkout@v4
with:
repository: processing/processing-website
- name: Use Node.js 16
uses: actions/setup-node@v3
with:
node-version: 16
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Make reference.zip
run: npm run zip
- name: Upload reference to release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
asset_name: processing-${{ needs.version.outputs.version }}-reference.zip
file: reference.zip
publish:
name: Publish Processing Core to Maven Central
name: Publish Processing Libraries to Maven Central
runs-on: ubuntu-latest
needs: version
steps:
@@ -35,6 +63,7 @@ jobs:
java-version: 17
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Build with Gradle
run: ./gradlew publish
env:
@@ -48,9 +77,9 @@ jobs:
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_IN_MEMORY_KEY_PASSWORD }}
ORG_GRADLE_PROJECT_version: ${{ needs.version.outputs.version }}
ORG_GRADLE_PROJECT_group: ${{ vars.PROCESSING_GROUP }}
ORG_GRADLE_PROJECT_group: ${{ vars.GRADLE_GROUP }}
build:
name: Publish Release for ${{ matrix.os_prefix }} (${{ matrix.arch }})
name: (${{ matrix.os_prefix }}/${{ matrix.arch }}) Create Processing Release
runs-on: ${{ matrix.os }}
needs: version
permissions:
@@ -59,26 +88,47 @@ jobs:
fail-fast: false
matrix:
include:
# compiling for arm32 needs a self-hosted runner on Raspi OS (32-bit)
- os: [self-hosted, linux, ARM]
- os: ubuntu-24.04-arm
os_prefix: linux
arch: arm
arch: aarch64
binary: ${{ vars.SNAP_NAME }}_${{ needs.version.outputs.version }}_arm64
extension: snap
- os: ubuntu-latest
os_prefix: linux
arch: x64
binary: ${{ vars.SNAP_NAME }}_${{ needs.version.outputs.version }}_amd64
extension: snap
- os: windows-latest
os_prefix: windows
arch: x64
binary: msi/Processing-${{ needs.version.outputs.version }}
extension: msi
- os: macos-latest
os_prefix: macos
arch: x64
binary: dmg/Processing-${{ needs.version.outputs.version }}
extension: dmg
- os: macos-latest
os_prefix: macos
arch: aarch64
- os: macos-latest
os_prefix: linux
arch: aarch64
binary: dmg/Processing-${{ needs.version.outputs.version }}
extension: dmg
steps:
- name: Install Certificates for Code Signing
if: runner.os == 'macOS'
continue-on-error: true
uses: apple-actions/import-codesign-certs@v3
with:
p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
- name: Install Snapcraft
if: runner.os == 'Linux'
uses: samuelmeuli/action-snapcraft@v3
- name: Install LXD
if: runner.os == 'Linux'
uses: canonical/setup-lxd@main
- name: Checkout Repository
uses: actions/checkout@v4
- name: Install Java
@@ -89,25 +139,53 @@ jobs:
architecture: ${{ matrix.arch }}
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
# - name: Install Certificates for Code Signing
# if: ${{ matrix.os_prefix == 'macos' }}
# uses: apple-actions/import-codesign-certs@v3
# with:
# p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
# p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
- name: Build with Gradle
run: ./gradlew packageDistributionForCurrentOS
env:
env:
ORG_GRADLE_PROJECT_version: ${{ needs.version.outputs.version }}
ORG_GRADLE_PROJECT_group: ${{ vars.PROCESSING_GROUP }}
ORG_GRADLE_PROJECT_group: ${{ vars.GRADLE_GROUP }}
ORG_GRADLE_PROJECT_revision: ${{ needs.version.outputs.revision }}
ORG_GRADLE_PROJECT_compose.desktop.verbose: true
ORG_GRADLE_PROJECT_compose.desktop.mac.sign: ${{ secrets.PROCESSING_SIGNING }}
ORG_GRADLE_PROJECT_compose.desktop.mac.signing.identity: ${{ secrets.PROCESSING_SIGNING_IDENTITY }}
ORG_GRADLE_PROJECT_compose.desktop.mac.notarization.appleID: ${{ secrets.PROCESSING_APPLE_ID }}
ORG_GRADLE_PROJECT_compose.desktop.mac.notarization.password: ${{ secrets.PROCESSING_APP_PASSWORD }}
ORG_GRADLE_PROJECT_compose.desktop.mac.notarization.teamID: ${{ secrets.PROCESSING_TEAM_ID }}
ORG_GRADLE_PROJECT_snapname: ${{ vars.SNAP_NAME }}
- name: Upload binaries to release
- name: Upload portables to release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: |
./app/build/compose/binaries/main/dmg/Processing-*.dmg
./app/build/compose/binaries/main/dmg/INSTRUCTIONS_FOR_TESTING.txt
./app/build/compose/binaries/main/msi/Processing-*.msi
./app/build/compose/binaries/main/deb/processing*.deb
file_glob: true
asset_name: processing-${{ needs.version.outputs.version }}-${{ matrix.os_prefix }}-${{ matrix.arch }}-portable.zip
file: app/build/compose/binaries/main/Processing-${{ needs.version.outputs.version }}.zip
- name: Upload installers to release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
asset_name: processing-${{ needs.version.outputs.version }}-${{ matrix.os_prefix }}-${{ matrix.arch }}.${{ matrix.extension }}
file: app/build/compose/binaries/main/${{ matrix.binary }}.${{ matrix.extension }}
- name: Upload snap to Snap Store
if: runner.os == 'Linux'
run: snapcraft upload --release=beta app/build/compose/binaries/main/${{ matrix.binary }}.${{ matrix.extension }}
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.PROCESSING_SNAPCRAFT_TOKEN }}
- name: Sign files with Trusted Signing
if: runner.os == 'Windows'
uses: azure/trusted-signing-action@v0
with:
azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
endpoint: https://eus.codesigning.azure.net/
trusted-signing-account-name: vscx-codesigning
certificate-profile-name: vscx-certificate-profile
files-folder: app/build/compose/binaries/main/msi
files-folder-filter: msi
file-digest: SHA256
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
+3
View File
@@ -113,3 +113,6 @@ java/build/
/core/examples/build
/java/gradle/build
/java/gradle/example/.processing
.build/
/app/windows/obj
+5
View File
@@ -0,0 +1,5 @@
<svg width="800" height="800" viewBox="0 0 800 800" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M400 500C700 500 700 100 400 100" stroke="#0468FF" stroke-width="150"/>
<path d="M400 200L100 600" stroke="#1F34AB" stroke-width="150"/>
<path d="M100 300L200 500" stroke="#85AEFF" stroke-width="150"/>
</svg>

After

Width:  |  Height:  |  Size: 324 B

-1
View File
@@ -4,7 +4,6 @@
<option name="env">
<map>
<entry key="DEBUG" value="true" />
<entry key="ORG_GRADLE_PROJECT_version" value="Development Build" />
</map>
</option>
<option name="executionName" />
@@ -0,0 +1,11 @@
package processing.app.ui;
// Stub class for backwards compatibility with the ant-build system
// This class is not used in the Gradle build system
// The actual implementation is in src/.../Schema.kt
public class WelcomeToBeta {
public static void showWelcomeToBeta(){
}
}
+303 -16
View File
@@ -1,6 +1,14 @@
import org.gradle.kotlin.dsl.support.zipTo
import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.jetbrains.compose.desktop.application.tasks.AbstractJPackageTask
import org.jetbrains.compose.internal.de.undercouch.gradle.tasks.download.Download
import org.jetbrains.kotlin.fir.scopes.impl.overrides
import java.io.FileOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
// TODO: Update to 2.10.20 and add hot-reloading: https://github.com/JetBrains/compose-hot-reload
plugins{
id("java")
@@ -12,9 +20,6 @@ plugins{
alias(libs.plugins.download)
}
group = rootProject.group
version = rootProject.version
repositories{
mavenCentral()
google()
@@ -29,6 +34,14 @@ sourceSets{
kotlin{
srcDirs("src")
}
resources{
srcDirs("resources", listOf("languages", "fonts", "theme").map { "../build/shared/lib/$it" })
}
}
test{
kotlin{
srcDirs("test")
}
}
}
@@ -37,37 +50,38 @@ compose.desktop {
mainClass = "processing.app.ui.Start"
jvmArgs(*listOf(
Pair("processing.version", version),
Pair("processing.revision", "1300"),
Pair("processing.contributions.source", "https://contributions-preview.processing.org/contribs.txt"),
Pair("processing.version", rootProject.version),
Pair("processing.revision", findProperty("revision") ?: Int.MAX_VALUE),
Pair("processing.contributions.source", "https://download.processing.org/contribs.txt"),
Pair("processing.download.page", "https://processing.org/download/"),
Pair("processing.download.latest", "https://processing.org/download/latest.txt"),
Pair("processing.tutorials", "https://processing.org/tutorials/"),
).map { "-D${it.first}=${it.second}" }.toTypedArray())
nativeDistributions{
modules("jdk.jdi", "java.compiler", "jdk.accessibility")
modules("jdk.jdi", "java.compiler", "jdk.accessibility", "java.management.rmi")
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "Processing"
macOS{
bundleID = "org.processing.app"
iconFile = project.file("../build/macos/processing.icns")
bundleID = "${rootProject.group}.app"
iconFile = rootProject.file("build/macos/processing.icns")
infoPlist{
extraKeysRawXml = layout.projectDirectory.file("info.plist").asFile.readText()
extraKeysRawXml = file("macos/info.plist").readText()
}
entitlementsFile.set(project.file("entitlements.plist"))
runtimeEntitlementsFile.set(project.file("entitlements.plist"))
entitlementsFile.set(file("macos/entitlements.plist"))
runtimeEntitlementsFile.set(file("macos/entitlements.plist"))
appStore = true
}
windows{
iconFile = project.file("../build/windows/processing.ico")
iconFile = rootProject.file("build/windows/processing.ico")
menuGroup = "Processing"
upgradeUuid = "89d8d7fe-5602-4b12-ba10-0fe78efbd602"
}
linux {
appCategory = "Programming"
menuGroup = "Processing"
iconFile = project.file("../build/linux/processing.png")
menuGroup = "Development;Programming;"
iconFile = rootProject.file("build/linux/processing.png")
// Fix fonts on some Linux distributions
jvmArgs("-Dawt.useSystemAAFontSettings=on")
@@ -99,12 +113,191 @@ dependencies {
implementation(libs.compottie)
implementation(libs.kaml)
implementation(libs.markdown)
implementation(libs.markdownJVM)
testImplementation(kotlin("test"))
testImplementation(libs.mockitoKotlin)
testImplementation(libs.junitJupiter)
testImplementation(libs.junitJupiterParams)
}
tasks.test {
useJUnitPlatform()
workingDir = file("build/test")
workingDir.mkdirs()
}
tasks.compileJava{
options.encoding = "UTF-8"
}
val version = if(project.version == "unspecified") "1.0.0" else project.version
tasks.register<Exec>("installCreateDmg") {
onlyIf { org.gradle.internal.os.OperatingSystem.current().isMacOsX }
commandLine("arch", "-arm64", "brew", "install", "--quiet", "create-dmg")
}
tasks.register<Exec>("packageCustomDmg"){
onlyIf { org.gradle.internal.os.OperatingSystem.current().isMacOsX }
group = "compose desktop"
val distributable = tasks.named<AbstractJPackageTask>("createDistributable").get()
dependsOn(distributable, "installCreateDmg")
val packageName = distributable.packageName.get()
val dir = distributable.destinationDir.get()
val dmg = dir.file("../dmg/$packageName-$version.dmg").asFile
val app = dir.file("$packageName.app").asFile
dmg.parentFile.deleteRecursively()
dmg.parentFile.mkdirs()
val extra = mutableListOf<String>()
val isSigned = compose.desktop.application.nativeDistributions.macOS.signing.sign.get()
if(!isSigned) {
val content = """
run 'xattr -d com.apple.quarantine Processing-${version}.dmg' to remove the quarantine flag
""".trimIndent()
val instructions = dmg.parentFile.resolve("INSTRUCTIONS.txt")
instructions.writeText(content)
extra.add("--add-file")
extra.add("INSTRUCTIONS.txt")
extra.add(instructions.path)
extra.add("200")
extra.add("25")
}
commandLine("brew", "install", "--quiet", "create-dmg")
commandLine("create-dmg",
"--volname", packageName,
"--volicon", file("macos/volume.icns"),
"--background", file("macos/background.png"),
"--icon", "$packageName.app", "190", "185",
"--window-pos", "200", "200",
"--window-size", "658", "422",
"--app-drop-link", "466", "185",
"--hide-extension", "$packageName.app",
*extra.toTypedArray(),
dmg,
app
)
}
tasks.register<Exec>("packageCustomMsi"){
onlyIf { org.gradle.internal.os.OperatingSystem.current().isWindows }
dependsOn("createDistributable")
workingDir = file("windows")
group = "compose desktop"
val version = if(version == "unspecified") "1.0.0" else version
commandLine(
"dotnet",
"build",
"/p:Platform=x64",
"/p:Version=$version",
"/p:DefineConstants=\"Version=$version;\""
)
}
val snapname = findProperty("snapname") ?: rootProject.name
val snaparch = when (System.getProperty("os.arch")) {
"amd64", "x86_64" -> "amd64"
"aarch64" -> "arm64"
else -> System.getProperty("os.arch")
}
tasks.register("generateSnapConfiguration"){
onlyIf { org.gradle.internal.os.OperatingSystem.current().isLinux }
val distributable = tasks.named<AbstractJPackageTask>("createDistributable").get()
dependsOn(distributable)
val dir = distributable.destinationDir.get()
val content = """
name: $snapname
version: $version
base: core22
summary: A creative coding editor
description: |
Processing is a flexible software sketchbook and a programming language designed for learning how to code.
confinement: strict
apps:
processing:
command: opt/processing/bin/Processing
desktop: opt/processing/lib/processing-Processing.desktop
environment:
LD_LIBRARY_PATH: ${'$'}SNAP/opt/processing/lib/runtime/lib:${'$'}LD_LIBRARY_PATH
LIBGL_DRIVERS_PATH: ${'$'}SNAP/usr/lib/${'$'}SNAPCRAFT_ARCH_TRIPLET/dri
plugs:
- desktop
- desktop-legacy
- wayland
- x11
- network
- opengl
parts:
processing:
plugin: dump
source: deb/processing_$version-1_$snaparch.deb
source-type: deb
stage-packages:
- openjdk-17-jre
override-prime: |
snapcraftctl prime
chmod -R +x opt/processing/lib/app/resources/jdk-*
rm -vf usr/lib/jvm/java-17-openjdk-*/lib/security/cacerts
""".trimIndent()
dir.file("../snapcraft.yaml").asFile.writeText(content)
}
tasks.register<Exec>("packageSnap"){
onlyIf { org.gradle.internal.os.OperatingSystem.current().isLinux }
dependsOn("packageDeb", "generateSnapConfiguration")
group = "compose desktop"
val distributable = tasks.named<AbstractJPackageTask>("createDistributable").get()
workingDir = distributable.destinationDir.dir("../").get().asFile
commandLine("snapcraft")
}
tasks.register<Zip>("zipDistributable"){
dependsOn("createDistributable", "setExecutablePermissions")
group = "compose desktop"
val distributable = tasks.named<AbstractJPackageTask>("createDistributable").get()
val dir = distributable.destinationDir.get()
val packageName = distributable.packageName.get()
from(dir){ eachFile{ permissions{ unix("755") } } }
archiveBaseName.set(packageName)
destinationDirectory.set(dir.file("../").asFile)
}
afterEvaluate{
tasks.named("packageDmg").configure{
dependsOn("packageCustomDmg")
group = "compose desktop"
actions = emptyList()
}
tasks.named("packageMsi").configure{
dependsOn("packageCustomMsi")
group = "compose desktop"
actions = emptyList()
}
tasks.named("packageDistributionForCurrentOS").configure {
if(org.gradle.internal.os.OperatingSystem.current().isMacOsX
&& compose.desktop.application.nativeDistributions.macOS.notarization.appleID.isPresent
){
dependsOn("notarizeDmg")
}
dependsOn("packageSnap", "zipDistributable")
}
}
// LEGACY TASKS
// Most of these are shims to be compatible with the old build system
@@ -220,6 +413,97 @@ tasks.register<Copy>("renameWindres") {
}
duplicatesStrategy = DuplicatesStrategy.INCLUDE
into(dir)
}
tasks.register("signResources"){
onlyIf {
org.gradle.internal.os.OperatingSystem.current().isMacOsX
&&
compose.desktop.application.nativeDistributions.macOS.signing.sign.get()
}
group = "compose desktop"
dependsOn(
"includeCore",
"includeJavaMode",
"includeJdk",
"includeSharedAssets",
"includeProcessingExamples",
"includeProcessingWebsiteExamples",
"includeJavaModeResources",
"renameWindres"
)
finalizedBy("prepareAppResources")
val resourcesPath = composeResources("")
// find jars in the resources directory
val jars = mutableListOf<File>()
doFirst{
fileTree(resourcesPath)
.matching { include("**/Info.plist") }
.singleOrNull()
?.let { file ->
copy {
from(file)
into(resourcesPath)
}
}
fileTree(resourcesPath) {
include("**/*.jar")
exclude("**/*.jar.tmp/**")
}.forEach { file ->
val tempDir = file.parentFile.resolve("${file.name}.tmp")
copy {
from(zipTree(file))
into(tempDir)
}
file.delete()
jars.add(tempDir)
}
fileTree(resourcesPath){
include("**/bin/**")
include("**/*.jnilib")
include("**/*.dylib")
include("**/*aarch64*")
include("**/*x86_64*")
include("**/*ffmpeg*")
include("**/ffmpeg*/**")
exclude("jdk-*/**")
exclude("*.jar")
exclude("*.so")
exclude("*.dll")
}.forEach{ file ->
exec {
commandLine("codesign", "--timestamp", "--force", "--deep","--options=runtime", "--sign", "Developer ID Application", file)
}
}
jars.forEach { file ->
FileOutputStream(File(file.parentFile, file.nameWithoutExtension)).use { fos ->
ZipOutputStream(fos).use { zos ->
file.walkTopDown().forEach { fileEntry ->
if (fileEntry.isFile) {
// Calculate the relative path for the zip entry
val zipEntryPath = fileEntry.relativeTo(file).path
val entry = ZipEntry(zipEntryPath)
zos.putNextEntry(entry)
// Copy file contents to the zip
fileEntry.inputStream().use { input ->
input.copyTo(zos)
}
zos.closeEntry()
}
}
}
}
file.deleteRecursively()
}
file(composeResources("Info.plist")).delete()
}
}
afterEvaluate {
tasks.named("prepareAppResources").configure {
@@ -253,5 +537,8 @@ afterEvaluate {
}
}
}
tasks.findByName("createDistributable")?.finalizedBy("setExecutablePermissions")
tasks.named("createDistributable").configure {
dependsOn("signResources")
finalizedBy("setExecutablePermissions")
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 497 KiB

Binary file not shown.
+5
View File
@@ -0,0 +1,5 @@
<svg width="900" height="900" viewBox="0 0 900 900" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M456.735 552.393L155.9 151.279" stroke="#A44D28" stroke-width="150.418"/>
<path d="M757.575 452.113C456.738 552.391 456.738 752.948 155.903 652.67" stroke="#FF7D45" stroke-width="150.418"/>
<path d="M556.832 552.392L456.555 51" stroke="#FFBEA2" stroke-width="150.418"/>
</svg>

After

Width:  |  Height:  |  Size: 386 B

+309
View File
@@ -0,0 +1,309 @@
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# DO NOT MAKE CHANGES TO THIS FILE!!!
# These are the default preferences. If you want to modify
# them directly, use the per-user local version of the file:
# Users -> [username] -> AppData -> Roaming ->
# Processing -> preferences.txt (on Windows 10)
# ~/Library -> Processing -> preferences.txt (on macOS)
# ~/.config/processing -> preferences.txt (on Linux)
# The exact location of your preferences file can be found at
# the bottom of the Preferences window inside Processing.
# Because AppData and Application Data may be considered
# hidden or system folders on Windows, you'll have to ensure
# that they're visible in order to get at preferences.txt
# You'll have problems running Processing if you incorrectly
# modify lines in this file. It will probably not start at all.
# AGAIN, DO NOT ALTER THIS FILE! I'M ONLY YELLING BECAUSE I LOVE YOU!
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# If you don't want users to have their sketchbook default to
# "My Documents/Processing" on Windows and "Documents/Processing" on OS X,
# set this to another path that will be used by default.
# Note that this path must exist already otherwise it won't see
# the sketchbook folder, and will instead assume the sketchbook
# has gone missing, and that it should instead use the default.
# In 4.0, the location has changed.
#sketchbook.path.four=
# Whether or not to show the Welcome screen for 4.0
# (It's always available under Help → Welcome)
welcome.four.show = true
welcome.four.seen = false
# Set 'true' for the default behavior before 4.0, where the
# main tab must have the same name as the sketch folder
editor.sync_folder_and_filename = true
# By default, contributions are moved to backup folders when
# they are removed or replaced. The backups can be found at
# sketchbook/libraries/old, sketchbook/tools/old, and sketchbook/modes/old
# true to backup contributions when "Remove" button is pressed
contribution.backup.on_remove = true
# true to backup contributions when installing a newer version
contribution.backup.on_install = true
recent.count = 10
# Default to the native (AWT) file selector where possible
chooser.files.native = true
# We were shutting this off on macOS because it broke Copy/Paste:
# https://github.com/processing/processing/issues/1035
# But removing again for 4.0 alpha 5, because the JFileChooser is awful,
# and worse on Big Sur, so a bigger problem than the Copy/Paste issue.
# https://github.com/processing/processing4/issues/77
#chooser.files.native.macos = false
# set to 'lab' to interpolate theme gradients using L*a*b* color space
theme.gradient.method = rgb
# by default, check the processing server for any updates
# (please avoid disabling, this also helps us know basic numbers
# on how many people are using Processing)
update.check = true
# on windows, automatically associate .pde files with processing.exe
platform.auto_file_type_associations = true
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# default size for the main window
editor.window.width.default = 700
editor.window.height.default = 600
editor.window.width.min = 400
editor.window.height.min = 500
# tested as approx 440 on OS X
editor.window.height.min.macos = 450
# tested to be 515 on Windows XP, this leaves some room
editor.window.height.min.windows = 530
# tested with Raspberry Pi display
editor.window.height.min.linux = 480
# scaling for the interface (to handle Windows and Linux HiDPI displays)
editor.zoom = 100%
# automatically set based on system dpi (only helps on Windows)
editor.zoom.auto = true
# Use the default monospace font included in lib/fonts.
# (As of Processing 4 alpha 5, that's Source Code Pro)
editor.font.family = processing.mono
editor.font.size = 12
# To reset everyone's default, replaced editor.antialias with editor.smooth
# for 2.1. Fonts are unusably gross on OS X (and Linux) w/o smoothing and
# the Oracle JVM, and many longtime users have anti-aliasing turned off.
editor.smooth = true
# blink the caret by default
editor.caret.blink = true
# change to true to use a block (instead of a bar)
editor.caret.block = false
# enable ctrl-ins, shift-ins, shift-delete for cut/copy/paste
# on windows and linux, but disable on the mac
editor.keys.alternative_cut_copy_paste = true
editor.keys.alternative_cut_copy_paste.macos = false
# true if shift-backspace sends the delete character,
# false if shift-backspace just means backspace
editor.keys.shift_backspace_is_delete = false
# home and end keys should only travel to the start/end of the current line
editor.keys.home_and_end_travel_far = false
# home and end keys move to the first/last non-whitespace character,
# and move to the actual start/end when pressed a second time.
# Only works if editor.keys.home_and_end_travel_far is false.
editor.keys.home_and_end_travel_smart = true
# The OS X HI Guidelines say that home/end are relative to the document,
# but that drives some people nuts. This pref enables/disables it.
editor.keys.home_and_end_travel_far.macos = true
# Enable/disable support for complex scripts. Used for Japanese and others,
# but disable when not needed, otherwise basic Western European chars break.
editor.input_method_support = false
# convert tabs to spaces? how many spaces?
editor.tabs.expand = true
editor.tabs.size = 2
# Set to true to automatically close [ { ( " and '
editor.completion.auto_close = false
# automatically indent each line
editor.indent = true
# Whether to check files to see if they've been modified externally
editor.watcher = true
# Set true to enable debugging, since this is quirky on others' machines
editor.watcher.debug = false
# The window of time (in milliseconds) in which a change won't be counted
editor.watcher.window = 1500
# Format and search engine to use for online queries
search.format = https://google.com/search?q=%s
# font choice and size for the console
console.font.size = 12
# number of lines to show by default
console.lines = 4
# Number of blank lines to advance/clear console.
# Note that those lines are also printed in the terminal when
# Processing is executed there.
# Setting to 0 stops this behavior.
console.head_padding = 10
# Set to false to disable automatically clearing the console
# each time 'run' is hit
# If one sets it to false, one may also want to set 'console.head_padding'
# to a positive number to separate outputs from different runs.
console.auto_clear = true
# number of days of history to keep around before cleaning
# setting to 0 will never clean files
console.temp.days = 7
# set the maximum number of lines remembered by the console
# the default is 500, lengthen at your own peril
console.scrollback.lines = 500
console.scrollback.chars = 40000
# Any additional Java options when running.
# If you change this and can't run things, it's your own durn fault.
run.options =
# settings for the -XmsNNNm and -XmxNNNm command line option
run.options.memory = false
run.options.memory.initial = 64
run.options.memory.maximum = 512
# Index of the display to use for running sketches (starts at 1).
# Kept this 1-indexed because older vesions of Processing were setting
# the preference even before it was being used.
# -1 means the default display, 0 means all displays
run.display = -1
# set internally because it comes from the system
#run.window.bgcolor=
# set to false to open a new untitled window when closing the last window
# (otherwise, the environment will quit)
# default to the relative norm for the different platforms,
# but the setting can be changed in the prefs dialog anyway
#sketchbook.closing_last_window_quits = true
#sketchbook.closing_last_window_quits.macos = false
editor.untitled.prefix=sketch_
# The old (pre-1.0, back for 2.0) style for default sketch name.
# If you change this, be careful that this will work with your language
# settings. For instance, MMMdd won't work on Korean-language systems
# because it'll insert non-ASCII characters and break the environment.
# https://github.com/processing/processing/issues/322
editor.untitled.suffix=yyMMdd
# replace underscores in .pde file names with spaces
sketch.name.replace_underscore = true
# what to use for generating sketch names (change in the prefs window)
#sketch.name.approach =
# number of days of build history and other temp files to keep around
# these are kept around for debugging purposes, and in case code is lost
temp.days = 7
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# whether or not to export as full screen (present) mode
export.application.fullscreen = false
# whether to show the stop button when exporting to application
export.application.stop = true
# embed Java by default for lower likelihood of problems
export.application.embed_java = true
# set to false to no longer delete application folders before export
# (removed from the Preferences windows in 4.0 beta 9)
export.delete_target_folder = true
# may be useful when attempting to debug the preprocessor
preproc.save_build_files=false
# allows various preprocessor features to be toggled
# in case they are causing problems
# preprocessor: pde.g
preproc.color_datatype = true
preproc.web_colors = true
preproc.enhanced_casting = true
# preprocessor: PdeEmitter.java
preproc.substitute_floats = true
# PdePreproc.java
# writes out the parse tree as parseTree.xml, which can be usefully
# viewed in (at least) Mozilla or IE. useful when debugging the preprocessor.
preproc.output_parse_tree = false
# set to the program to be used for opening HTML files, folders, etc.
#launcher.linux = xdg-open
# FULL SCREEN (PRESENT MODE)
run.present.bgcolor = #666666
run.present.stop.color = #cccccc
# PROXIES
# Set a proxy server for folks that require it. This will allow the update
# checker and the contrib manager to run properly in those environments.
# This changed from proxy.host and proxy.port to proxy.http.host and
# proxy.http.port in 3.0a8. In addition, https and socks were added.
proxy.http.host=
proxy.http.port=
proxy.https.host=
proxy.https.port=
proxy.socks.host=
proxy.socks.port=
# Example of usage (replace 'http' with 'https' or 'socks' as needed)
#proxy.http.host=proxy.example.com
#proxy.http.port=8080
# Whether to use the system proxy by default
proxy.system=true
# PDE X
pdex.errorCheckEnabled = true
pdex.warningsEnabled = true
pdex.writeErrorLogs = false
pdex.autoSave.autoSaveEnabled = false
pdex.autoSaveInterval = 5
pdex.autoSave.promptDisplay = true
pdex.autoSave.autoSaveByDefault = true
# Enable auto-completion when hitting ctrl-space
pdex.completion = false
# Setting this true will show completions whenever available, not just after ctrl-space
pdex.completion.trigger = false
# Suggest libraries to import when a class is undefined/unavailable
pdex.suggest.imports = true
# Set to false to disable ctrl/cmd-click jump to definition
pdex.inspectMode.hotkey = true
+5
View File
@@ -0,0 +1,5 @@
<svg width="800" height="800" viewBox="0 0 800 800" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M400 500C700 500 700 100 400 100" stroke="#0468FF" stroke-width="150"/>
<path d="M400 200L100 600" stroke="#1F34AB" stroke-width="150"/>
<path d="M100 300L200 500" stroke="#85AEFF" stroke-width="150"/>
</svg>

After

Width:  |  Height:  |  Size: 329 B

+9 -10
View File
@@ -124,6 +124,7 @@ public class Base {
static public void main(final String[] args) {
Messages.log("Starting Processing version" + VERSION_NAME + " revision "+ REVISION);
EventQueue.invokeLater(() -> {
try {
createAndShowGUI(args);
@@ -563,14 +564,12 @@ public class Base {
cl.downloadAvailableList(this, new ContribProgress(null));
long t9 = System.currentTimeMillis();
if (DEBUG) {
System.out.println("core modes: " + (t2b-t2) +
", contrib modes: " + (t2c-t2b) +
", contrib ex: " + (t2c-t2b));
System.out.println("base took " + (t2-t1) + " " + (t3-t2) + " " + (t4-t3) +
Messages.log("core modes: " + (t2b-t2) +
", contrib modes: " + (t2c-t2b) +
", contrib ex: " + (t2c-t2b));
Messages.log("base took " + (t2-t1) + " " + (t3-t2) + " " + (t4-t3) +
" " + (t5-t4) + " t6-t5=" + (t6-t5) + " " + (t7-t6) +
" handleNew=" + (t8-t7) + " " + (t9-t8) + " ms");
}
}
@@ -1366,10 +1365,10 @@ public class Base {
* @param schemeUri the full URI, including pde://
*/
public Editor handleScheme(String schemeUri) {
// var result = Schema.handleSchema(schemeUri, this);
// if (result != null) {
// return result;
// }
var result = Schema.handleSchema(schemeUri, this);
if (result != null) {
return result;
}
String location = schemeUri.substring(6);
if (location.length() > 0) {
+13 -11
View File
@@ -1,6 +1,5 @@
package processing.app;
import java.awt.EventQueue;
import java.io.*;
import java.util.*;
import java.util.zip.ZipFile;
@@ -330,6 +329,7 @@ public class Library extends LocalContribution {
* imports to specific libraries.
* @param importToLibraryTable mapping from package names to Library objects
*/
static boolean instructed = false;
// public void addPackageList(HashMap<String,Library> importToLibraryTable) {
public void addPackageList(Map<String, List<Library>> importToLibraryTable) {
// PApplet.println(packages);
@@ -342,18 +342,20 @@ public class Library extends LocalContribution {
libraries = new ArrayList<>();
importToLibraryTable.put(pkg, libraries);
} else {
if (Base.DEBUG) {
System.err.println("The library found in");
System.err.println(getPath());
System.err.println("conflicts with");
if(!instructed) {
instructed = true;
Messages.err("The library found in");
Messages.err(getPath());
Messages.err("conflicts with");
for (Library library : libraries) {
System.err.println(library.getPath());
Messages.err(library.getPath());
}
System.err.println("which already define(s) the package " + pkg);
System.err.println("If you have a line in your sketch that reads");
System.err.println("import " + pkg + ".*;");
System.err.println("Then you'll need to first remove one of those libraries.");
System.err.println();
Messages.err("which already define(s) the package " + pkg);
Messages.err("If you have a line in your sketch that reads");
Messages.err("import " + pkg + ".*;");
Messages.err("Then you'll need to first remove one of those libraries.");
}else{
Messages.err("\tPackage ("+pkg+")\t conflict found in [" + name + "] with libraries: " + libraries.stream().map(Library::getName).reduce((a, b) -> a + ", " + b).orElse(""));
}
}
libraries.add(this);
+282
View File
@@ -0,0 +1,282 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /*
Part of the Processing project - http://processing.org
Copyright (c) 2015 The Processing Foundation
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2, as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app
import processing.app.ui.Toolkit
import java.awt.EventQueue
import java.awt.Frame
import java.io.PrintWriter
import java.io.StringWriter
import javax.swing.JFrame
import javax.swing.JOptionPane
class Messages {
companion object {
/**
* "No cookie for you" type messages. Nothing fatal or all that
* much of a bummer, but something to notify the user about.
*/
@JvmStatic
fun showMessage(title: String = "Message", message: String) {
if (Base.isCommandLine()) {
println("$title: $message")
} else {
JOptionPane.showMessageDialog(
Frame(), message, title,
JOptionPane.INFORMATION_MESSAGE
)
}
}
/**
* Non-fatal error message with optional stack trace side dish.
*/
/**
* Non-fatal error message.
*/
@JvmStatic
@JvmOverloads
fun showWarning(title: String = "Warning", message: String, e: Throwable? = null) {
if (Base.isCommandLine()) {
println("$title: $message")
} else {
JOptionPane.showMessageDialog(
Frame(), message, title,
JOptionPane.WARNING_MESSAGE
)
}
e?.printStackTrace()
}
/**
* Non-fatal error message with two levels of formatting.
* Unlike the others, this is non-blocking and will run later on the EDT.
*/
@JvmStatic
fun showWarningTiered(
title: String,
primary: String, secondary: String,
e: Throwable?
) {
if (Base.isCommandLine()) {
// TODO All these messages need to be handled differently for
// proper parsing on the command line. Many have \n in them.
println("$title: $primary\n$secondary")
} else {
EventQueue.invokeLater {
JOptionPane.showMessageDialog(
JFrame(),
Toolkit.formatMessage(primary, secondary),
title, JOptionPane.WARNING_MESSAGE
)
}
}
e?.printStackTrace()
}
/**
* Show an error message that's actually fatal to the program.
* This is an error that can't be recovered. Use showWarning()
* for errors that allow P5 to continue running.
*/
@JvmStatic
fun showError(title: String = "Error", message: String, e: Throwable?) {
if (Base.isCommandLine()) {
System.err.println("$title: $message")
} else {
JOptionPane.showMessageDialog(
Frame(), message, title,
JOptionPane.ERROR_MESSAGE
)
}
e?.printStackTrace()
System.exit(1)
}
/**
* Warning window that includes the stack trace.
*/
@JvmStatic
fun showTrace(
title: String?,
message: String,
t: Throwable?,
fatal: Boolean
) {
val title = title ?: if (fatal) "Error" else "Warning"
if (Base.isCommandLine()) {
System.err.println("$title: $message")
t?.printStackTrace()
} else {
val sw = StringWriter()
t!!.printStackTrace(PrintWriter(sw))
JOptionPane.showMessageDialog(
Frame(), // first <br/> clears to the next line
// second <br/> is a shorter height blank space before the trace
Toolkit.formatMessage("$message<br/><tt><br/>$sw</tt>"),
title,
if (fatal) JOptionPane.ERROR_MESSAGE else JOptionPane.WARNING_MESSAGE
)
if (fatal) {
System.exit(1)
}
}
}
@JvmStatic
fun showYesNoQuestion(
editor: Frame?, title: String?,
primary: String?, secondary: String?
): Int {
if (!Platform.isMacOS()) {
return JOptionPane.showConfirmDialog(
editor,
Toolkit.formatMessage(primary, secondary), //"<html><body>" +
//"<b>" + primary + "</b>" +
//"<br>" + secondary,
title,
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE
)
} else {
val result = showCustomQuestion(
editor, title, primary, secondary,
0, "Yes", "No"
)
return if (result == 0) {
JOptionPane.YES_OPTION
} else if (result == 1) {
JOptionPane.NO_OPTION
} else {
JOptionPane.CLOSED_OPTION
}
}
}
/**
* @param highlight A valid array index for options[] that specifies the
* default (i.e. safe) choice.
* @return The (zero-based) index of the selected value, -1 otherwise.
*/
@JvmStatic
fun showCustomQuestion(
editor: Frame?, title: String?,
primary: String?, secondary: String?,
highlight: Int, vararg options: String
): Int {
val result: Any
if (!Platform.isMacOS()) {
return JOptionPane.showOptionDialog(
editor,
Toolkit.formatMessage(primary, secondary), title,
JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null,
options, options[highlight]
)
} else {
val pane =
JOptionPane(
Toolkit.formatMessage(primary, secondary),
JOptionPane.QUESTION_MESSAGE
)
pane.options = options
// highlight the safest option ala apple hig
pane.initialValue = options[highlight]
val dialog = pane.createDialog(editor, null)
dialog.isVisible = true
result = pane.value
}
for (i in options.indices) {
if (result != null && result == options[i]) return i
}
return -1
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
@JvmStatic
@Deprecated("Use log() instead")
fun log(from: Any, message: String) {
if (Base.DEBUG) {
val callingClass = Throwable()
.stackTrace[2]
.className
.formatClassName()
println("$callingClass: $message")
}
}
@JvmStatic
fun log(message: String?) {
if (Base.DEBUG) {
val callingClass = Throwable()
.stackTrace[2]
.className
.formatClassName()
println("$callingClass$message")
}
}
@JvmStatic
fun logf(message: String?, vararg args: Any?) {
if (Base.DEBUG) {
val callingClass = Throwable()
.stackTrace[2]
.className
.formatClassName()
System.out.printf("$callingClass$message", *args)
}
}
@JvmStatic
@JvmOverloads
fun err(message: String?, e: Throwable? = null) {
if (Base.DEBUG) {
if (message != null) {
val callingClass = Throwable()
.stackTrace[4]
.className
.formatClassName()
System.err.println("$callingClass$message")
}
e?.printStackTrace()
}
}
}
}
// Helper functions to give the base classes a color
fun String.formatClassName() = this
.replace("processing.", "")
.replace(".", "/")
.padEnd(40)
.colorizePathParts()
fun String.colorizePathParts() = split("/").joinToString("/") { part ->
"\u001B[${31 + (part.hashCode() and 0x7).rem(6)}m$part\u001B[0m"
}
+12 -5
View File
@@ -577,18 +577,25 @@ public class Platform {
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
/**
* These methods were refactored to use the Preferences system instead of
* actual environment variables, since modifying environment variables at runtime
* proved problematic. This approach provides similar functionality
* while being compatible with various platforms and execution environments.
*
* This abstraction maintains a consistent API for environment-like variable storage
* while implementing it differently under the hood to work around runtime limitations.
*/
static public void setenv(String variable, String value) {
inst.setenv(variable, value);
Preferences.set(variable, value);
}
static public String getenv(String variable) {
return inst.getenv(variable);
return Preferences.get(variable);
}
static public int unsetenv(String variable) {
return inst.unsetenv(variable);
throw new RuntimeException("unsetenv() not yet implemented");
}
}
@@ -1,11 +1,10 @@
package processing.app.contrib.ui
package processing.app
import androidx.compose.runtime.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import processing.app.Base
import processing.app.Platform
import java.io.File
import java.io.InputStream
import java.nio.file.*
import java.util.Properties
@@ -13,10 +12,13 @@ import java.util.Properties
const val PREFERENCES_FILE_NAME = "preferences.txt"
const val DEFAULTS_FILE_NAME = "defaults.txt"
fun PlatformStart(){
Platform.inst ?: Platform.init()
}
@Composable
fun loadPreferences(): Properties{
Platform.init()
PlatformStart()
val settingsFolder = Platform.getSettingsFolder()
val preferencesFile = settingsFolder.resolve(PREFERENCES_FILE_NAME)
@@ -24,20 +26,12 @@ fun loadPreferences(): Properties{
if(!preferencesFile.exists()){
preferencesFile.createNewFile()
}
val watched = watchFile(preferencesFile)
watchFile(preferencesFile)
val preferences by remember {
mutableStateOf(Properties())
return Properties().apply {
load(ClassLoader.getSystemResourceAsStream(DEFAULTS_FILE_NAME) ?: InputStream.nullInputStream())
load(preferencesFile.inputStream())
}
LaunchedEffect(watched){
val defaults = Base::class.java.getResourceAsStream("/lib/${DEFAULTS_FILE_NAME}") ?: return@LaunchedEffect
preferences.load(defaults)
preferences.load(preferencesFile.inputStream())
}
return preferences
}
@Composable
@@ -68,4 +62,12 @@ fun watchFile(file: File): Any? {
}
}
return event
}
val LocalPreferences = compositionLocalOf<Properties> { error("No preferences provided") }
@Composable
fun PreferencesProvider(content: @Composable () -> Unit){
val preferences = loadPreferences()
CompositionLocalProvider(LocalPreferences provides preferences){
content()
}
}
+26 -28
View File
@@ -53,7 +53,11 @@ class Schema {
private fun handleSketchUrl(uri: URI): Editor?{
val url = File(uri.path.replace("/url/", ""))
val tempSketchFolder = File(Base.untitledFolder, url.nameWithoutExtension)
val rand = (1..6)
.map { (('a'..'z') + ('A'..'Z')).random() }
.joinToString("")
val tempSketchFolder = File(File(Base.untitledFolder, rand), url.nameWithoutExtension)
tempSketchFolder.mkdirs()
val tempSketchFile = File(tempSketchFolder, "${tempSketchFolder.name}.pde")
@@ -71,7 +75,7 @@ class Schema {
?.map { it.split("=") }
?.associate {
URLDecoder.decode(it[0], StandardCharsets.UTF_8) to
URLDecoder.decode(it[1], StandardCharsets.UTF_8)
URLDecoder.decode(it[1], StandardCharsets.UTF_8)
}
?: emptyMap()
options["data"]?.let{ data ->
@@ -81,7 +85,7 @@ class Schema {
downloadFiles(uri, code, File(sketchFolder, "code"))
}
options["pde"]?.let{ pde ->
downloadFiles(uri, pde, sketchFolder)
downloadFiles(uri, pde, sketchFolder, "pde")
}
options["mode"]?.let{ mode ->
val modeFile = File(sketchFolder, "sketch.properties")
@@ -89,7 +93,7 @@ class Schema {
}
}
private fun downloadFiles(uri: URI, urlList: String, targetFolder: File){
private fun downloadFiles(uri: URI, urlList: String, targetFolder: File, extension: String = ""){
Thread{
targetFolder.mkdirs()
@@ -101,37 +105,31 @@ class Schema {
val files = urlList.split(",")
files.filter { it.isNotBlank() }
.map{ it.split(":", limit = 2) }
.map{ segments ->
if(segments.size == 2){
if(segments[0].isBlank()){
return@map listOf(null, segments[1])
}
return@map segments
}
return@map listOf(null, segments[0])
.map {
if (it.contains(":")) it
else "$it:$it"
}
.map{ it.split(":", limit = 2) }
.forEach { (name, content) ->
var target = File(targetFolder, name)
if(extension.isNotBlank() && target.extension != extension){
target = File(targetFolder, "$name.$extension")
}
try{
// Try to decode the content as base64
val file = Base64.getDecoder().decode(content)
if(name == null){
if(name.isBlank()){
Messages.err("Base64 files needs to start with a file name followed by a colon")
return@forEach
}
File(targetFolder, name).writeBytes(file)
target.writeBytes(file)
}catch(_: IllegalArgumentException){
// Assume it's a URL and download it
var url = URI.create(content)
if(url.host == null){
url = URI.create("https://$base/$content")
}
if(url.scheme == null){
url = URI.create("https://$content")
}
val target = File(targetFolder, name ?: url.path.split("/").last())
url.toURL().openStream().use { input ->
val url = URL(when{
content.startsWith("https://") -> content
content.startsWith("http://") -> content.replace("http://", "https://")
URL("https://$content").path.isNotBlank() -> "https://$content"
else -> "https://$base/$content"
})
url.openStream().use { input ->
target.outputStream().use { output ->
input.copyTo(output)
}
@@ -148,7 +146,7 @@ class Schema {
?.map { it.split("=") }
?.associate {
URLDecoder.decode(it[0], StandardCharsets.UTF_8) to
URLDecoder.decode(it[1], StandardCharsets.UTF_8)
URLDecoder.decode(it[1], StandardCharsets.UTF_8)
}
?: emptyMap()
for ((key, value) in options){
+5 -1
View File
@@ -31,6 +31,7 @@ import java.util.Random;
import javax.swing.JOptionPane;
import processing.app.ui.WelcomeToBeta;
import processing.core.PApplet;
@@ -116,7 +117,7 @@ public class UpdateCheck {
long now = System.currentTimeMillis();
if (lastString != null) {
long when = Long.parseLong(lastString);
if (now - when < ONE_DAY) {
if (now - when < ONE_DAY && !Base.DEBUG) {
// don't annoy the shit outta people
return;
}
@@ -134,6 +135,9 @@ public class UpdateCheck {
// offerToUpdateContributions = !promptToVisitDownloadPage();
promptToVisitDownloadPage();
}
if(latest < Base.getRevision()){
WelcomeToBeta.showWelcomeToBeta();
}
/*
if (offerToUpdateContributions) {
@@ -30,6 +30,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import processing.app.Base;
import processing.app.Messages;
import processing.app.UpdateCheck;
import processing.app.Util;
import processing.core.PApplet;
@@ -228,6 +229,7 @@ public class ContributionListing {
public void downloadAvailableList(final Base base,
final ContribProgress progress) {
// TODO: replace with SwingWorker [jv]
Messages.log("Downloading contributions list from " + LISTING_URL);
new Thread(() -> {
downloadingLock.lock();
@@ -12,8 +12,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.awt.ComposePanel
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.pointer.PointerIcon
import androidx.compose.ui.input.pointer.pointerHoverIcon
import androidx.compose.ui.text.font.FontWeight
@@ -25,6 +23,7 @@ import com.charleskorn.kaml.Yaml
import com.charleskorn.kaml.YamlConfiguration
import kotlinx.serialization.Serializable
import processing.app.Platform
import processing.app.loadPreferences
import java.net.URL
import java.util.*
import javax.swing.JFrame
@@ -33,16 +32,7 @@ import kotlin.io.path.*
fun main() = application {
val active = remember { mutableStateOf(true) }
if(!active.value){
Window(onCloseRequest = ::exitApplication) {
}
return@application
}
Window(
onCloseRequest = { active.value = false },
) {
Window(onCloseRequest = ::exitApplication) {
contributionsManager()
}
}
+2 -2
View File
@@ -13,6 +13,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import processing.app.Base;
import processing.app.Messages;
/**
@@ -79,8 +80,7 @@ public class StreamPump implements Runnable {
}
} catch (final IOException e) {
if (Base.DEBUG) {
System.err.println("StreamPump: " + name);
e.printStackTrace(System.err);
Messages.err("StreamPump: " + name, e);
// removing for 0190, but need a better way to handle these
throw new RuntimeException("Inside " + this + " for " + name, e);
}
@@ -257,31 +257,31 @@ public class DefaultPlatform {
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
public interface CLibrary extends Library {
CLibrary INSTANCE = Native.load("c", CLibrary.class);
int setenv(String name, String value, int overwrite);
String getenv(String name);
int unsetenv(String name);
int putenv(String string);
}
public void setenv(String variable, String value) {
CLibrary clib = CLibrary.INSTANCE;
clib.setenv(variable, value, 1);
}
public String getenv(String variable) {
CLibrary clib = CLibrary.INSTANCE;
return clib.getenv(variable);
}
public int unsetenv(String variable) {
CLibrary clib = CLibrary.INSTANCE;
return clib.unsetenv(variable);
}
// public interface CLibrary extends Library {
// CLibrary INSTANCE = Native.load("c", CLibrary.class);
// int setenv(String name, String value, int overwrite);
// String getenv(String name);
// int unsetenv(String name);
// int putenv(String string);
// }
//
//
// public void setenv(String variable, String value) {
// CLibrary clib = CLibrary.INSTANCE;
// clib.setenv(variable, value, 1);
// }
//
//
// public String getenv(String variable) {
// CLibrary clib = CLibrary.INSTANCE;
// return clib.getenv(variable);
// }
//
//
// public int unsetenv(String variable) {
// CLibrary clib = CLibrary.INSTANCE;
// return clib.unsetenv(variable);
// }
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
@@ -33,7 +33,8 @@ import processing.core.PApplet;
public class LinuxPlatform extends DefaultPlatform {
String homeDir;
// Switched to use ~ as the home directory for compatibility with snap
String homeDir = "~";
public void initBase(Base base) {
@@ -98,7 +99,7 @@ public class LinuxPlatform extends DefaultPlatform {
File configHome = null;
// Check to see if the user has set a different location for their config
String configHomeEnv = getenv("XDG_CONFIG_HOME");
String configHomeEnv = System.getenv("XDG_CONFIG_HOME");
if (configHomeEnv != null && !configHomeEnv.isBlank()) {
configHome = new File(configHomeEnv);
if (!configHome.exists()) {
@@ -79,9 +79,7 @@ public class InputMethodSupport implements InputMethodRequests, InputMethodListe
@Override
public Rectangle getTextLocation(TextHitInfo offset) {
if (Base.DEBUG) {
Messages.log("#Called getTextLocation:" + offset);
}
Messages.log("#Called getTextLocation:" + offset);
int line = textArea.getCaretLine();
int offsetX = textArea.getCaretPosition() - textArea.getLineStartOffset(line);
// '+1' mean textArea.lineToY(line) + textArea.getPainter().getFontMetrics().getHeight().
@@ -238,9 +236,7 @@ public class InputMethodSupport implements InputMethodRequests, InputMethodListe
RenderingHints.VALUE_TEXT_ANTIALIAS_ON :
RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
FontRenderContext frc = g2d.getFontRenderContext();
if (Base.DEBUG) {
Messages.log("debug: FontRenderContext is Antialiased = " + frc.getAntiAliasingHint());
}
Messages.log("debug: FontRenderContext is Antialiased = " + frc.getAntiAliasingHint());
return new TextLayout(composedTextString.getIterator(), frc);
}
+3 -1
View File
@@ -276,7 +276,9 @@ public class EditorConsole extends JScrollPane {
// components, causing deadlock. Updates are buffered to the console and
// displayed at regular intervals on Swing's event-dispatching thread.
// (patch by David Mellis)
consoleDoc.appendString(what, err ? errStyle : stdStyle);
// Remove ANSI escape codes from the text before adding it to the console
String clean = what.replaceAll("\u001B\\[[0-9;]*m", "");
consoleDoc.appendString(clean, err ? errStyle : stdStyle);
}
}
+211
View File
@@ -0,0 +1,211 @@
package processing.app.ui
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.MaterialTheme
import androidx.compose.material.MaterialTheme.colors
import androidx.compose.material.MaterialTheme.typography
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.awt.ComposePanel
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.PointerIcon
import androidx.compose.ui.input.pointer.onPointerEvent
import androidx.compose.ui.input.pointer.pointerHoverIcon
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.WindowPosition
import androidx.compose.ui.window.application
import androidx.compose.ui.window.rememberWindowState
import com.formdev.flatlaf.util.SystemInfo
import com.mikepenz.markdown.compose.Markdown
import com.mikepenz.markdown.m2.markdownColor
import com.mikepenz.markdown.m2.markdownTypography
import com.mikepenz.markdown.model.MarkdownColors
import com.mikepenz.markdown.model.MarkdownTypography
import processing.app.Base.getRevision
import processing.app.Base.getVersionName
import processing.app.ui.theme.LocalLocale
import processing.app.ui.theme.LocalTheme
import processing.app.ui.theme.Locale
import processing.app.ui.theme.ProcessingTheme
import java.awt.Cursor
import java.awt.Dimension
import java.awt.event.KeyAdapter
import java.awt.event.KeyEvent
import java.io.InputStream
import java.util.Properties
import javax.swing.JFrame
import javax.swing.SwingUtilities
class WelcomeToBeta {
companion object{
val windowSize = Dimension(400, 200)
val windowTitle = Locale()["beta.window.title"]
@JvmStatic
fun showWelcomeToBeta() {
val mac = SystemInfo.isMacFullWindowContentSupported
SwingUtilities.invokeLater {
JFrame(windowTitle).apply {
val close = { dispose() }
rootPane.putClientProperty("apple.awt.transparentTitleBar", mac)
rootPane.putClientProperty("apple.awt.fullWindowContent", mac)
defaultCloseOperation = JFrame.DISPOSE_ON_CLOSE
contentPane.add(ComposePanel().apply {
size = windowSize
setContent {
ProcessingTheme {
Box(modifier = Modifier.padding(top = if (mac) 22.dp else 0.dp)) {
welcomeToBeta(close)
}
}
}
})
pack()
background = java.awt.Color.white
setLocationRelativeTo(null)
addKeyListener(object : KeyAdapter() {
override fun keyPressed(e: KeyEvent) {
if (e.keyCode == KeyEvent.VK_ESCAPE) close()
}
})
isResizable = false
isVisible = true
requestFocus()
}
}
}
@Composable
fun welcomeToBeta(close: () -> Unit = {}) {
Row(
modifier = Modifier
.padding(20.dp, 10.dp)
.size(windowSize.width.dp, windowSize.height.dp),
horizontalArrangement = Arrangement
.spacedBy(20.dp)
){
val locale = LocalLocale.current
Image(
painter = painterResource("bird.svg"),
contentDescription = locale["beta.logo"],
modifier = Modifier
.align(Alignment.CenterVertically)
.size(100.dp, 100.dp)
.offset(0.dp, (-25).dp)
)
Column(
modifier = Modifier
.fillMaxHeight(),
verticalArrangement = Arrangement
.spacedBy(
10.dp,
alignment = Alignment.CenterVertically
)
) {
Text(
text = locale["beta.title"],
style = typography.subtitle1,
)
val text = locale["beta.message"]
.replace('$' + "version", getVersionName())
.replace('$' + "revision", getRevision().toString())
Markdown(
text,
colors = markdownColor(),
typography = markdownTypography(text = typography.body1, link = typography.body1.copy(color = colors.primary)),
modifier = Modifier.background(Color.Transparent).padding(bottom = 10.dp)
)
Row {
Spacer(modifier = Modifier.weight(1f))
PDEButton(onClick = {
close()
}) {
Text(
text = locale["beta.button"],
color = colors.onPrimary
)
}
}
}
}
}
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun PDEButton(onClick: () -> Unit, content: @Composable BoxScope.() -> Unit) {
val theme = LocalTheme.current
var hover by remember { mutableStateOf(false) }
var clicked by remember { mutableStateOf(false) }
val offset by animateFloatAsState(if (hover) -5f else 5f)
val color by animateColorAsState(if(clicked) colors.primaryVariant else colors.primary)
Box(modifier = Modifier.padding(end = 5.dp, top = 5.dp)) {
Box(
modifier = Modifier
.offset((-offset).dp, (offset).dp)
.background(theme.getColor("toolbar.button.pressed.field"))
.matchParentSize()
)
Box(
modifier = Modifier
.onPointerEvent(PointerEventType.Press) {
clicked = true
}
.onPointerEvent(PointerEventType.Release) {
clicked = false
onClick()
}
.onPointerEvent(PointerEventType.Enter) {
hover = true
}
.onPointerEvent(PointerEventType.Exit) {
hover = false
}
.pointerHoverIcon(PointerIcon(Cursor(Cursor.HAND_CURSOR)))
.background(color)
.padding(10.dp)
.sizeIn(minWidth = 100.dp),
contentAlignment = Alignment.Center,
content = content
)
}
}
@JvmStatic
fun main(args: Array<String>) {
application {
val windowState = rememberWindowState(
size = DpSize.Unspecified,
position = WindowPosition(Alignment.Center)
)
Window(onCloseRequest = ::exitApplication, state = windowState, title = windowTitle) {
ProcessingTheme {
Surface(color = colors.background) {
welcomeToBeta {
exitApplication()
}
}
}
}
}
}
}
}
+45
View File
@@ -0,0 +1,45 @@
package processing.app.ui.theme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.compositionLocalOf
import processing.app.LocalPreferences
import processing.app.Messages
import processing.app.Platform
import processing.app.PlatformStart
import processing.app.watchFile
import java.io.File
import java.io.InputStream
import java.util.*
class Locale(language: String = "") : Properties() {
init {
val locale = java.util.Locale.getDefault()
load(ClassLoader.getSystemResourceAsStream("PDE.properties"))
load(ClassLoader.getSystemResourceAsStream("PDE_${locale.language}.properties") ?: InputStream.nullInputStream())
load(ClassLoader.getSystemResourceAsStream("PDE_${locale.toLanguageTag()}.properties") ?: InputStream.nullInputStream())
load(ClassLoader.getSystemResourceAsStream("PDE_${language}.properties") ?: InputStream.nullInputStream())
}
@Deprecated("Use get instead", ReplaceWith("get(key)"))
override fun getProperty(key: String?, default: String): String {
val value = super.getProperty(key, default)
if(value == default) Messages.log("Missing translation for $key")
return value
}
operator fun get(key: String): String = getProperty(key, key)
}
val LocalLocale = compositionLocalOf { Locale() }
@Composable
fun LocaleProvider(content: @Composable () -> Unit) {
PlatformStart()
val settingsFolder = Platform.getSettingsFolder()
val languageFile = File(settingsFolder, "language.txt")
watchFile(languageFile)
val locale = Locale(languageFile.readText().substring(0, 2))
CompositionLocalProvider(LocalLocale provides locale) {
content()
}
}
+75
View File
@@ -0,0 +1,75 @@
package processing.app.ui.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.Colors
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.graphics.Color
import processing.app.LocalPreferences
import processing.app.PreferencesProvider
import java.io.InputStream
import java.util.Properties
class Theme(themeFile: String? = "") : Properties() {
init {
load(ClassLoader.getSystemResourceAsStream("theme.txt"))
load(ClassLoader.getSystemResourceAsStream(themeFile) ?: InputStream.nullInputStream())
}
fun getColor(key: String): Color {
return Color(getProperty(key).toColorInt())
}
}
val LocalTheme = compositionLocalOf<Theme> { error("No theme provided") }
@Composable
fun ProcessingTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable() () -> Unit
) {
PreferencesProvider {
val preferences = LocalPreferences.current
val theme = Theme(preferences.getProperty("theme"))
val colors = Colors(
primary = theme.getColor("editor.gradient.top"),
primaryVariant = theme.getColor("toolbar.button.pressed.field"),
secondary = theme.getColor("editor.gradient.bottom"),
secondaryVariant = theme.getColor("editor.scrollbar.thumb.pressed.color"),
background = theme.getColor("editor.bgcolor"),
surface = theme.getColor("editor.bgcolor"),
error = theme.getColor("status.error.bgcolor"),
onPrimary = theme.getColor("toolbar.button.enabled.field"),
onSecondary = theme.getColor("toolbar.button.enabled.field"),
onBackground = theme.getColor("editor.fgcolor"),
onSurface = theme.getColor("editor.fgcolor"),
onError = theme.getColor("status.error.fgcolor"),
isLight = theme.getProperty("laf.mode").equals("light")
)
CompositionLocalProvider(LocalTheme provides theme) {
LocaleProvider {
MaterialTheme(
colors = colors,
typography = Typography,
content = content
)
}
}
}
}
fun String.toColorInt(): Int {
if (this[0] == '#') {
var color = substring(1).toLong(16)
if (length == 7) {
color = color or 0x00000000ff000000L
} else if (length != 9) {
throw IllegalArgumentException("Unknown color")
}
return color.toInt()
}
throw IllegalArgumentException("Unknown color")
}
@@ -0,0 +1,38 @@
package processing.app.ui.theme
import androidx.compose.material.MaterialTheme.typography
import androidx.compose.material.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.platform.Font
import androidx.compose.ui.unit.sp
val processingFont = FontFamily(
Font(
resource = "ProcessingSans-Regular.ttf",
weight = FontWeight.Normal,
style = FontStyle.Normal
),
Font(
resource = "ProcessingSans-Bold.ttf",
weight = FontWeight.Bold,
style = FontStyle.Normal
)
)
val Typography = Typography(
body1 = TextStyle(
fontFamily = processingFont,
fontWeight = FontWeight.Normal,
fontSize = 13.sp,
lineHeight = 16.sp
),
subtitle1 = TextStyle(
fontFamily = processingFont,
fontWeight = FontWeight.Bold,
fontSize = 16.sp,
lineHeight = 20.sp
)
)
+113
View File
@@ -0,0 +1,113 @@
package processing.app
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource
import org.mockito.ArgumentCaptor
import org.mockito.MockedStatic
import org.mockito.Mockito.mockStatic
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import java.io.File
import kotlin.io.encoding.Base64
import kotlin.io.encoding.ExperimentalEncodingApi
import kotlin.test.Test
class SchemaTest {
private val base: Base = mock<Base>{
}
companion object {
val preferences: MockedStatic<Preferences> = mockStatic(Preferences::class.java)
}
@Test
fun testLocalFiles() {
val file = "/this/is/a/local/file"
Schema.handleSchema("pde://$file", base)
verify(base).handleOpen(file)
}
@Test
fun testNewSketch() {
Schema.handleSchema("pde://sketch/new", base)
verify(base).handleNew()
}
@OptIn(ExperimentalEncodingApi::class)
@Test
fun testBase64SketchAndExtraFiles() {
val sketch = """
void setup(){
}
void draw(){
}
""".trimIndent()
val base64 = Base64.encode(sketch.toByteArray())
Schema.handleSchema("pde://sketch/base64/$base64?pde=AnotherFile:$base64", base)
val captor = ArgumentCaptor.forClass(String::class.java)
verify(base).handleOpenUntitled(captor.capture())
val file = File(captor.value)
assert(file.exists())
assert(file.readText() == sketch)
val extra = file.parentFile.resolve("AnotherFile.pde")
assert(extra.exists())
assert(extra.readText() == sketch)
file.parentFile.deleteRecursively()
}
@Test
fun testURLSketch() {
Schema.handleSchema("pde://sketch/url/github.com/processing/processing-examples/raw/refs/heads/main/Basics/Arrays/Array/Array.pde", base)
val captor = ArgumentCaptor.forClass(String::class.java)
verify(base).handleOpenUntitled(captor.capture())
val output = File(captor.value)
assert(output.exists())
assert(output.name == "Array.pde")
assert(output.extension == "pde")
assert(output.parentFile.name == "Array")
output.parentFile.parentFile.deleteRecursively()
}
@ParameterizedTest
@ValueSource(strings = [
"Module.pde:https://github.com/processing/processing-examples/raw/refs/heads/main/Basics/Arrays/ArrayObjects/Module.pde",
"Module.pde",
"Module:Module.pde",
"Module:https://github.com/processing/processing-examples/raw/refs/heads/main/Basics/Arrays/ArrayObjects/Module.pde",
"Module.pde:github.com/processing/processing-examples/raw/refs/heads/main/Basics/Arrays/ArrayObjects/Module.pde"
])
fun testURLSketchWithFile(file: String){
Schema.handleSchema("pde://sketch/url/github.com/processing/processing-examples/raw/refs/heads/main/Basics/Arrays/ArrayObjects/ArrayObjects.pde?pde=$file", base)
val captor = ArgumentCaptor.forClass(String::class.java)
verify(base).handleOpenUntitled(captor.capture())
// wait for threads to resolve
Thread.sleep(1000)
val output = File(captor.value)
assert(output.parentFile.name == "ArrayObjects")
assert(output.exists())
assert(output.parentFile.resolve("Module.pde").exists())
output.parentFile.parentFile.deleteRecursively()
}
@Test
fun testPreferences() {
Schema.handleSchema("pde://preferences?test=value", base)
preferences.verify {
Preferences.set("test", "value")
Preferences.save()
}
}
}
File diff suppressed because it is too large Load Diff
+9
View File
@@ -0,0 +1,9 @@
<Project Sdk="WixToolset.Sdk/5.0.1">
<PropertyGroup>
<OutputPath>..\build\compose\binaries\main\msi</OutputPath>
<OutputName>Processing-$(Version)</OutputName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="WixToolset.UI.wixext" Version="5.0.1" />
</ItemGroup>
</Project>
+35
View File
@@ -0,0 +1,35 @@
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
<Package Name="Processing" Manufacturer="Processing Foundation" Version="$(Version)" UpgradeCode="89d8d7fe-5602-4b12-ba10-0fe78efbd602">
<Icon Id="icon.ico" SourceFile="..\..\build\windows\processing.ico" />
<Property Id="ARPPRODUCTICON" Value="icon.ico" />
<MediaTemplate EmbedCab="yes" />
<ui:WixUI Id="WixUI_Mondo" InstallDirectory="INSTALLFOLDER" />
<WixVariable Id="WixUILicenseRtf" Value="LICENSE.rtf" />
<WixVariable Id="WixUIDialogBmp" Value="background.png" />
<WixVariable Id="WixUIBannerBmp" Value="banner.png" />
<Feature Id="MainApplication" Title="Processing" Level="1">
<Files Include="..\build\compose\binaries\main\app\Processing\**" />
<ComponentRef Id="ApplicationShortcut" />
</Feature>
<Directory Id="ProgramMenuFolder">
<Directory Id="ApplicationProgramsFolder" Name="Processing"/>
</Directory>
<Directory Id="ProgramFiles64Folder">
<Directory Id="INSTALLFOLDER" Name="Processing">
</Directory>
</Directory>
<Component Id="ApplicationShortcut" Guid="b15e6d69-f054-4ec2-aade-8e3756b537d6">
<Shortcut Id="ApplicationStartMenuShortcut"
Name="Processing"
Description="My Application Description"
Directory="ApplicationProgramsFolder"
Target="[INSTALLFOLDER]\Processing.exe"
WorkingDirectory="INSTALLFOLDER"/>
<RemoveFolder Id="CleanUpShortCut" Directory="ApplicationProgramsFolder" On="uninstall"/>
<RegistryValue Root="HKCU" Key="Software\Processing Foundation\Processing" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
</Component>
</Package>
</Wix>
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

-3
View File
@@ -1,6 +1,3 @@
group = "org.processing"
version = "4.4.0"
plugins {
kotlin("jvm") version libs.versions.kotlin apply false
alias(libs.plugins.kotlinMultiplatform) apply false
@@ -613,7 +613,6 @@ update_check = Update
update_check.updates_available.core = A new version of Processing is available,\nwould you like to visit the Processing download page?
update_check.updates_available.contributions = There are updates available for some of the installed contributions,\nwould you like to open the the Contribution Manager now?
# ---------------------------------------
# Color Chooser
@@ -315,6 +315,13 @@ update_check = Update
update_check.updates_available.core = Een nieuwe versie van Processing is beschikbaar,\nwilt u de Processing download pagina bezoeken?
update_check.updates_available.contributions = Er zijn updates beschikbaar voor sommige van de door u geïnstalleerde bijdragen,\nwilt u nu de Bijdragen Manager openen?
# ---------------------------------------
# Beta
beta.window.title = Welkom bij Beta
beta.title = Welkom bij de Processing Beta
beta.message = Bedankt dat je de nieuwe versie van Processing uitprobeert. We zijn je zeer dankbaar!\n\nMeld eventuele bugs alsjeblieft op de forums.
beta.button = Okee!
# ---------------------------------------
# Color Chooser
+1 -1
View File
@@ -63,7 +63,7 @@ dependencies {
```
## Developing for Core
The easiest way to develop for core, without the need to build the whole project, is to use the `examples/src` sketches. In
The easiest way to develop for core, without the need to build the whole project, is to use the `examples/src` sketches.
## PGraphics Modes
Documentation on how to develop graphics modes as a library should go here.
+9 -3
View File
@@ -6,8 +6,6 @@ plugins {
alias(libs.plugins.mavenPublish)
}
version = rootProject.version
repositories {
mavenCentral()
maven { url = uri("https://jogamp.org/deployment/maven") }
@@ -23,6 +21,11 @@ sourceSets{
exclude("**/*.java")
}
}
test{
java{
srcDirs("test")
}
}
}
dependencies {
@@ -70,4 +73,7 @@ tasks.test {
}
tasks.withType<Jar> {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
}
tasks.compileJava{
options.encoding = "UTF-8"
}
+7 -1
View File
@@ -3,17 +3,21 @@ kotlin = "2.0.20"
compose-plugin = "1.7.1"
jogl = "2.5.0"
antlr = "4.13.2"
jupiter = "5.12.0"
[libraries]
jogl = { module = "org.jogamp.jogl:jogl-all-main", version.ref = "jogl" }
gluegen = { module = "org.jogamp.gluegen:gluegen-rt-main", version.ref = "jogl" }
flatlaf = { module = "com.formdev:flatlaf", version = "3.4.1" }
flatlaf = { module = "com.formdev:flatlaf", version = "2.4" }
jna = { module = "net.java.dev.jna:jna", version = "5.12.1" }
jnaplatform = { module = "net.java.dev.jna:jna-platform", version = "5.12.1" }
compottie = { module = "io.github.alexzhirkevich:compottie", version = "2.0.0-rc02" }
kaml = { module = "com.charleskorn.kaml:kaml", version = "0.65.0" }
junit = { module = "junit:junit", version = "4.13.2" }
junitJupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "jupiter" }
junitJupiterParams = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "jupiter" }
mockito = { module = "org.mockito:mockito-core", version = "4.11.0" }
mockitoKotlin = { module = "org.mockito.kotlin:mockito-kotlin", version = "5.4.0" }
antlr = { module = "org.antlr:antlr4", version = "4.7.2" }
eclipseJDT = { module = "org.eclipse.jdt:org.eclipse.jdt.core", version = "3.16.0" }
eclipseJDTCompiler = { module = "org.eclipse.jdt:org.eclipse.jdt.compiler.apt", version = "1.3.400" }
@@ -27,6 +31,8 @@ antlr4Runtime = { module = "org.antlr:antlr4-runtime", version.ref = "antlr" }
composeGradlePlugin = { module = "org.jetbrains.compose:compose-gradle-plugin", version.ref = "compose-plugin" }
kotlinGradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
kotlinComposePlugin = { module = "org.jetbrains.kotlin.plugin.compose:org.jetbrains.kotlin.plugin.compose.gradle.plugin", version.ref = "kotlin" }
markdown = { module = "com.mikepenz:multiplatform-markdown-renderer-m2", version = "0.31.0" }
markdownJVM = { module = "com.mikepenz:multiplatform-markdown-renderer-jvm", version = "0.31.0" }
[plugins]
jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" }
-2
View File
@@ -5,8 +5,6 @@ plugins{
id("com.vanniktech.maven.publish") version "0.30.0"
}
group = "org.processing"
repositories{
mavenCentral()
google()
-2
View File
@@ -6,8 +6,6 @@ plugins{
alias(libs.plugins.mavenPublish)
}
version = rootProject.version
repositories{
mavenCentral()
google()
@@ -287,7 +287,7 @@ public class CompletionPanel {
int x = ta.getCaretPosition() - ta.getLineStartOffset(line) - 1, x1 = x - 1;
if (x >= s.length() || x < 0)
return null; //TODO: Does this check cause problems? Verify.
if (Base.DEBUG) System.out.print(" x char: " + s.charAt(x));
Messages.log(" x char: " + s.charAt(x));
String word = String.valueOf(s.charAt(x));
if (s.trim().length() == 1) {
@@ -117,7 +117,7 @@ public class PreprocService {
running = true;
PreprocSketch prevResult = null;
CompletableFuture<?> runningCallbacks = null;
Messages.log("PPS: Hi!");
Messages.log("Hi!");
while (running) {
try {
try {
@@ -127,7 +127,7 @@ public class PreprocService {
break;
}
Messages.log("PPS: Starting");
Messages.log("Starting");
prevResult = preprocessSketch(prevResult);
@@ -143,7 +143,7 @@ public class PreprocService {
synchronized (requestLock) {
if (requestQueue.isEmpty()) {
runningCallbacks = lastCallback;
Messages.log("PPS: Done");
Messages.log("Done");
preprocessingTask.complete(prevResult);
}
}
@@ -151,7 +151,7 @@ public class PreprocService {
Messages.err("problem in preprocessor service loop", e);
}
}
Messages.log("PPS: Bye!");
Messages.log("Bye!");
}
/**
@@ -188,7 +188,7 @@ public class PreprocService {
* Indicate to this service that the sketch libraries have changed.
*/
public void notifyLibrariesChanged() {
Messages.log("PPS: notified libraries changed");
Messages.log("notified libraries changed");
librariesChanged.set(true);
notifySketchChanged();
}
@@ -197,7 +197,7 @@ public class PreprocService {
* Indicate to this service that the folder housing sketch code has changed.
*/
public void notifyCodeFolderChanged() {
Messages.log("PPS: notified code folder changed");
Messages.log("notified code folder changed");
codeFolderChanged.set(true);
notifySketchChanged();
}
@@ -216,7 +216,7 @@ public class PreprocService {
.thenAcceptBothAsync(lastCallback, (ps, a) -> callback.accept(ps))
// Make sure exception in callback won't cancel whole callback chain
.handleAsync((res, e) -> {
if (e != null) Messages.err("PPS: exception in callback", e);
if (e != null) Messages.err("exception in callback", e);
return res;
});
return lastCallback;
@@ -253,19 +253,19 @@ public class Runner implements MessageConsumer {
// while (!available) {
while (true) {
try {
Messages.log(getClass().getName() + " attempting to attach to VM");
Messages.log("attempting to attach to VM");
synchronized (cancelLock) {
vm = connector.attach(arguments);
if (cancelled && vm != null) {
// cancelled and connected to the VM, handle closing now
Messages.log(getClass().getName() + " aborting, launch cancelled");
Messages.log("aborting, launch cancelled");
close();
return false;
}
}
// vm = connector.attach(arguments);
if (vm != null) {
Messages.log(getClass().getName() + " attached to the VM");
Messages.log("attached to the VM");
// generateTrace();
// available = true;
return true;
@@ -273,17 +273,17 @@ public class Runner implements MessageConsumer {
} catch (ConnectException ce) {
// This will fire ConnectException (socket not available) until
// the VM finishes starting up and opens its socket for us.
Messages.log(getClass().getName() + " socket for VM not ready");
Messages.log("socket for VM not ready");
// System.out.println("waiting");
// e.printStackTrace();
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
Messages.err(getClass().getName() + " interrupted", ie);
Messages.err("interrupted", ie);
// ie.printStackTrace(sketchErr);
}
} catch (IOException e) {
Messages.err(getClass().getName() + " while attaching to VM", e);
Messages.err("while attaching to VM", e);
}
}
// } catch (IOException exc) {