diff --git a/.all-contributorsrc b/.all-contributorsrc index 5f1aa5cc6..ef4a6d2dd 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1465,6 +1465,34 @@ "contributions": [ "code" ] + }, + { + "login": "Rishab87", + "name": "Rishab Kumar Jha", + "avatar_url": "https://avatars.githubusercontent.com/u/138858208?v=4", + "profile": "https://github.com/Rishab87", + "contributions": [ + "code" + ] + }, + { + "login": "yehiarasheed", + "name": "Yehia Rasheed", + "avatar_url": "https://avatars.githubusercontent.com/u/157399068?v=4", + "profile": "https://github.com/yehiarasheed", + "contributions": [ + "code" + ] + }, + { + "login": "babaissarkar", + "name": "Subhraman Sarkar", + "avatar_url": "https://avatars.githubusercontent.com/u/8469888?v=4", + "profile": "https://github.com/babaissarkar", + "contributions": [ + "code", + "a11y" + ] } ], "repoType": "github", diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4182c98b0..b1b7f9ad6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: Pre-releases +name: Branch Builds (Legacy) on: push: paths-ignore: diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 02a71ba3c..5831a166a 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -1,4 +1,4 @@ -name: Pull Requests +name: Pull Requests (Legacy) on: pull_request: paths-ignore: diff --git a/.github/workflows/release-gradle.yml b/.github/workflows/release-gradle.yml index cafc22ce3..16e8984e3 100644 --- a/.github/workflows/release-gradle.yml +++ b/.github/workflows/release-gradle.yml @@ -134,7 +134,7 @@ jobs: - name: Install Java uses: actions/setup-java@v4 with: - java-version: '17' + java-version: '17.0.8' distribution: 'temurin' architecture: ${{ matrix.arch }} - name: Setup Gradle @@ -153,6 +153,22 @@ jobs: 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: 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: ${{ secrets.AZURE_SIGNING_ACCOUNT_NAME }} + certificate-profile-name: ${{ secrets.AZURE_CERTIFICATE_PROFILE_NAME }} + 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 - name: Upload portables to release uses: svenstaro/upload-release-action@v2 @@ -174,18 +190,4 @@ jobs: 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 \ No newline at end of file + \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 44681a78f..bf4d33cd8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -name: Releases +name: Releases (Legacy) on: release: types: [published] diff --git a/.gitignore b/.gitignore index 7faa26d70..26b5b9756 100644 --- a/.gitignore +++ b/.gitignore @@ -113,3 +113,5 @@ java/build/ .build/ /app/windows/obj +/java/gradle/build +/java/gradle/example/.processing diff --git a/.idea/icon.svg b/.idea/icon.svg index 149e0a180..796997b91 100644 --- a/.idea/icon.svg +++ b/.idea/icon.svg @@ -1,5 +1,40 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BUILD.md b/BUILD.md index 672716639..b73147e8b 100644 --- a/BUILD.md +++ b/BUILD.md @@ -25,36 +25,37 @@ Instructions for other editors are welcome and feel free to contribute the docum ## Architecture -Processing is made of three distinct parts, the `Core`, `Java` and the `App`. The `Core` currently stands alone and `Java` and `App` depend on it. `Java` and `App` are currently interdependent but we are working on decoupling those two. +Processing consists of three main components: `Core`, `Java`, and `App`. The `Core` is independent, while `Java` and `App` depend on it. Currently, `Java` and `App` are interdependent, but efforts are underway to decouple them. -`Core`: The part of the code that gets bundled with your sketches, so the functionality like `ellipse(25,25,50,50);` The inner workings of that function can be found here. +- **Core**: The essential code included with your sketches that provides Processing’s basic functions. When you use functions like `ellipse(25,25,50,50)` or `background(255)`, their underlying code is part of `Core`. -`Java`: This is the pipeline that will take your `.pde` file and compile and run it. The PDE understands different _modes_ with `Java` being the primary one. +- **Java**: The part of Processing that compiles and runs `.pde` files. It supports different *modes* which implement support for different languages or versions of Processing. The default mode is `Java`. + +- **App**: This is the Processing Development Environment (PDE), the visual part of the editor that you see and work within when you use Processing. -`App`: This is the PDE, the visual part of the editor that you see and work within when you use Processing. ### Examples - You want to fix a bug with one of the argument of a function that you use in a sketch. The `Core` is probably where you would find the implementation of the function that you would like to modify. -- A feature/bug of the PDE editor has been driving you nuts, and you can no longer stand it. You would probably find your bug in the `App` section of this project. -- You've written a large sketch and Processing has become slow to compile, a place to improve this code can probably be found in the `Java` section. +- A bug of the PDE editor has been keeping you up at night, you would likely find the relevant code in the `App` section of this project. +- If you've written a large sketch and Processing has become slow to compile and run it, a place to improve this code can most likely be found in the `Java` section. ## User interface -Traditionally Processing has been written in Java swing and Flatlaf (and some html & css). Since 2025 we have switched to include Jetpack Compose, for a variety of reasons but mostly for it's inter-compatibility. There were ideas to switch to a React based editor, but this approach allows us to slowly replace Java swing components to Jetpack Compose, Ship of Theseus style. +Historically, Processing's UI has been written in Java Swing and Flatlaf (and some html & css). Since 2025 we have switched to include Jetpack Compose, mostly for it's backwards-compatibility with Swing. This approach allows us to gradually replace Java Swing components with Jetpack Compose ones, instead of doing a complete overhaul of the editor. ## Build system -We use `Gradle` as the build system for Processing. This used to be `Ant` but we have switched to be more in line with modern standards and to hopefully switch the internal build system in the `Java` mode to `Gradle` as well, unifying both systems for simplicity. +We use `Gradle` as the build system for Processing. Until 2025, Processing used `Ant` but we have switched to `Gradle` to be more in line with modern standards. We plan to migrate the internal build system of the `Java` mode to `Gradle` as well, unifying both systems for simplicity. ## Kotlin vs Java -Since introducing the Gradle build system we also support Kotlin within the repository. Refactors from Java to Kotlin are not really necessary at this stage, but all new functionality should be written in Kotlin. +With the introduction of the Gradle build system we now support Kotlin within the repository. Refactors from Java to Kotlin are not necessary at this stage, but all new functionality should be written in Kotlin. -Any classes that end in `..Kt.Java` are there for backwards compatibility with the `Ant` build system and can be removed when that is no longer necessary. +Any classes that end up being written in Kotlin have their equivalent Java class under `app/ant/` source directory. ### Running Processing -The main task to run or debug the PDE is `app:run` this run the application with `compose desktop` +The main task to run or debug the PDE is `run`. That means you just need to run `./gradlew run` (Linux) or `./gradlew.bat run` (Windows) to build and run Processing. -If your main concern is with the `Core` you don't need to start the whole PDE to test your changes. In IntelliJ IDEA you can select any of the sketches in `core/examples/src/.../` to run by click on the green arrow next to their main functions. This will just compile core and the example sketch. Feel free to also new examples for your newly added functionality. +If your main concern is with the `Core` you don't need to build and start the whole PDE to test your changes. In IntelliJ IDEA you can select any of the sketches in `core/examples/src/.../` to run by click on the green arrow next to their main functions. This will just compile core and the example sketch. Feel free to create additional examples for your new functionality. ## Other editors diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8676bf662..d764d4150 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,106 @@ -## Welcome to Processing! +# Contributing to Processing on GitHub -Thanks for your interest in contributing to Processing! Processing is a collaborative project with contributions from many volunteers. Our community is always looking for contributors and appreciates involvement in all forms. We acknowledge that not everyone has the capacity, time, or financial means to participate actively or in the same ways. We want to expand the meaning of the word “contributor.” Whether you're an experienced developer or just starting out, we value your involvement. Your unique perspectives, skills, and experiences enrich our community, and we encourage you to get involved in a way that works for you. It includes documentation, teaching, writing code, making art, writing, design, activism, organizing, curating, or anything else you might imagine. The [p5.js contribute page](https://p5js.org/contribute/) gives a great overview of different ways to get involved and contribute. +Welcome to the contributor guidelines! + +This document is for new contributors looking to contribute code to Processing, contributors refreshing their memory on some technical steps, or anyone interested in working on Processing’s codebase. We believe that anyone can be a contributor. You don’t need to be an expert. We also know that not everyone has the same time, energy, or resources to spend on Processing. That’s okay. We’re glad you’re here! + +> [!TIP] +> For questions about your own sketches, or broader conversations about coding in Processing, our [online forum](https://discourse.processing.org/) is a fantastic resource (make sure to read the [forum guidelines](https://discourse.processing.org/t/welcome-to-the-processing-foundation-discourse/8) before posting). You can also visit the [Processing Community Discord](https://discord.gg/8pFwMVATh8). + +## About GitHub +Processing’s codebase is hosted on [GitHub](https://github.com/processing). GitHub is a website where people can collaborate on code. It’s widely used for open source projects and makes it easier to keep track of changes, report issues with the software, and contribute improvements to the code. + +If you're new to GitHub, a good place to start is [this tutorial](https://github.com/firstcontributions/first-contributions/blob/main/docs/gui-tool-tutorials/github-desktop-tutorial.md) guide, which walks you through the basics of contributing to a project using GitHub Desktop. For more information, we recommend [Git and GitHub for Poets](https://www.youtube.com/playlist?list=PLRqwX-V7Uu6ZF9C0YMKuns9sLDzK6zoiV), a beginner-friendly video series by Dan Shiffman. + +## About issues + +Most activity on Processing’s GitHub happens in _issues_. Issues are GitHub posts which can contain bug reports, feature requests, or broader discussions about the development of Processing. It’s a great place to begin contributing. + +To file a new issue, visit the [Issues](https://github.com/processing/processing4/issues) tab on the repository and click `New issue` then select the most appropriate template and follow the included instructions. These templates help maintainers understand and respond to issues faster. + +## Working on the Processing codebase + +### Prerequisites + +To contribute to Processing, we recommend using [GitHub Desktop](https://github.com/apps/desktop) and [IntelliJ IDEA (Community Edition)](https://www.jetbrains.com/idea/download/), as that’s the toolchain we’re best able to support. If you’re comfortable using Git on the command line or prefer a different editor, that’s totally fine too! Use what works best for you. Some familiarity with the [command line](https://developer.mozilla.org/en-US/docs/Learn_web_development/Getting_started/Environment_setup/Command_line) can help, but it’s not required. + +You'll need to set up a local development environment—see our [build instructions](https://github.com/processing/processing4/blob/main/BUILD.md) to get started. + +### Making your first contribution + +Most issues marked [help wanted](https://github.com/processing/processing4/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) or [good first issue](https://github.com/processing/processing4/issues?q=is%3Aissue%20is%3Aopen%20label%3A%22good%20first%20issue%22%20) are a good place to start. + +Before beginning work on a code contribution, please make sure that: + +- The issue has been discussed and a proposed solution has been approved. +- You have been **assigned** to the issue. + +If an implementation has been agreed upon but no one has volunteered to take it on, feel free to comment and offer to help. A maintainer can then assign the issue to you. + +Please do **not** open a pull request for an issue that is already assigned to someone else. We follow a “first assigned, first served” approach to avoid duplicated work. If you open a PR for an issue that someone else is already working on, your PR will be closed. + +If an issue has been inactive for a long time, you’re welcome to check in politely by commenting to see if the assignee still plans to work on it or would be open to someone else taking over. + +There’s no hard deadline for completing contributions. We understand that people often contribute on a volunteer basis and timelines may vary. That said, if you run into trouble or have questions at any point, don’t hesitate to ask for help in the issue thread. Maintainers and other community members are here to support you. + +### Follow the style guidelines +Keep the [style guidelines](https://github.com/processing/processing/wiki/Style-Guidelines) in mind when making changes to the code. If you don’t, someone else will have to reformat your code so that it fits everything else (or we’ll have to reject your changes if it’ll take us too long to clean things up). + +### Test locally +Before you contribute your changes, it's essential that you make sure that Processing still builds, runs, and functions on your machine. Here again, the [build instructions](https://github.com/processing/processing4/blob/main/BUILD.md) are your best friend. Pay special attention to any features that may be affected by your changes. Does everything still work as before? Great! + +## Submit a pull request (PR) + +Once your changes are ready: + +1. Push your branch to your fork +2. Open a pull request from your branch into `main` on the official repository +3. Fill out the pull request information: + + - **Title**: clear and descriptive + - **Resolves**: add `Resolves #[issue-number]` if applicable + - **Changes**: explain what you changed and why + - **Tests**: mention if you added tests or validated your changes + - **Checklist**: ensure tests pass and the branch is up-to-date + +Maintainers usually review pull requests within one to two weeks. If changes are requested, follow up by pushing additional commits. The PR will automatically update. + +If there hasn’t been any activity after two weeks, feel free to gently follow up. We kindly ask that you don’t request a review or tag maintainers before that time. Thanks for your patience! + +Before opening a pull request, please make sure to discuss the related issue and get assigned to it first. This helps us stay aligned and avoid unnecessary work. Thank you! + +## New Features + +In most cases, the best way to contribute a new feature is to create a library. The [Processing Library Template](https://github.com/processing/processing-library-template) is a great way to get started. For more instructions, see the [library template documenation](https://processing.github.io/processing-library-template/). + +Nearly all new features are first introduced as a Library or a Mode, or even as an example. The current [OpenGL renderer](http://glgraphics.sourceforge.net/) and Video library began as separate projects by Andrés Colubri, who needed a more performant, more sophisticated version of what we had in Processing for work that he was creating. The original `loadShape()` implementation came from the “Candy” library by Michael Chang (“mflux“). Similarly, Tweak Mode began as a [separate project](http://galsasson.com/tweakmode/) by Gal Sasson before being incorporated. PDE X was a Google Summer of code [project](https://github.com/processing/processing-experimental) by Manindra Moharana that updated the PDE to include basic refactoring and better error checking. + +Developing features separately from the main software has several benefits: + +* It’s easier for the contributor to develop the software without it needing to work for tens or hundreds of thousands of Processing users. +* It provides a way to get feedback on that code independently of everything else, and the ability to iterate on it rapidly. +* This feedback process also helps gauge the level of interest for the community, and how it should be prioritized for the software. +* We can delay the process of “normalizing” the features so that they’re consistent with the rest of Processing (function naming, structure, etc). + +A major consideration for any new feature is the level of maintenance that it might require in the future. If the original maintainer loses interest over time (which is normal) and the feature breaks (which happens more often than we'd like), it sits on the issues list unfixed, which isn’t good for anyone. + +Processing is a massive project that has existed for more than 20 years. Part of its longevity comes from the effort that’s gone into keeping things as simple as we can, and in particular, making a lot of difficult decisions about *what to leave out*. Adding a new feature always has to be weighed against the potential confusion of one more thing—whether it’s a menu item, a dialog box, a function that needs to be added to the reference, etc. Adding a new graphics function means making it work across all the renderers that we ship (Java2D, OpenGL, JavaFX, PDF, etc) and across platforms (macOS, Windows, Linux). Does the feature help enough people that it's worth making the reference longer? Or the additional burden of maintaining that feature? It's no fun to say “no,” especially to people volunteering their time, but we often have to. + +## Editor + +The current Editor component is based on the ancient [JEditSyntax](http://syntax.jedit.org/) package and has held up long past its expiration date. [Exhaustive work](https://github.com/processing/processing4/blob/master/app/src/processing/app/syntax/README.md) has been done to look at replacing the component with something more modern, like `RSyntaxArea`, but that approach was considered too risky. + +With Processing 4.4.0, we’ve started transitioning the Processing UI from Swing to Jetpack Compose Multiplatform, allowing us to replace Swing components gradually, without a full rewrite. Any work on updating the PDE and adding new features should focus on this migration. Replacing JEditSyntax will likely be the last step in the process. In the meantime, the editor does what it needs to do, for the intended audience. Features like code-folding, refactoring, or symbol navigation are currently out of scope. + +For users who want editor features beyond what the PDE offers, we’re working to make Processing easier to use in other environments. [Migrating the Processing CLI to Gradle](https://github.com/orgs/processing/projects/32/views/2?filterQuery=CLI&pane=issue&itemId=81026317) and [better Language Server support](https://github.com/orgs/processing/projects/32/views/2?filterQuery=LSP&pane=issue&itemId=90809690) will help make that possible. This should reduce the pressure to add these features to the PDE itself, allowing it to stay focused on being a minimal, beginner-friendly coding sketchbook. If you'd like to help, [let us know](https://github.com/processing/processing4/issues/883)! + +## Refactoring + +Refactoring is fun! There’s always more cleaning to do. It’s also often not very helpful. + +Broadly speaking, the code is built the way it is for a reason. There are so many things that can be improved, but those improvements need to come from an understanding of what’s been built so far. Changes that include refactoring are typically only accepted from contributors who have an established record of working on the code. With a better understanding of the software, the refactoring decisions come from a better, more useful place. + +## Contributor Recognition The Processing project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification, recognizing all types of contributions, not just code. We use the @all-contributors bot to handle adding people to the README.md file. You can ask the @all-contributors bot to add you in an issue or PR comment like so: @@ -10,70 +110,15 @@ The Processing project follows the [all-contributors](https://github.com/kentcdo We usually add contributors automatically after merging a PR, but feel free to request addition yourself by commenting on [this issue](https://github.com/processing/processing4-carbon-aug-19/issues/839). -## Found a Bug? +## Other Ways to Contribute -First, please visit our [troubleshooting](https://github.com/processing/processing/wiki/Troubleshooting) page for common issues—you might find the answer there! +We're always grateful for your help fixing bugs and implementing new features BUT You don’t have to write code to contribute to Processing! Here are just a few other ways to get involved: -For coding questions or help getting started, our [online forum](https://discourse.processing.org/) is a fantastic resource. Make sure to read the [forum guidelines](https://discourse.processing.org/t/welcome-to-the-processing-foundation-discourse/8) before posting. - -If your issue remains unresolved after exploring these options, we'd appreciate it if you could [file a bug report](https://github.com/processing/processing4/issues). Your feedback is crucial as it helps us address issues we might not be aware of yet. - -## Making Your First Contribution - -* **Help Wanted** – Most [issues marked help wanted](https://github.com/processing/processing4/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) are a good place to start. Issues are marked with this tag when: - - * They are isolated enough that someone can jump into it without significant reworking of other code. - * Ben knows that it’s unlikely that he’ll have time to work on them. - -* **The Old Repository** – There are also many “help wanted” [issues in the 3.x repository](https://github.com/processing/processing4/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22). Some of these are very old, so if you're interested in one of these, check in about the priority before putting in too much work! - -* **JavaFX** – There are several [active issues](https://github.com/processing/processing4-javafx/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) for the JavaFX renderer as well. - -* **The `todo.txt` File** – This is *not* a good place to start. It’s Ben’s rambling to-do list, and dates back to the very start of the project over twenty years ago. It shouldn’t be used as a guideline for work to be done, because there are lots of things there that are no longer relevant. Consider it the dusty attic of what’s inside his head. If you see something of interest there, open an issue to see if it’s still a priority, or how it should be approached. But really, there are *so many open issues*, which represent actual problems identified by community members, and they are by far the best place to start. - -* **Style Guidelines** – Keep the [style guidelines](https://github.com/processing/processing/wiki/Style-Guidelines) in mind when submitting pull requests. If you don’t, someone else will have to reformat your code so that it fits everything else (or we’ll have to reject it if it’ll take us too long to clean it up). - -* **Larger Projects** – If you’re looking for a larger project, check out the [Project List](https://github.com/processing/processing/wiki/Project-List#processing) for other ideas. - - -## New Features - -Nearly all new features are first introduced as a Library or a Mode, or even as an example. The current [OpenGL renderer](http://glgraphics.sourceforge.net/) and Video library began as separate projects by Andrés Colubri, who needed a more performant, more sophisticated version of what we had in Processing for work that he was creating. The original `loadShape()` implementation came from the “Candy” library by Michael Chang (“mflux“). - -Similarly, Tweak Mode began as a [separate project](http://galsasson.com/tweakmode/) by Gal Sasson before being incorporated. PDE X was a Google Summer of code [project](https://github.com/processing/processing-experimental) by Manindra Moharana that updated the PDE to include basic refactoring and better error checking. - -Developing features separately from the main software has several benefits: - -* It’s easier for the contributor to develop the software without it needing to work for tens or hundreds of thousands of Processing users. -* It provides a way to get feedback on that code independently of everything else, and the ability to iterate on it rapidly. -* This feedback process also helps gauge the level of interest for the community, and how it should be prioritized for the software. -* We can delay the process of “normalizing” the features so that they’re consistent with the rest of Processing (function naming, structure, etc). - -A major consideration for any new feature is the level of maintenance that it might require in the future. If the original maintainer loses interest over time (which is normal), any ongoing work usually falls to Ben, or it sits on the issues list unfixed, which isn’t good for the community, or for Ben, who has plenty of issues of his own—whether Processing or otherwise. - -Processing is a massive project that has existed for more than 20 years. Part of its longevity comes from the effort that’s gone into keeping things as simple as we can, and in particular, making a lot of difficult decisions about *what to leave out*. Adding a new feature always has to be weighed against the potential confusion of one more thing—whether it’s a menu item, a dialog box, a function that needs to be added to the reference, etc. Adding a new graphics function means making it work across all the renderers that we ship (Java2D, OpenGL, JavaFX, PDF, etc) and across platforms (macOS, Windows, Linux). Does the feature help enough people that it's worth making the reference longer? Or the additional burden of maintaining that feature? It's no fun to say “no,” especially to people volunteering their time, but we often have to. - - -## Editor - -The current Editor, based on the ancient [JEditSyntax](http://syntax.jedit.org/) package has held up long past its expiration date. [Exhaustive work](https://github.com/processing/processing4/blob/master/app/src/processing/app/syntax/README.md) has been done to look at replacing the component with something more modern, like `RSyntaxArea`, but it’s simply not feasible without breaking a massive amount of code, and likely introducing a lot of regressions in the process. All for… code folding? An incrementally better experience? But with potential for major setbacks in low-level code? It’s simply not a path that makes sense. - -With that in mind, any work on updating the editor and adding new features should be focused on further [adapting the preprocessor and compiler](https://github.com/processing/processing4/issues/117) to be wrapped using the [Language Server Protocol](https://en.wikipedia.org/wiki/Language_Server_Protocol), so that we can link to other existing editors (Visual Studio Code and many others). It should be possible to create a Java-only, headless implementation that wraps the current source in this repository and can communicate via LSP. - -The initial work was completed in Processing 4.1, and now needs more testing and implementation of Language Server clients such as [this one](https://github.com/kgtkr/processing-language-server-vscode). - -We can start building a new PDE that’s as simple to use as the current application, based on something like [Theia](https://theia-ide.org/), a new editor platform that uses LSP as its basis. - -With that in mind, nearly all editor enhancement requests will be redirected to this aim. The current editor does what we want it to, for the intended audience, and improving it requires a better foundation as a starting point. - - -## Refactoring - -Refactoring is fun! There’s always more cleaning to do. It’s also often not very helpful. - -Broadly speaking, the code is built the way it is for a reason. There are so many things that can be improved, but those improvements need to come from an understanding of what’s been built so far. Changes that include refactoring are typically only accepted from contributors who have an established record of working on the code. With a better understanding of the software, the refactoring decisions come from a better, more useful place. - - -## Other Details - -This document was hastily thrown together in an attempt to improve the bug reporting and development/contribution process. It doesn’t yet include detail about our intent with the project, the community behind it, our values, and an explanation of how the code itself is designed. +- **Translation** – Help localize the software and documentation in your language. Many of us made our first contribution this way. +- **Testing** – Try out new releases (especially the betas) and [report bugs](https://github.com/processing/processing4/issues/new/choose). +- **Documentation** – Improve tutorials, reference pages, or even this guide! +- **Design** – Contribute UI design ideas or help improve user experience. +- **Community Support** – Answer questions on the [forum](https://discourse.processing.org/). +- **Education** – Create learning resources, curriculums, organize workshops, or share your teaching experiences. +- **Art and Projects** – Share what you’re making with Processing and use the #BuiltWithProcessing hashtag 💙 +- **Outreach and Advocacy** – Help others discover and get excited about the project. diff --git a/README.md b/README.md index 313e4596c..62d3c0ee2 100644 --- a/README.md +++ b/README.md @@ -287,6 +287,11 @@ Add yourself to the contributors list [here](https://github.com/processing/proce Benjamin Fox
Benjamin Fox
💻 e1dem
e1dem

💻 Aditya Chaudhary
Aditya Chaudhary

💻 + Rishab Kumar Jha
Rishab Kumar Jha

💻 + Yehia Rasheed
Yehia Rasheed

💻 + + + Subhraman Sarkar
Subhraman Sarkar

💻 ️️️️♿️ diff --git a/SCHEMA.md b/SCHEMA.md index 23244be7f..b4a09f786 100644 --- a/SCHEMA.md +++ b/SCHEMA.md @@ -1,5 +1,14 @@ # Processing URI Schema Definition +The Processing URI schema defines a custom protocol for launching and interacting with the Processing Development Environment (PDE) via specially formatted `pde://` links. These links can be used to open sketches, create new ones, load hosted or base64-encoded files, and set preferences, all through a simple URI-based interface. + +This feature is primarily intended for integration with web platforms, tutorials, documentation, or third-party tools that want to streamline the experience of launching sketches in Processing from a web page. + +Because these links can be generated dynamically, they support a range of interactive use cases. For example, an online editor could generate a unique link for each sketch, or a forum could create links based on user-submitted code snippets. + +>[!WARNING] +> Be cautious when opening `pde://` links from unknown sources. Always review the contents of a sketch before running it, especially if it was shared by someone you don’t know. To protect your system, Processing runs downloaded sketches in a temporary folder, but you should still treat untrusted code with care. + ## Local File Schema ``` pde:///path/to/sketch.pde @@ -60,4 +69,4 @@ Sets and saves multiple preferences in a single operation. - URL-based operations automatically prepend https:// if no scheme is provided - All URLs and query parameters are decoded using UTF-8 - File downloads occur asynchronously in a separate thread -- Base64 and remote sketches are saved to temporary folders \ No newline at end of file +- Base64 and remote sketches are saved to temporary folders diff --git a/app/build.gradle.kts b/app/build.gradle.kts index fe2ab7249..c4ffaff4d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -53,7 +53,7 @@ compose.desktop { jvmArgs(*listOf( 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.contributions.source", "https://contributions.processing.org/contribs"), 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/"), @@ -73,6 +73,7 @@ compose.desktop { entitlementsFile.set(file("macos/entitlements.plist")) runtimeEntitlementsFile.set(file("macos/entitlements.plist")) appStore = true + jvmArgs("-Dsun.java2d.metal=true") } windows{ iconFile = rootProject.file("build/windows/processing.ico") @@ -243,6 +244,7 @@ tasks.register("generateSnapConfiguration"){ - x11 - network - opengl + - home parts: processing: @@ -358,6 +360,7 @@ tasks.register("includeJdk") { into(composeResources("")) } } + finalizedBy("prepareAppResources") } tasks.register("includeSharedAssets"){ from("../build/shared/") @@ -515,7 +518,6 @@ afterEvaluate { dependsOn( "includeCore", "includeJavaMode", - "includeJdk", "includeSharedAssets", "includeProcessingExamples", "includeProcessingWebsiteExamples", @@ -543,7 +545,7 @@ afterEvaluate { } } tasks.named("createDistributable").configure { - dependsOn("signResources") + dependsOn("signResources", "includeJdk") finalizedBy("setExecutablePermissions") } -} \ No newline at end of file +} diff --git a/app/src/main/resources/about-processing.svg b/app/src/main/resources/about-processing.svg new file mode 100644 index 000000000..11abb1078 --- /dev/null +++ b/app/src/main/resources/about-processing.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/resources/processing/app/laf/FlatLaf.properties b/app/src/main/resources/processing/app/laf/FlatLaf.properties new file mode 100644 index 000000000..70e082912 --- /dev/null +++ b/app/src/main/resources/processing/app/laf/FlatLaf.properties @@ -0,0 +1,23 @@ +# The default is 8, which creates tiny nubby scroll bars +ScrollBar.width = 16 + +TitlePane.inactiveForeground = #000000 + + +# Better matched for macOS dark mode (but using everywhere) +# https://github.com/JFormDesigner/FlatLaf/issues/497 + +[dark]@background = #1e1e1e +[dark]@foreground = #e0e0e0 +[dark]@accentColor = #107aff +[dark]@accentFocusColor = #176896 + +[dark]Component.arrowType = chevron + +[dark]CheckBox.icon.style = filled +[dark]CheckBox.icon[filled].selectedBorderColor = @accentColor +[dark]CheckBox.icon[filled].selectedBackground = @accentColor +[dark]CheckBox.icon[filled].checkmarkColor = @foreground + +[dark]RadioButton.icon.style = filled +[dark]RadioButton.icon[filled].centerDiameter = 6 diff --git a/app/src/processing/app/Schema.kt b/app/src/processing/app/Schema.kt index 3a269f7d3..a02bf1da7 100644 --- a/app/src/processing/app/Schema.kt +++ b/app/src/processing/app/Schema.kt @@ -1,5 +1,9 @@ package processing.app +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch import processing.app.ui.Editor import java.io.File import java.io.FileOutputStream @@ -13,6 +17,8 @@ import java.util.* class Schema { companion object{ private var base: Base? = null + val jobs = mutableListOf() + @JvmStatic fun handleSchema(input: String, base: Base): Editor?{ this.base = base @@ -72,10 +78,9 @@ class Schema { } private fun handleSketchOptions(uri: URI, sketchFolder: File){ val options = uri.query?.split("&") - ?.map { it.split("=") } + ?.map { it.split("=", limit = 2) } ?.associate { - URLDecoder.decode(it[0], StandardCharsets.UTF_8) to - URLDecoder.decode(it[1], StandardCharsets.UTF_8) + it[0] to it[1] } ?: emptyMap() options["data"]?.let{ data -> @@ -93,8 +98,9 @@ class Schema { } } + + private val scope = CoroutineScope(Dispatchers.Default) private fun downloadFiles(uri: URI, urlList: String, targetFolder: File, extension: String = ""){ - Thread{ targetFolder.mkdirs() val base = uri.path.split("/") @@ -129,15 +135,20 @@ class Schema { URL("https://$content").path.isNotBlank() -> "https://$content" else -> "https://$base/$content" }) - url.openStream().use { input -> - target.outputStream().use { output -> - input.copyTo(output) + val download = scope.launch{ + url.openStream().use { input -> + target.outputStream().use { output -> + input.copyTo(output) + } } } + jobs.add(download) + download.invokeOnCompletion { + jobs.remove(download) + } } } - }.start() } diff --git a/app/src/processing/app/platform/LinuxPlatform.java b/app/src/processing/app/platform/LinuxPlatform.java index cda629192..3426144ca 100644 --- a/app/src/processing/app/platform/LinuxPlatform.java +++ b/app/src/processing/app/platform/LinuxPlatform.java @@ -31,15 +31,19 @@ import processing.app.Messages; import processing.app.Preferences; import processing.core.PApplet; +import javax.swing.*; + public class LinuxPlatform extends DefaultPlatform { - // Switched to use ~ as the home directory for compatibility with snap - String homeDir = "~"; + String homeDir; public void initBase(Base base) { super.initBase(base); + JFrame.setDefaultLookAndFeelDecorated(true); + System.setProperty("flatlaf.menuBarEmbedded", "true"); + // Set X11 WM_CLASS property which is used as the application // name by Gnome 3 and other window managers. // https://github.com/processing/processing/issues/2534 @@ -107,6 +111,10 @@ public class LinuxPlatform extends DefaultPlatform { configHome = null; // don't use non-existent folder } } + String snapUserCommon = System.getenv("SNAP_USER_COMMON"); + if (snapUserCommon != null && !snapUserCommon.isBlank()) { + configHome = new File(snapUserCommon); + } // If not set properly, use the default if (configHome == null) { configHome = new File(getHomeDir(), ".config"); diff --git a/app/src/processing/app/syntax/JEditTextArea.java b/app/src/processing/app/syntax/JEditTextArea.java index 566119e3d..b3c4d18a8 100644 --- a/app/src/processing/app/syntax/JEditTextArea.java +++ b/app/src/processing/app/syntax/JEditTextArea.java @@ -399,6 +399,7 @@ public class JEditTextArea extends JComponent public void updateScrollBars() { if (vertical != null && visibleLines != 0) { vertical.setValues(firstLine,visibleLines,0,getLineCount()); + vertical.setVisible(visibleLines < getLineCount()); vertical.setUnitIncrement(2); vertical.setBlockIncrement(visibleLines); } @@ -424,6 +425,7 @@ public class JEditTextArea extends JComponent // https://github.com/processing/processing/issues/319 // https://github.com/processing/processing/issues/355 //setValues(int newValue, int newExtent, int newMin, int newMax) + horizontal.setVisible(painterWidth < width); if (horizontalOffset < 0) { horizontal.setValues(-horizontalOffset, painterWidth, -leftHandGutter, width); } else { diff --git a/app/src/processing/app/ui/Editor.java b/app/src/processing/app/ui/Editor.java index f87ba4ee1..a06cbe238 100644 --- a/app/src/processing/app/ui/Editor.java +++ b/app/src/processing/app/ui/Editor.java @@ -208,7 +208,7 @@ public abstract class Editor extends JFrame implements RunnerListener { Box box = Box.createVerticalBox(); Box upper = Box.createVerticalBox(); - if(SystemInfo.isMacFullWindowContentSupported) { + if(Platform.isMacOS() && SystemInfo.isMacFullWindowContentSupported) { getRootPane().putClientProperty( "apple.awt.fullWindowContent", true ); getRootPane().putClientProperty( "apple.awt.transparentTitleBar", true ); @@ -349,6 +349,30 @@ public abstract class Editor extends JFrame implements RunnerListener { // Enable window resizing (which allows for full screen button) setResizable(true); + + { + // Move Lines Keyboard Shortcut (Alt + Arrow Up/Down) + KeyStroke moveUpKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.ALT_DOWN_MASK); + final String MOVE_UP_ACTION_KEY = "moveLinesUp"; + textarea.getInputMap(JComponent.WHEN_FOCUSED).put(moveUpKeyStroke, MOVE_UP_ACTION_KEY); + textarea.getActionMap().put(MOVE_UP_ACTION_KEY, new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + handleMoveLines(true); + } + }); + + KeyStroke moveDownKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.ALT_DOWN_MASK); + final String MOVE_DOWN_ACTION_KEY = "moveLinesDown"; + textarea.getInputMap(JComponent.WHEN_FOCUSED).put(moveDownKeyStroke, MOVE_DOWN_ACTION_KEY); + textarea.getActionMap().put(MOVE_DOWN_ACTION_KEY, new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + handleMoveLines(false); + } + }); + } + } @@ -595,12 +619,10 @@ public abstract class Editor extends JFrame implements RunnerListener { toolTipWarningColor = Theme.get("errors.selection.warning.bgcolor"); toolTipErrorColor = Theme.get("errors.selection.error.bgcolor"); - if(Platform.isWindows()) { - UIManager.put("RootPane.background", color); - UIManager.put("TitlePane.embeddedForeground", Theme.getColor("editor.fgcolor")); - getRootPane().updateUI(); - UIManager.put("RootPane.background", null); - } + UIManager.put("RootPane.background", color); + UIManager.put("TitlePane.embeddedForeground", Theme.getColor("editor.fgcolor")); + getRootPane().updateUI(); + UIManager.put("RootPane.background", null); JPopupMenu popup = modePopup.getPopupMenu(); // Cannot use instanceof because com.formdev.flatlaf.ui.FlatPopupMenuBorder @@ -791,6 +813,18 @@ public abstract class Editor extends JFrame implements RunnerListener { item.addActionListener(e -> handleIndentOutdent(false)); menu.add(item); + item = Toolkit.newJMenuItemExt("menu.edit.increase_font"); + item.addActionListener(e -> { + modifyFontSize(true); + }); + menu.add(item); + + item = Toolkit.newJMenuItemExt("menu.edit.decrease_font"); + item.addActionListener(e -> { + modifyFontSize(false); + }); + menu.add(item); + menu.addSeparator(); item = Toolkit.newJMenuItem(Language.text("menu.edit.find"), 'F'); @@ -848,6 +882,16 @@ public abstract class Editor extends JFrame implements RunnerListener { return menu; } + protected void modifyFontSize(boolean increase){ + var fontSize = Preferences.getInteger("editor.font.size"); + fontSize += increase ? 1 : -1; + fontSize = Math.max(5, Math.min(72, fontSize)); + Preferences.setInteger("editor.font.size", fontSize); + for (Editor editor : base.getEditors()) { + editor.applyPreferences(); + } + Preferences.save(); + } abstract public JMenu buildSketchMenu(); @@ -1919,6 +1963,110 @@ public abstract class Editor extends JFrame implements RunnerListener { sketch.setModified(true); } + + /** + * Moves the selected lines up or down in the text editor. + * + *

If {@code moveUp} is true, the selected lines are moved up. If false, they move down.

+ *

This method ensures proper selection updates and handles edge cases like moving + * the first or last line.

+ *

This operation is undo/redoable, allowing the user to revert the action using + * {@code Ctrl/Cmd + Z} (Undo). Redo functionality is available through the + * keybinding {@code Ctrl/Cmd + Z} on Windows/Linux and {@code Shift + Cmd + Z} on macOS.

+ * + * @param moveUp {@code true} to move the selection up, {@code false} to move it down. + */ + public void handleMoveLines(boolean moveUp) { + startCompoundEdit(); + boolean isSelected = false; + + if (textarea.isSelectionActive()) + isSelected = true; + + int caretPos = textarea.getCaretPosition(); + int currentLine = textarea.getCaretLine(); + int lineStart = textarea.getLineStartOffset(currentLine); + int column = caretPos - lineStart; + + int startLine = textarea.getSelectionStartLine(); + int stopLine = textarea.getSelectionStopLine(); + + // Adjust selection if the last line isn't fully selected + if (startLine != stopLine && + textarea.getSelectionStop() == textarea.getLineStartOffset(stopLine)) { + stopLine--; + } + + int replacedLine = moveUp ? startLine - 1 : stopLine + 1; + if (replacedLine < 0 || replacedLine >= textarea.getLineCount()) { + stopCompoundEdit(); + return; + } + + final String source = textarea.getText(); // Get full text from textarea + + int replaceStart = textarea.getLineStartOffset(replacedLine); + int replaceEnd = textarea.getLineStopOffset(replacedLine); + if (replaceEnd > source.length()) { + replaceEnd = source.length(); + } + + int selectionStart = textarea.getLineStartOffset(startLine); + int selectionEnd = textarea.getLineStopOffset(stopLine); + if (selectionEnd > source.length()) { + selectionEnd = source.length(); + } + + String replacedText = source.substring(replaceStart, replaceEnd); + String selectedText = source.substring(selectionStart, selectionEnd); + + if (replacedLine == textarea.getLineCount() - 1) { + replacedText += "\n"; + selectedText = selectedText.substring(0, Math.max(0, selectedText.length() - 1)); + } else if (stopLine == textarea.getLineCount() - 1) { + selectedText += "\n"; + replacedText = replacedText.substring(0, Math.max(0, replacedText.length() - 1)); + } + + int newSelectionStart, newSelectionEnd; + if (moveUp) { + textarea.select(selectionStart, selectionEnd); + textarea.setSelectedText(replacedText); // Use setSelectedText() + + textarea.select(replaceStart, replaceEnd); + textarea.setSelectedText(selectedText); + + newSelectionStart = textarea.getLineStartOffset(startLine - 1); + newSelectionEnd = textarea.getLineStopOffset(stopLine - 1); + } else { + textarea.select(replaceStart, replaceEnd); + textarea.setSelectedText(selectedText); + + textarea.select(selectionStart, selectionEnd); + textarea.setSelectedText(replacedText); + + newSelectionStart = textarea.getLineStartOffset(startLine + 1); + newSelectionEnd = stopLine + 1 < textarea.getLineCount() + ? Math.min(textarea.getLineStopOffset(stopLine + 1), source.length()) + : textarea.getLineStopOffset(stopLine); // Prevent out-of-bounds + } + stopCompoundEdit(); + + if (isSelected) + SwingUtilities.invokeLater(() -> { + textarea.select(newSelectionStart, newSelectionEnd-1); + }); + else if (replacedLine >= 0 && replacedLine < textarea.getLineCount()) { + int replacedLineStart = textarea.getLineStartOffset(replacedLine); + int replacedLineEnd = textarea.getLineStopOffset(replacedLine); + + // Ensure caret stays within bounds of the new line + int newCaretPos = Math.min(replacedLineStart + column, replacedLineEnd - 1); + + SwingUtilities.invokeLater(() -> textarea.setCaretPosition(newCaretPos)); + } +} + static public boolean checkParen(char[] array, int index, int stop) { while (index < stop) { @@ -2128,7 +2276,7 @@ public abstract class Editor extends JFrame implements RunnerListener { * something like "sketch_070752a - Processing 0126" */ public void updateTitle() { - setTitle(sketch.getName() + " | Processing " + Base.getVersionName()); + setTitle(sketch.getName()); if (!sketch.isUntitled()) { // Set current file for macOS so that cmd-click in title bar works. diff --git a/app/src/processing/app/ui/EditorFooter.java b/app/src/processing/app/ui/EditorFooter.java index 855e97d38..276b78364 100644 --- a/app/src/processing/app/ui/EditorFooter.java +++ b/app/src/processing/app/ui/EditorFooter.java @@ -30,6 +30,8 @@ import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.StringSelection; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.font.FontRenderContext; @@ -39,6 +41,7 @@ import java.util.List; import javax.swing.*; +import processing.app.Base; import processing.app.Mode; import processing.app.Sketch; import processing.app.contrib.ContributionManager; @@ -83,10 +86,14 @@ public class EditorFooter extends Box { Image gradient; Color bgColor; + Box tabBar; + JPanel cardPanel; CardLayout cardLayout; Controller controller; + JLabel version; + int updateCount; @@ -98,8 +105,33 @@ public class EditorFooter extends Box { cardPanel = new JPanel(cardLayout); add(cardPanel); + tabBar = new Box(BoxLayout.X_AXIS); + controller = new Controller(); - add(controller); + tabBar.add(controller); + + version = new JLabel(Base.getVersionName()); + version.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, Editor.RIGHT_GUTTER)); + version.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + if(e.getClickCount() == 5){ + Base.DEBUG = !Base.DEBUG; + } + var debugInformation = String.join("\n", + "Version: " + Base.getVersionName(), + "Revision: " + Base.getRevision(), + "OS: " + System.getProperty("os.name") + " " + System.getProperty("os.version") + " " + System.getProperty("os.arch"), + "Java: " + System.getProperty("java.version") + " " + System.getProperty("java.vendor") + ); + var stringSelection = new StringSelection(debugInformation); + var clipboard = java.awt.Toolkit.getDefaultToolkit().getSystemClipboard(); + clipboard.setContents(stringSelection, null); + } + }); + + tabBar.add(version); + + add(tabBar); updateTheme(); } @@ -175,6 +207,15 @@ public class EditorFooter extends Box { // replace colors for the "updates" indicator controller.updateTheme(); + + tabBar.setOpaque(true); + tabBar.setBackground(bgColor); + + var updatesTextColor = Theme.getColor("footer.updates.text.color"); + var withAlpha = new Color(updatesTextColor.getRed(), updatesTextColor.getGreen(), updatesTextColor.getBlue(), 128); + + version.setForeground(withAlpha); + version.setFont(font); } diff --git a/app/src/processing/app/ui/ExamplesFrame.java b/app/src/processing/app/ui/ExamplesFrame.java index a30473aec..0d8e89be9 100644 --- a/app/src/processing/app/ui/ExamplesFrame.java +++ b/app/src/processing/app/ui/ExamplesFrame.java @@ -33,6 +33,8 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.File; import java.io.IOException; +import java.util.ArrayList; +import java.util.Comparator; import java.util.Enumeration; import javax.swing.Box; @@ -57,6 +59,7 @@ import processing.app.Mode; import processing.app.Platform; import processing.app.Preferences; import processing.app.SketchReference; +import processing.app.contrib.Contribution; import processing.app.contrib.ContributionManager; import processing.app.contrib.ContributionType; import processing.app.contrib.ExamplesContribution; @@ -313,9 +316,11 @@ public class ExamplesFrame extends JFrame { } // Get examples for third party libraries - DefaultMutableTreeNode contributedLibExamples = new - DefaultMutableTreeNode(Language.text("examples.libraries")); - for (Library lib : mode.contribLibraries) { + DefaultMutableTreeNode contributedLibExamples = new DefaultMutableTreeNode(Language.text("examples.libraries")); + var sortedContribLibs = new ArrayList<>(mode.contribLibraries); + // Sort the libraries by actual name (not the name of the folder) + sortedContribLibs.sort(Comparator.comparing(Contribution::getName)); + for (Library lib : sortedContribLibs) { if (lib.hasExamples()) { DefaultMutableTreeNode libNode = new DefaultMutableTreeNode(lib.getName()); diff --git a/app/src/processing/app/ui/Start.kt b/app/src/processing/app/ui/Start.kt index 4fd9fb4c9..7de371eec 100644 --- a/app/src/processing/app/ui/Start.kt +++ b/app/src/processing/app/ui/Start.kt @@ -12,15 +12,12 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.toComposeImageBitmap -import androidx.compose.ui.unit.DpSize +import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.compose.ui.window.* import kotlinx.coroutines.delay import kotlinx.coroutines.launch import processing.app.Base -import processing.app.Platform -import javax.imageio.ImageIO /** * Show a splash screen window. A rewrite of Splash.java @@ -29,8 +26,6 @@ class Start { companion object { @JvmStatic fun main(args: Array) { - val splash = Platform.getContentFile("lib/about-processing.png") - val image = ImageIO.read(splash).toComposeImageBitmap() val duration = 200 val timeMargin = 50 @@ -44,7 +39,8 @@ class Start { resizable = false, state = rememberWindowState( position = WindowPosition(Alignment.Center), - size = DpSize(image.width.dp / 2 , image.height.dp / 2) + width = 578.dp, + height = 665.dp ) ) { var visible by remember { mutableStateOf(false) } @@ -81,7 +77,7 @@ class Start { ) ) { Image( - bitmap = image, + painter = painterResource("about-processing.svg"), contentDescription = "About", modifier = Modifier .fillMaxSize() diff --git a/app/test/processing/app/SchemaTest.kt b/app/test/processing/app/SchemaTest.kt index 009f77adf..73f7f9c9c 100644 --- a/app/test/processing/app/SchemaTest.kt +++ b/app/test/processing/app/SchemaTest.kt @@ -1,5 +1,7 @@ package processing.app +import kotlinx.coroutines.joinAll +import kotlinx.coroutines.runBlocking import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource import org.mockito.ArgumentCaptor @@ -35,6 +37,22 @@ class SchemaTest { verify(base).handleNew() } + + @Test + fun testCustomBase64Sketch(){ + Schema.handleSchema("pde://sketch/base64/LyoqCiAqIEFycmF5IE9iamVjdHMuIAogKiAKICogRGVtb25zdHJhdGVzIHRoZSBzeW50YXggZm9yIGNyZWF0aW5nIGFuIGFycmF5IG9mIGN1c3RvbSBvYmplY3RzLiAKICovCgppbnQgdW5pdCA9IDQwOwppbnQgY291bnQ7Ck1vZHVsZVtdIG1vZHM7Cgp2b2lkIHNldHVwKCkgewogIHNpemUoNjQwLCAzNjApOwogIG5vU3Ryb2tlKCk7CiAgaW50IHdpZGVDb3VudCA9IHdpZHRoIC8gdW5pdDsKICBpbnQgaGlnaENvdW50ID0gaGVpZ2h0IC8gdW5pdDsKICBjb3VudCA9IHdpZGVDb3VudCAqIGhpZ2hDb3VudDsKICBtb2RzID0gbmV3IE1vZHVsZVtjb3VudF07CgogIGludCBpbmRleCA9IDA7CiAgZm9yIChpbnQgeSA9IDA7IHkgPCBoaWdoQ291bnQ7IHkrKykgewogICAgZm9yIChpbnQgeCA9IDA7IHggPCB3aWRlQ291bnQ7IHgrKykgewogICAgICBtb2RzW2luZGV4KytdID0gbmV3IE1vZHVsZSh4KnVuaXQsIHkqdW5pdCwgdW5pdC8yLCB1bml0LzIsIHJhbmRvbSgwLjA1LCAwLjgpLCB1bml0KTsKICAgIH0KICB9Cn0KCnZvaWQgZHJhdygpIHsKICBiYWNrZ3JvdW5kKDApOwogIGZvciAoTW9kdWxlIG1vZCA6IG1vZHMpIHsKICAgIG1vZC51cGRhdGUoKTsKICAgIG1vZC5kaXNwbGF5KCk7CiAgfQp9?pde=Module:Y2xhc3MgTW9kdWxlIHsKICBpbnQgeE9mZnNldDsKICBpbnQgeU9mZnNldDsKICBmbG9hdCB4LCB5OwogIGludCB1bml0OwogIGludCB4RGlyZWN0aW9uID0gMTsKICBpbnQgeURpcmVjdGlvbiA9IDE7CiAgZmxvYXQgc3BlZWQ7IAogIAogIC8vIENvbnRydWN0b3IKICBNb2R1bGUoaW50IHhPZmZzZXRUZW1wLCBpbnQgeU9mZnNldFRlbXAsIGludCB4VGVtcCwgaW50IHlUZW1wLCBmbG9hdCBzcGVlZFRlbXAsIGludCB0ZW1wVW5pdCkgewogICAgeE9mZnNldCA9IHhPZmZzZXRUZW1wOwogICAgeU9mZnNldCA9IHlPZmZzZXRUZW1wOwogICAgeCA9IHhUZW1wOwogICAgeSA9IHlUZW1wOwogICAgc3BlZWQgPSBzcGVlZFRlbXA7CiAgICB1bml0ID0gdGVtcFVuaXQ7CiAgfQogIAogIC8vIEN1c3RvbSBtZXRob2QgZm9yIHVwZGF0aW5nIHRoZSB2YXJpYWJsZXMKICB2b2lkIHVwZGF0ZSgpIHsKICAgIHggPSB4ICsgKHNwZWVkICogeERpcmVjdGlvbik7CiAgICBpZiAoeCA+PSB1bml0IHx8IHggPD0gMCkgewogICAgICB4RGlyZWN0aW9uICo9IC0xOwogICAgICB4ID0geCArICgxICogeERpcmVjdGlvbik7CiAgICAgIHkgPSB5ICsgKDEgKiB5RGlyZWN0aW9uKTsKICAgIH0KICAgIGlmICh5ID49IHVuaXQgfHwgeSA8PSAwKSB7CiAgICAgIHlEaXJlY3Rpb24gKj0gLTE7CiAgICAgIHkgPSB5ICsgKDEgKiB5RGlyZWN0aW9uKTsKICAgIH0KICB9CiAgCiAgLy8gQ3VzdG9tIG1ldGhvZCBmb3IgZHJhd2luZyB0aGUgb2JqZWN0CiAgdm9pZCBkaXNwbGF5KCkgewogICAgZmlsbCgyNTUpOwogICAgZWxsaXBzZSh4T2Zmc2V0ICsgeCwgeU9mZnNldCArIHksIDYsIDYpOwogIH0KfQAA", base) + val captor = ArgumentCaptor.forClass(String::class.java) + + verify(base).handleOpenUntitled(captor.capture()) + + val file = File(captor.value) + assert(file.exists()) + + val extra = file.parentFile.resolve("Module.pde") + assert(extra.exists()) + file.parentFile.deleteRecursively() + } + @OptIn(ExperimentalEncodingApi::class) @Test fun testBase64SketchAndExtraFiles() { @@ -49,8 +67,8 @@ class SchemaTest { val base64 = Base64.encode(sketch.toByteArray()) Schema.handleSchema("pde://sketch/base64/$base64?pde=AnotherFile:$base64", base) - val captor = ArgumentCaptor.forClass(String::class.java) + val captor = ArgumentCaptor.forClass(String::class.java) verify(base).handleOpenUntitled(captor.capture()) val file = File(captor.value) @@ -66,6 +84,7 @@ class SchemaTest { @Test fun testURLSketch() { Schema.handleSchema("pde://sketch/url/github.com/processing/processing-examples/raw/refs/heads/main/Basics/Arrays/Array/Array.pde", base) + waitForSchemeJobsToComplete() val captor = ArgumentCaptor.forClass(String::class.java) verify(base).handleOpenUntitled(captor.capture()) @@ -88,6 +107,7 @@ class SchemaTest { ]) 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) + waitForSchemeJobsToComplete() val captor = ArgumentCaptor.forClass(String::class.java) verify(base).handleOpenUntitled(captor.capture()) @@ -110,4 +130,10 @@ class SchemaTest { Preferences.save() } } + + fun waitForSchemeJobsToComplete(){ + runBlocking { + joinAll(*Schema.jobs.toTypedArray()) + } + } } \ No newline at end of file diff --git a/build/linux/processing.png b/build/linux/processing.png index ef31c9391..8eb3f0294 100644 Binary files a/build/linux/processing.png and b/build/linux/processing.png differ diff --git a/build/linux/processing.svg b/build/linux/processing.svg new file mode 100644 index 000000000..9b5ccd52b --- /dev/null +++ b/build/linux/processing.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/build/macos/processing.icns b/build/macos/processing.icns index cb42dad17..e05dbaa5a 100644 Binary files a/build/macos/processing.icns and b/build/macos/processing.icns differ diff --git a/build/shared/lib/about-processing.png b/build/shared/lib/about-processing.png deleted file mode 100644 index 056354154..000000000 Binary files a/build/shared/lib/about-processing.png and /dev/null differ diff --git a/build/shared/lib/languages/PDE.properties b/build/shared/lib/languages/PDE.properties index 59cd63c74..3ea6d7652 100644 --- a/build/shared/lib/languages/PDE.properties +++ b/build/shared/lib/languages/PDE.properties @@ -59,6 +59,14 @@ menu.edit.decrease_indent = ← Decrease Indent menu.edit.decrease_indent.keystroke.macos = meta pressed OPEN_BRACKET menu.edit.decrease_indent.keystroke.windows = ctrl pressed OPEN_BRACKET menu.edit.decrease_indent.keystroke.linux = ctrl pressed OPEN_BRACKET +menu.edit.increase_font = Increase Font Size +menu.edit.increase_font.keystroke.macos = meta pressed EQUALS +menu.edit.increase_font.keystroke.windows = ctrl pressed EQUALS +menu.edit.increase_font.keystroke.linux = ctrl pressed EQUALS +menu.edit.decrease_font = Decrease Font Size +menu.edit.decrease_font.keystroke.macos = meta pressed MINUS +menu.edit.decrease_font.keystroke.windows = ctrl pressed MINUS +menu.edit.decrease_font.keystroke.linux = ctrl pressed MINUS menu.edit.find = Find... menu.edit.find_next = Find Next menu.edit.find_previous = Find Previous @@ -631,6 +639,13 @@ beta.title = Welcome to the Processing Beta beta.message = Thank you for trying out the new version of Processing. We're very grateful!\n\nPlease report any bugs on the forums. beta.button = Got it! +# --------------------------------------- +# Beta +beta.window.title = Welcome to Beta +beta.title = Thanks for testing this Processing Beta! +beta.message = This preview release lets us gather feedback and fix issues before the final version. **Some features may not work as expected.** If you encounter problems, [please post on the forum](https://discourse.processing.org) or [open a GitHub issue](https://github.com/processing/processing4/issues). +beta.button = Ok + # --------------------------------------- # Color Chooser diff --git a/build/shared/lib/languages/PDE_nl.properties b/build/shared/lib/languages/PDE_nl.properties index f1ac08b5e..e7f11b0a1 100644 --- a/build/shared/lib/languages/PDE_nl.properties +++ b/build/shared/lib/languages/PDE_nl.properties @@ -318,10 +318,9 @@ update_check.updates_available.contributions = Er zijn updates beschikbaar voor # --------------------------------------- # 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! - +beta.title = Dankuwel voor het testen van deze Processing Beta! +beta.message = Deze preview release laat ons feedback verzamelen en problemen oplossen. **Sommige functies werken mogelijk niet zoals verwacht.** Als u problemen ondervindt, [post dan op het forum](https://discourse.processing.org) of [open een GitHub issue](https://github.com/processing/processing4/issues). +beta.button = Ok # --------------------------------------- # Color Chooser diff --git a/build/shared/lib/theme/Alloys/agpalilik.txt b/build/shared/lib/theme/Alloys/agpalilik.txt index fdea1e465..157afaa04 100644 --- a/build/shared/lib/theme/Alloys/agpalilik.txt +++ b/build/shared/lib/theme/Alloys/agpalilik.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #0066C5 ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #C0FFFF # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #374E81 diff --git a/build/shared/lib/theme/Alloys/armanty.txt b/build/shared/lib/theme/Alloys/armanty.txt index 7b6269bd3..7a2af8122 100644 --- a/build/shared/lib/theme/Alloys/armanty.txt +++ b/build/shared/lib/theme/Alloys/armanty.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #834548 ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #FFEBEC # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #0087A9 diff --git a/build/shared/lib/theme/Alloys/bacubirito.txt b/build/shared/lib/theme/Alloys/bacubirito.txt index c344ea624..d215ef339 100644 --- a/build/shared/lib/theme/Alloys/bacubirito.txt +++ b/build/shared/lib/theme/Alloys/bacubirito.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #49D0A7 ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #1A0300 # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #9C9824 diff --git a/build/shared/lib/theme/Alloys/bondoc.txt b/build/shared/lib/theme/Alloys/bondoc.txt index 93d78a7d9..7f9106cec 100644 --- a/build/shared/lib/theme/Alloys/bondoc.txt +++ b/build/shared/lib/theme/Alloys/bondoc.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #431D29 ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #FFF2FF # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #544587 diff --git a/build/shared/lib/theme/Alloys/brahin.txt b/build/shared/lib/theme/Alloys/brahin.txt index 278fa6ed3..e371cbfb1 100644 --- a/build/shared/lib/theme/Alloys/brahin.txt +++ b/build/shared/lib/theme/Alloys/brahin.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #47502C ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #FBFFD7 # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #3D4B37 diff --git a/build/shared/lib/theme/Alloys/esquel.txt b/build/shared/lib/theme/Alloys/esquel.txt index 31641367c..7db8de73c 100644 --- a/build/shared/lib/theme/Alloys/esquel.txt +++ b/build/shared/lib/theme/Alloys/esquel.txt @@ -161,16 +161,16 @@ editor.scrollbar.color = #978FAC ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #540000 # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 90 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 60 # bgcolor for the current (highlighted) line -editor.gutter.highlight.color = #CA0022 +editor.gutter.highlight.color = #F7637B # squiggly line underneath errors in the editor editor.error.underline.color = #000000 diff --git a/build/shared/lib/theme/Alloys/gancedo.txt b/build/shared/lib/theme/Alloys/gancedo.txt index cb640b15d..4a0da3de4 100644 --- a/build/shared/lib/theme/Alloys/gancedo.txt +++ b/build/shared/lib/theme/Alloys/gancedo.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #9D0038 ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #FFE8FF # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #8F4965 diff --git a/build/shared/lib/theme/Alloys/hoba.txt b/build/shared/lib/theme/Alloys/hoba.txt index 795a01427..948b0a30e 100644 --- a/build/shared/lib/theme/Alloys/hoba.txt +++ b/build/shared/lib/theme/Alloys/hoba.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #F07D44 ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #3E0000 # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #D9964A diff --git a/build/shared/lib/theme/Alloys/imilac.txt b/build/shared/lib/theme/Alloys/imilac.txt index 9cc03ae24..a0aed27ab 100644 --- a/build/shared/lib/theme/Alloys/imilac.txt +++ b/build/shared/lib/theme/Alloys/imilac.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #E9E9E9 ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #00003B # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #809AE3 diff --git a/build/shared/lib/theme/Alloys/jepara.txt b/build/shared/lib/theme/Alloys/jepara.txt index 988738056..0fb527553 100644 --- a/build/shared/lib/theme/Alloys/jepara.txt +++ b/build/shared/lib/theme/Alloys/jepara.txt @@ -161,16 +161,16 @@ editor.scrollbar.color = #FF6E38 ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #000049 # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line -editor.gutter.highlight.color = #4F4CA9 +editor.gutter.highlight.color = #9090ED # squiggly line underneath errors in the editor editor.error.underline.color = #000000 diff --git a/build/shared/lib/theme/Alloys/mbozi.txt b/build/shared/lib/theme/Alloys/mbozi.txt index 2c7bd137d..3182db46a 100644 --- a/build/shared/lib/theme/Alloys/mbozi.txt +++ b/build/shared/lib/theme/Alloys/mbozi.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #FF8F2F ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #470000 # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 90 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 50 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #CF226A diff --git a/build/shared/lib/theme/Alloys/morito.txt b/build/shared/lib/theme/Alloys/morito.txt index cfd7428af..9ba420688 100644 --- a/build/shared/lib/theme/Alloys/morito.txt +++ b/build/shared/lib/theme/Alloys/morito.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #697982 ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #EFFFFF # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #7C5295 diff --git a/build/shared/lib/theme/Alloys/omolon.txt b/build/shared/lib/theme/Alloys/omolon.txt index 391f2f6e2..a545c5a82 100644 --- a/build/shared/lib/theme/Alloys/omolon.txt +++ b/build/shared/lib/theme/Alloys/omolon.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #4E535A ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #FAFFFF # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #535558 diff --git a/build/shared/lib/theme/Alloys/seymchan.txt b/build/shared/lib/theme/Alloys/seymchan.txt index 8624cb5ac..3f2921107 100644 --- a/build/shared/lib/theme/Alloys/seymchan.txt +++ b/build/shared/lib/theme/Alloys/seymchan.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #00593B ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #B7FFEA # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #347A00 diff --git a/build/shared/lib/theme/Alloys/tagish.txt b/build/shared/lib/theme/Alloys/tagish.txt index 780c39f08..33797a665 100644 --- a/build/shared/lib/theme/Alloys/tagish.txt +++ b/build/shared/lib/theme/Alloys/tagish.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #A55134 ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #FFFDFB # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #888280 diff --git a/build/shared/lib/theme/Alloys/youxi.txt b/build/shared/lib/theme/Alloys/youxi.txt index b20264d2f..10ee680a0 100644 --- a/build/shared/lib/theme/Alloys/youxi.txt +++ b/build/shared/lib/theme/Alloys/youxi.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #008A50 ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 -editor.gutter.text.color = #71FFD5 +editor.gutter.text.font = processing.mono,bold,16 +editor.gutter.text.color = #CBFFEF # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 90 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 60 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #8F9090 diff --git a/build/shared/lib/theme/Minerals/antimony.txt b/build/shared/lib/theme/Minerals/antimony.txt index fdd2f70e8..67f9fb351 100644 --- a/build/shared/lib/theme/Minerals/antimony.txt +++ b/build/shared/lib/theme/Minerals/antimony.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #092D38 ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #E1FFFF # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #30505D diff --git a/build/shared/lib/theme/Minerals/bauxite.txt b/build/shared/lib/theme/Minerals/bauxite.txt index ed0e41ab3..102be6158 100644 --- a/build/shared/lib/theme/Minerals/bauxite.txt +++ b/build/shared/lib/theme/Minerals/bauxite.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #4A4E59 ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #FAFEFF # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #686C78 diff --git a/build/shared/lib/theme/Minerals/beryl.txt b/build/shared/lib/theme/Minerals/beryl.txt index 8e00681a4..d332ee148 100644 --- a/build/shared/lib/theme/Minerals/beryl.txt +++ b/build/shared/lib/theme/Minerals/beryl.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #00926F ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #001E00 # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #007757 diff --git a/build/shared/lib/theme/Minerals/calcite.txt b/build/shared/lib/theme/Minerals/calcite.txt index 3a7cd90c1..a48957eb7 100644 --- a/build/shared/lib/theme/Minerals/calcite.txt +++ b/build/shared/lib/theme/Minerals/calcite.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #B9BDC4 ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #000009 # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #92969D diff --git a/build/shared/lib/theme/Minerals/feldspar.txt b/build/shared/lib/theme/Minerals/feldspar.txt index 3ea2392e2..11d2ee52c 100644 --- a/build/shared/lib/theme/Minerals/feldspar.txt +++ b/build/shared/lib/theme/Minerals/feldspar.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #BD8A68 ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #270000 # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #9C6D4C diff --git a/build/shared/lib/theme/Minerals/fluorite.txt b/build/shared/lib/theme/Minerals/fluorite.txt index d169c92c1..e354d2262 100644 --- a/build/shared/lib/theme/Minerals/fluorite.txt +++ b/build/shared/lib/theme/Minerals/fluorite.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #402563 ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #FFEFFF # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #654788 diff --git a/build/shared/lib/theme/Minerals/gabbro.txt b/build/shared/lib/theme/Minerals/gabbro.txt index 2961de644..1c84a5719 100644 --- a/build/shared/lib/theme/Minerals/gabbro.txt +++ b/build/shared/lib/theme/Minerals/gabbro.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #7A896D ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #000700 # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #606F54 diff --git a/build/shared/lib/theme/Minerals/galena.txt b/build/shared/lib/theme/Minerals/galena.txt index 5bf35c161..94d91edbd 100644 --- a/build/shared/lib/theme/Minerals/galena.txt +++ b/build/shared/lib/theme/Minerals/galena.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #6C7076 ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #000009 # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #565A60 diff --git a/build/shared/lib/theme/Minerals/garnet.txt b/build/shared/lib/theme/Minerals/garnet.txt index 3fcf0d7d1..c7501bcff 100644 --- a/build/shared/lib/theme/Minerals/garnet.txt +++ b/build/shared/lib/theme/Minerals/garnet.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #973542 ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 -editor.gutter.text.color = #FFDFE5 +editor.gutter.text.font = processing.mono,bold,16 +editor.gutter.text.color = #FFEFF2 # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 90 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #B9545E diff --git a/build/shared/lib/theme/Minerals/jasper.txt b/build/shared/lib/theme/Minerals/jasper.txt index f682815e8..a32bcd850 100644 --- a/build/shared/lib/theme/Minerals/jasper.txt +++ b/build/shared/lib/theme/Minerals/jasper.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #CC383C ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 -editor.gutter.text.color = #460000 +editor.gutter.text.font = processing.mono,bold,16 +editor.gutter.text.color = #fbb5b5 # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 100 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 70 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #B11928 diff --git a/build/shared/lib/theme/Minerals/kyanite.txt b/build/shared/lib/theme/Minerals/kyanite.txt index 43c778272..6cd8c76ac 100644 --- a/build/shared/lib/theme/Minerals/kyanite.txt +++ b/build/shared/lib/theme/Minerals/kyanite.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #5E93BF ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 -editor.gutter.text.color = #00072B +editor.gutter.text.font = processing.mono,bold,16 +editor.gutter.text.color = #000833 # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #3E76A0 diff --git a/build/shared/lib/theme/Minerals/malachite.txt b/build/shared/lib/theme/Minerals/malachite.txt index 41f0e1413..3a4b6182d 100644 --- a/build/shared/lib/theme/Minerals/malachite.txt +++ b/build/shared/lib/theme/Minerals/malachite.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #313E38 ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #F2FFFA # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #515F58 diff --git a/build/shared/lib/theme/Minerals/olivine.txt b/build/shared/lib/theme/Minerals/olivine.txt index afd08c8e2..c5d711cba 100644 --- a/build/shared/lib/theme/Minerals/olivine.txt +++ b/build/shared/lib/theme/Minerals/olivine.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #869F36 ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #000D00 # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #678015 diff --git a/build/shared/lib/theme/Minerals/orpiment.txt b/build/shared/lib/theme/Minerals/orpiment.txt index d93f8d428..a7f73cb78 100644 --- a/build/shared/lib/theme/Minerals/orpiment.txt +++ b/build/shared/lib/theme/Minerals/orpiment.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #EFBA4E ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #2D0000 # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #C29225 diff --git a/build/shared/lib/theme/Minerals/pyrite.txt b/build/shared/lib/theme/Minerals/pyrite.txt index efc9d3fc8..43bc2079e 100644 --- a/build/shared/lib/theme/Minerals/pyrite.txt +++ b/build/shared/lib/theme/Minerals/pyrite.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #06545D ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #C9FFFF # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 40 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #32747C diff --git a/build/shared/lib/theme/Minerals/serandite.txt b/build/shared/lib/theme/Minerals/serandite.txt index 9abccd32c..6f692fa6a 100644 --- a/build/shared/lib/theme/Minerals/serandite.txt +++ b/build/shared/lib/theme/Minerals/serandite.txt @@ -161,13 +161,13 @@ editor.scrollbar.color = #DE5C25 ## PdeTextAreaPainter - extras added to the editor ## -editor.gutter.text.font = processing.mono,plain,12 +editor.gutter.text.font = processing.mono,bold,16 editor.gutter.text.color = #440000 # transparency (0..100) for line numbers in gutter -editor.gutter.text.active.alpha = 70 +editor.gutter.text.active.alpha = 80 # transparency for lines not currently in use -editor.gutter.text.inactive.alpha = 30 +editor.gutter.text.inactive.alpha = 50 # bgcolor for the current (highlighted) line editor.gutter.highlight.color = #BC4007 diff --git a/build/windows/processing.ico b/build/windows/processing.ico index 2db3db4aa..53896b103 100644 Binary files a/build/windows/processing.ico and b/build/windows/processing.ico differ diff --git a/build/windows/windows.png b/build/windows/windows.png new file mode 100644 index 000000000..1491d1430 Binary files /dev/null and b/build/windows/windows.png differ diff --git a/build/windows/windows.svg b/build/windows/windows.svg new file mode 100644 index 000000000..796997b91 --- /dev/null +++ b/build/windows/windows.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/src/processing/core/PApplet.java b/core/src/processing/core/PApplet.java index 24c0d5d39..9f3486a10 100644 --- a/core/src/processing/core/PApplet.java +++ b/core/src/processing/core/PApplet.java @@ -2635,8 +2635,11 @@ public class PApplet implements PConstants { keyPressed = true; keyPressed(keyEvent); } - case KeyEvent.RELEASE -> { - pressedKeys.remove(((long) keyCode << Character.SIZE) | key); + case KeyEvent.RELEASE -> { + pressedKeys.removeIf(hash -> { + int pressedKeyCode = (int)(hash >> Character.SIZE); + return pressedKeyCode == keyCode; + }); keyPressed = !pressedKeys.isEmpty(); keyReleased(keyEvent); } @@ -2842,6 +2845,7 @@ public class PApplet implements PConstants { public void focusLost() { // TODO: if user overrides this without calling super it's not gonna work pressedKeys.clear(); + keyPressed = false; } diff --git a/core/src/processing/core/PImage.java b/core/src/processing/core/PImage.java index d2ac4e3b9..666061ee9 100644 --- a/core/src/processing/core/PImage.java +++ b/core/src/processing/core/PImage.java @@ -1057,6 +1057,9 @@ public class PImage implements PConstants, Cloneable { * [toxi 050728] */ protected void buildBlurKernel(float r) { + float maxRadius = Math.min(width, height) / 2.0f; + float maxR = maxRadius / 3.5f; + r = Math.min(r, maxR); int radius = (int) (r * 3.5f); if (radius < 1) radius = 1; if (radius > 248) radius = 248; @@ -1083,6 +1086,9 @@ public class PImage implements PConstants, Cloneable { } } + private int safeDivide(int numerator, int denominator) { + return denominator == 0 ? numerator : numerator / denominator; + } protected void blurAlpha(float r) { int sum, cb; @@ -1115,7 +1121,7 @@ public class PImage implements PConstants, Cloneable { read++; } ri = yi + x; - b2[ri] = cb / sum; + b2[ri] = safeDivide(cb, sum); } yi += pixelWidth; } @@ -1146,7 +1152,7 @@ public class PImage implements PConstants, Cloneable { ri++; read += pixelWidth; } - pixels[x+yi] = (cb/sum); + pixels[x+yi] = safeDivide(cb, sum); } yi += pixelWidth; ymi += pixelWidth; @@ -1191,9 +1197,9 @@ public class PImage implements PConstants, Cloneable { read++; } ri = yi + x; - r2[ri] = cr / sum; - g2[ri] = cg / sum; - b2[ri] = cb / sum; + r2[ri] = safeDivide(cr, sum); + g2[ri] = safeDivide(cg, sum); + b2[ri] = safeDivide(cb, sum); } yi += pixelWidth; } @@ -1228,7 +1234,7 @@ public class PImage implements PConstants, Cloneable { ri++; read += pixelWidth; } - pixels[x+yi] = 0xff000000 | (cr/sum)<<16 | (cg/sum)<<8 | (cb/sum); + pixels[x+yi] = 0xff000000 | (safeDivide(cr, sum))<<16 | (safeDivide(cg, sum))<<8 | (safeDivide(cb, sum)); } yi += pixelWidth; ymi += pixelWidth; @@ -1276,10 +1282,10 @@ public class PImage implements PConstants, Cloneable { read++; } ri = yi + x; - a2[ri] = ca / sum; - r2[ri] = cr / sum; - g2[ri] = cg / sum; - b2[ri] = cb / sum; + a2[ri] = safeDivide(ca, sum); + r2[ri] = safeDivide(cr, sum); + g2[ri] = safeDivide(cg, sum); + b2[ri] = safeDivide(cb, sum); } yi += pixelWidth; } @@ -1315,7 +1321,7 @@ public class PImage implements PConstants, Cloneable { ri++; read += pixelWidth; } - pixels[x+yi] = (ca/sum)<<24 | (cr/sum)<<16 | (cg/sum)<<8 | (cb/sum); + pixels[x+yi] = (safeDivide(ca, sum))<<24 | (safeDivide(cr, sum))<<16 | (safeDivide(cg, sum))<<8 | (safeDivide(cb, sum)); } yi += pixelWidth; ymi += pixelWidth; diff --git a/core/src/processing/opengl/PGraphicsOpenGL.java b/core/src/processing/opengl/PGraphicsOpenGL.java index 88164f43e..2d6dca991 100644 --- a/core/src/processing/opengl/PGraphicsOpenGL.java +++ b/core/src/processing/opengl/PGraphicsOpenGL.java @@ -4481,7 +4481,7 @@ public class PGraphicsOpenGL extends PGraphics { // The minus sign is needed to invert the Y axis. projection.set(x, 0, 0, tx, - 0, -y, 0, ty, + 0, -y, 0, -ty, 0, 0, z, tz, 0, 0, 0, 1); diff --git a/core/test/processing/core/PAppletKeyEventTest.java b/core/test/processing/core/PAppletKeyEventTest.java new file mode 100644 index 000000000..19770ebc3 --- /dev/null +++ b/core/test/processing/core/PAppletKeyEventTest.java @@ -0,0 +1,140 @@ +package processing.core; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import processing.event.KeyEvent; + +public class PAppletKeyEventTest { + + private static final int SHIFT_MASK = 1; + private static final int CTRL_MASK = 2; + private static final int ALT_MASK = 4; + + private PApplet applet; + + @Before + public void setup() { + applet = new PApplet(); + } + + @Test + public void testSingleKeyPressAndRelease() { + KeyEvent pressEvent = new KeyEvent(null, 0L, KeyEvent.PRESS, 0, 'a', 65, false); + applet.handleKeyEvent(pressEvent); + Assert.assertEquals(1, applet.pressedKeys.size()); + + KeyEvent releaseEvent = new KeyEvent(null, 0L, KeyEvent.RELEASE, 0, 'a', 65, false); + applet.handleKeyEvent(releaseEvent); + Assert.assertEquals(0, applet.pressedKeys.size()); + Assert.assertFalse(applet.keyPressed); + } + + @Test + public void testShiftAndLetterSequence() { + KeyEvent pressA = new KeyEvent(null, 0L, KeyEvent.PRESS, 0, 'a', 65, false); + applet.handleKeyEvent(pressA); + + KeyEvent pressShift = new KeyEvent(null, 0L, KeyEvent.PRESS, SHIFT_MASK, 'A', 16, false); + applet.handleKeyEvent(pressShift); + + KeyEvent releaseA = new KeyEvent(null, 0L, KeyEvent.RELEASE, SHIFT_MASK, 'A', 65, false); + applet.handleKeyEvent(releaseA); + + KeyEvent releaseShift = new KeyEvent(null, 0L, KeyEvent.RELEASE, 0, 'A', 16, false); + applet.handleKeyEvent(releaseShift); + + Assert.assertFalse("keyPressed should be false after all keys released", applet.keyPressed); + Assert.assertTrue("pressedKeys should be empty", applet.pressedKeys.isEmpty()); + } + + @Test + public void testControlAndLetterSequence() { + KeyEvent pressCtrl = new KeyEvent(null, 0L, KeyEvent.PRESS, CTRL_MASK, '\0', 17, false); + applet.handleKeyEvent(pressCtrl); + + KeyEvent pressC = new KeyEvent(null, 0L, KeyEvent.PRESS, CTRL_MASK, (char)3, 67, false); + applet.handleKeyEvent(pressC); + + KeyEvent releaseC = new KeyEvent(null, 0L, KeyEvent.RELEASE, CTRL_MASK, 'c', 67, false); + applet.handleKeyEvent(releaseC); + + KeyEvent releaseCtrl = new KeyEvent(null, 0L, KeyEvent.RELEASE, 0, '\0', 17, false); + applet.handleKeyEvent(releaseCtrl); + + Assert.assertFalse("keyPressed should be false after all keys released", applet.keyPressed); + Assert.assertTrue("pressedKeys should be empty", applet.pressedKeys.isEmpty()); + } + + @Test + public void testAltAndLetterSequence() { + KeyEvent pressV = new KeyEvent(null, 0L, KeyEvent.PRESS, 0, 'v', 86, false); + applet.handleKeyEvent(pressV); + + KeyEvent pressAlt = new KeyEvent(null, 0L, KeyEvent.PRESS, ALT_MASK, 'v', 18, false); + applet.handleKeyEvent(pressAlt); + + KeyEvent releaseV = new KeyEvent(null, 0L, KeyEvent.RELEASE, ALT_MASK, 'v', 86, false); + applet.handleKeyEvent(releaseV); + + KeyEvent releaseAlt = new KeyEvent(null, 0L, KeyEvent.RELEASE, 0, 'v', 18, false); + applet.handleKeyEvent(releaseAlt); + + Assert.assertFalse("keyPressed should be false after all keys released", applet.keyPressed); + Assert.assertTrue("pressedKeys should be empty", applet.pressedKeys.isEmpty()); + } + + @Test + public void testKeyRepeat() { + applet.keyRepeatEnabled = false; + + KeyEvent pressR = new KeyEvent(null, 0L, KeyEvent.PRESS, 0, 'r', 82, false); + applet.handleKeyEvent(pressR); + + KeyEvent repeatR = new KeyEvent(null, 0L, KeyEvent.PRESS, 0, 'r', 82, true); + applet.handleKeyEvent(repeatR); + + Assert.assertTrue("keyPressed should be true after key press", applet.keyPressed); + Assert.assertEquals("pressedKeys should have 1 entry", 1, applet.pressedKeys.size()); + + KeyEvent releaseR = new KeyEvent(null, 0L, KeyEvent.RELEASE, 0, 'r', 82, false); + applet.handleKeyEvent(releaseR); + + Assert.assertFalse("keyPressed should be false after key release", applet.keyPressed); + Assert.assertEquals("pressedKeys should be empty", true, applet.pressedKeys.isEmpty()); + } + + @Test + public void testKeyRepeatEnabled() { + applet.keyRepeatEnabled = true; + + KeyEvent pressT = new KeyEvent(null, 0L, KeyEvent.PRESS, 0, 't', 84, false); + applet.handleKeyEvent(pressT); + + KeyEvent repeatT = new KeyEvent(null, 0L, KeyEvent.PRESS, 0, 't', 84, true); + applet.handleKeyEvent(repeatT); + + Assert.assertTrue("keyPressed should be true with key repeat enabled", applet.keyPressed); + Assert.assertEquals("pressedKeys should have 1 entry", 1, applet.pressedKeys.size()); + + KeyEvent releaseT = new KeyEvent(null, 0L, KeyEvent.RELEASE, 0, 't', 84, false); + applet.handleKeyEvent(releaseT); + + Assert.assertFalse("keyPressed should be false after key release", applet.keyPressed); + Assert.assertEquals("pressedKeys should be empty", true, applet.pressedKeys.isEmpty()); + } + + @Test + public void testKeyFocusLost() { + KeyEvent pressF = new KeyEvent(null, 0L, KeyEvent.PRESS, 0, 'f', 70, false); + applet.handleKeyEvent(pressF); + + Assert.assertTrue("keyPressed should be true after key press", applet.keyPressed); + Assert.assertEquals("pressedKeys should have 1 entry", 1, applet.pressedKeys.size()); + + applet.focusLost(); + + Assert.assertFalse("keyPressed should be false after focus lost", applet.keyPressed); + Assert.assertEquals("pressedKeys should be empty after focus lost", true, applet.pressedKeys.isEmpty()); + } +}