Skip to content

Commit cc28164

Browse files
authored
Merge pull request #26 from openpatch/v5-api-slim
V5 api slim
2 parents 56a0585 + f307bae commit cc28164

520 files changed

Lines changed: 9378 additions & 2074 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.changeset/v5-release.md‎

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
---
2+
type: major
3+
---
4+
5+
Scratch for Java 5 is about what a beginner meets first.
6+
7+
Version 4 grew by addition. Every capability arrived as more public methods on
8+
`Sprite` and `Stage`, more constructors, more public fields to assign before
9+
starting, and more packages to import. A learner opening `Sprite` in BlueJ met
10+
289 public methods, most of which had no Scratch block behind them, and a
11+
mistyped costume name closed BlueJ itself.
12+
13+
v5 removes what nobody needed, moves what belonged elsewhere, and fills the
14+
gaps that were actually stopping projects. `Sprite` and `Stage` are down from
15+
**289 public methods to 193**, and everything a first course needs is behind one
16+
import.
17+
18+
Nothing here is a rename for its own sake: every removal is either a second way
19+
to do something that already had a first way, or something with no Scratch
20+
equivalent and no demonstrated use.
21+
22+
## Migrating from v4
23+
24+
### One import
25+
26+
Eight packages moved into `org.openpatch.scratch`, so `Pen`, `Text`, `Timer`,
27+
`AnimatedSprite`, `Vector2`, `Random`, `Color`, `Hitbox` and the shapes all come
28+
from one line:
29+
30+
```java
31+
import org.openpatch.scratch.*;
32+
```
33+
34+
Delete any `import org.openpatch.scratch.extensions.animation.*;` and its
35+
siblings for `color`, `hitbox`, `math`, `pen`, `shape`, `text` and `timer`.
36+
37+
Five packages are still opt-in, for things a first course does not meet:
38+
`camera`, `fs`, `recorder`, `shader` and `tiled`, joined by the new `pixels` and
39+
`sorting`.
40+
41+
### Handlers are overridden, not registered
42+
43+
The whole `setWhenX` family is gone — 16 setters across `Sprite`, `Stage` and
44+
`Window`, and the 16 nested `When*Handler` interfaces that went with them. Every
45+
event already had an overridable method, which is the form that matches Scratch:
46+
47+
```java
48+
// v4
49+
this.setWhenKeyPressed((keyCode) -> { ... });
50+
51+
// v5
52+
public void whenKeyPressed(KeyCode keyCode) { ... }
53+
```
54+
55+
`setRun` goes the same way — override `run()`. And `setOnEdgeBounce(true)`
56+
becomes a call inside `run()`, the way the block works:
57+
58+
```java
59+
this.ifOnEdgeBounce();
60+
```
61+
62+
### Settings are methods, chosen before the window opens
63+
64+
Public fields you assigned before starting did nothing at all if you assigned
65+
them afterwards — no error, no warning. They are methods now, and they say so
66+
when used too late:
67+
68+
| v4 | v5 |
69+
|---|---|
70+
| `new Stage(true, 800, 600)` | `Window.useFullScreen();` then `new Stage(800, 600)` |
71+
| `Window.TEXTURE_SAMPLING_MODE = 2;` | `Window.useTextureSampling(TextureSampling.POINT);` |
72+
| `Text.DEFAULT_FONT = "f.ttf";` | `Text.useFont("f.ttf");` |
73+
| `Text.DEFAULT_FONT_SIZE = 11;` | `Text.useFont("f.ttf", 11);` |
74+
| `Text.FONT_SIZES = new int[]{14, 20};` | `Text.useFontSizes(14, 20);` |
75+
| `Text.SMOOTHING = false;` | `Text.useSmoothing(false);` |
76+
| `Stage.setTextureSampling(...)` | `Window.useTextureSampling(...)` |
77+
78+
`TextureSampling` and `TextAlign` are enums rather than loose ints, so a wrong
79+
value can no longer be written. Code that already said `TextAlign.LEFT` compiles
80+
unchanged.
81+
82+
### Methods that moved to a class of their own
83+
84+
Nine clock values and nine shader methods existed on **both** `Sprite` and
85+
`Stage` — 36 methods for 18 ideas.
86+
87+
| v4 | v5 |
88+
|---|---|
89+
| `this.getCurrentYear()` | `Clock.getYear()` |
90+
| `this.addShader(...)`, `this.switchShader(...)` | `this.getShaders().add(...)`, `.switchTo(...)` |
91+
| `this.setSorter(...)`, `this.enableYSort()` | `this.getSorting().byY()` |
92+
| `this.getPixels()` → `int[]` | `this.getPixels().main()` → `int[]` |
93+
| `this.stampToUI()` | `this.stamp(Layer.UI)` |
94+
| `this.getWindow()` | `Window.getInstance()` |
95+
| `stage.countSpritesOf(C.class)` | `stage.count(C.class)` |
96+
| `stage.findPensOf(C.class)` | `stage.find(C.class)` |
97+
98+
`Layer` is a new enum with `BACKGROUND`, `FOREGROUND` and `UI`, so choosing a
99+
layer is something you say rather than a method name you pick.
100+
101+
### Overloads that made the wrong thing easy
102+
103+
- `broadcast(Object)` and `whenIReceive(Object)` are gone. Broadcasting a
104+
non-`String` used to call only the `Object` handler, so a sprite overriding
105+
`whenIReceive(String)` silently heard nothing. Messages are names, as in
106+
Scratch.
107+
- `whenAddedToStage(Stage)` and `whenRemovedFromStage(Stage)` are gone —
108+
override the no-argument forms; the argument is what `getStage()` returns.
109+
- `setHitbox(Hitbox)` and `setHitbox(double[], double[])` are gone.
110+
`setHitbox(Shape)` and `setHitbox(double... points)` cover both.
111+
112+
### Interface parts have their own class
113+
114+
Buttons and bars want to be drawn on top, ignore the camera, and be sized in
115+
pixels rather than percent. That is now `UISprite`:
116+
117+
```java
118+
public class Button extends UISprite {
119+
public Button() {
120+
this.addCostume("button", "ui/button.png");
121+
this.setNineSlice(12, 24, 12, 24);
122+
this.setWidth(600);
123+
}
124+
}
125+
```
126+
127+
`setWidth`, `setHeight`, `setNineSlice` and `disableNineSlice` are `protected` on
128+
`Sprite` and public on `UISprite`; `changeWidth` and `changeHeight` are on
129+
`UISprite` only; `isUI(boolean)` became `protected setUI(boolean)`; and
130+
`Stage.goToUILayer` is gone.
131+
132+
### Fewer constructors
133+
134+
`Stage` had seven and `Window` eight, four of each differing only by a leading
135+
`boolean fullScreen`. `Stage` keeps `Stage()`, `Stage(width, height)` and
136+
`Stage(width, height, assets)`; `Window` keeps those three plus
137+
`Window(assets)`. Full screen is `Window.useFullScreen()`.
138+
139+
### Plumbing that was public only to cross a package
140+
141+
`Stage.draw`, `pre`, `keyEvent` and `mouseEvent` were public only so the render
142+
loop could reach them from another package. They are private now, reached
143+
through an internal interface the stage hands over.
144+
145+
`Stage.stamp(Queue<Stamp>, Layer)` went the same way. It existed so `TiledMap`
146+
could put a map's tiles into the stage's stamp queues, which meant a method
147+
taking `internal.Stamp` — a type nobody outside the library can build — sat in
148+
autocomplete next to the real blocks. `TiledMap` now goes through an internal
149+
accessor, and the method is package-private.
150+
151+
Neither was reachable in any useful way before, so no project has to change.
152+
`Sprite.stamp(Layer)` is still the stamp block.
153+
154+
## Behaviour that changed without the code changing
155+
156+
These need no edit, but a v4 project may look or behave differently:
157+
158+
- **Hitboxes now wrap the painted pixels**, not the whole costume. Costumes are
159+
often drawn into a larger canvas — a standing pose in a costume tall enough to
160+
hold a jumping one — and a sprite used to collide with that empty space.
161+
Collisions now match what a player can see. Hitboxes set with `setHitbox(...)`
162+
are unaffected.
163+
- **A sprite with no costume is no longer invisible.** It draws a marked box and
164+
explains once on the console what to add, because drawing nothing looks exactly
165+
like a broken program. A sprite with a hitbox and no costume — an invisible
166+
wall — is left alone.
167+
- **Asset failures throw `ScratchException` instead of calling `System.exit`.**
168+
Ending the process took BlueJ's virtual machine with it, so a mistyped costume
169+
name used to close everything the student had open.
170+
- **The loading screen fades in and out**, which adds about half a second to
171+
starting a project.
172+
173+
## What is new
174+
175+
- **838 built-in sprites and 266 built-in sounds** from [kenney.nl](https://kenney.nl)
176+
ship inside the jar, so a first project needs no downloads at all:
177+
178+
```java
179+
this.addCostume("alienGreen_walk1");
180+
this.addAnimation("walk", "alienGreen_walk%d", 2);
181+
this.addSound("footstep_carpet_000");
182+
```
183+
184+
Names are matched ignoring case, a wrong name suggests the closest matches
185+
instead of reporting a missing file, and anything with a file extension is
186+
still treated as a path — so existing projects are unaffected. Ogg Vorbis is
187+
now a supported sound format.
188+
189+
- **`ask` and `answer`**, so a program can take typed input. Unlike Scratch it
190+
does not pause the script, so check `isAsking()` or wait for `getAnswer()` to
191+
change.
192+
193+
- **`glide(seconds, x, y)`** with `isGliding()`, **`setVolume` / `changeVolume` /
194+
`getVolume`**, and **`Stage.waitUntil(condition)`**.
195+
196+
- **Your own logo on the loading screen**, with `Window.useSplashLogo(...)`.
197+
198+
- **Named timers create themselves** — `getTimer("countdown")` no longer has to
199+
be declared with `addTimer` first.
200+
201+
`touching color` was attempted and dropped: it needs the whole screen read back
202+
off the graphics card every frame, which is too slow for the machines this
203+
library is meant for. `getPixels()` carries the same limitation, now written
204+
down in its documentation.
205+
206+
## Fixes
207+
208+
- `ifOnEdgeBounce()` no longer leaves a sprite stuck half outside the stage. It
209+
used to reposition using the size of the hitbox alone, which only works while
210+
the hitbox is centred on the sprite; it now nudges the sprite by however far
211+
its hitbox pokes over the border.
212+
- Setting a stage no longer makes the window flash. On a scaled or HiDPI screen
213+
the letterbox bars came out grey for one frame, because a render buffer's
214+
OpenGL framebuffer was being allocated after the canvas had already been
215+
painted black.
216+
- `GifRecorder.stop()` no longer truncates the last frame of a recording.
217+
- Ogg files below roughly 8 kB used to decode to silence with no error, which
218+
covers most short sound effects.
219+
- The shaded "all" jar merges `META-INF/services`, so the Ogg decoder survives
220+
shading.
221+
- Sprites can be constructed and tested without a running window.
222+
223+
## Documentation
224+
225+
The book is now seven tutorials, and every Java block on the hand-written pages
226+
is compiled by `mvn test`, so an example cannot quietly stop working — which had
227+
already happened twice.
228+
229+
- **Your first program**, **Make it Walk**, **Catch the Coins**, **Red Light,
230+
Green Light**, **Guess the Number**, **Bouncing Hedgehog**, **Dodge the
231+
Rocks**. All but the hedgehog need no downloaded files, and each links a
232+
finished project to compare against.
233+
- **Browsable pages for the built-in sprites and sounds**, generated from the
234+
jar's own resources, searchable, with a click copying the `addCostume(...)` or
235+
`addSound(...)` line.
236+
- **The reference site shows the Scratch block beside the Java call** on 113
237+
pages, and 108 pages now carry a worked example, against one before v5. 429
238+
stale pages for methods that no longer exist were deleted.
239+
- `./scripts/run.sh` picks any of the 153 demos, finished projects and reference
240+
examples and runs it.

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
.claude
12
.classpath
23
!examples/**/.vscode
34
.idea

‎CLAUDE.md‎

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,29 @@ keeping the same mental model (Window / Stage / Sprite, costumes, backdrops, bro
1111
## Build & release commands
1212

1313
- `mvn compile` — compile the library (this is the command referenced in the README for day-to-day dev)
14+
- `mvn test` — run the JUnit 5 test suite (152 tests)
1415
- `mvn clean package` — build the JAR
1516
- `mvn clean package -Pall` — build a standalone JAR with all dependencies shaded in (maven-shade-plugin)
1617
- `mvn deploy -Pcentral` — release to Maven Central (GPG signing + central-publishing-maven-plugin); not something to run casually
1718
- `./build.sh` — full doc site build: copies CHANGELOG into the docs book, regenerates reference GIFs from `src/examples/java/reference`, substitutes the version into docs, then runs `npx hyperbook build` inside `docs/`
1819
- `cd docs && npx hyperbook dev` — run the documentation site locally
1920

20-
There is no test suite (no JUnit dependency, no `*Test.java` files under `src/main`). Verifying a
21-
change means making sure it compiles (`mvn compile`) and, where relevant, that the affected
22-
reference/demo example under `src/examples/java` still behaves correctly.
21+
Tests live in `src/test/java` and run under JUnit 5. They cover the parts that can be checked
22+
without a window — geometry, operators, colours, the built-in asset registry — plus one that guards
23+
the documentation:
24+
25+
- `DocumentationSnippetsTest` pulls every ```java block out of the hand-written pages under
26+
`docs/book` and compiles them, a page at a time. **If you change a public API, this is what tells
27+
you which tutorial you just broke.** Two examples had rotted unnoticed before it existed.
28+
29+
Most of the library still cannot be unit-tested, because anything with a costume needs a live
30+
`Window`. So verifying a change usually also means running something: the affected
31+
reference/demo example under `src/examples/java`, or `./scripts/run.sh` to pick one.
32+
33+
Running a windowed example headlessly works with `xvfb-run -a java ...`, which is how the
34+
documentation GIFs are recorded. Note that under Xvfb the window is exactly the render size, so
35+
letterbox bars never appear — a bug that only shows on a scaled or HiDPI display will not reproduce
36+
there.
2337

2438
Java 17 is required (`maven.compiler.release` in `pom.xml`).
2539

‎README.md‎

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,70 @@ You can release the library with the following command:
3535
mvn deploy -Pcentral
3636
```
3737

38+
## Running anything, quickly
39+
40+
```
41+
./scripts/run.sh
42+
```
43+
44+
Pick a demo, a finished tutorial project or a reference example from a list and
45+
it runs. Needs [fzf](https://github.com/junegunn/fzf).
46+
47+
```
48+
./scripts/run.sh cat # start with the list already filtered
49+
./scripts/run.sh --demos # only the demos
50+
./scripts/run.sh --archives # only the finished tutorial projects
51+
./scripts/run.sh --reference # only the reference examples
52+
```
53+
54+
A query matching exactly one thing runs it without asking. The library and
55+
examples are compiled if they have not been already, and the tutorial projects,
56+
which are not part of the build, are compiled on the fly.
57+
58+
## Running the tutorial projects
59+
60+
`docs/archives` holds a finished project for every tutorial. They are handy for
61+
checking that a change to the library has not broken what the documentation
62+
teaches.
63+
64+
### With Maven, without building a jar
65+
66+
```
67+
mvn -q compile exec:exec -Parchive -Darchive=make-it-walk-100 -Dmain=WalkStage
68+
```
69+
70+
This compiles the chosen project against the library as it stands in your
71+
working copy and runs it in a fresh JVM. The project sources go to their own
72+
output directory, so they can never end up in a released jar.
73+
74+
The finished projects and the class to start are:
75+
76+
| `-Darchive=` | `-Dmain=` |
77+
|---|---|
78+
| `getting-started-100` | `MyStage` |
79+
| `make-it-walk-100` | `WalkStage` |
80+
| `catch-the-coins-100` | `CatchStage` |
81+
| `red-light-green-light-100` | `RaceStage` |
82+
| `guess-the-number-100` | `GuessStage` |
83+
| `bouncy-hedgehog-100` | `BouncyHedgehogStage` |
84+
85+
### With BlueJ or VS Code
86+
87+
Those need a real jar in each project's `+libs` folder:
88+
89+
```
90+
mvn package -Pall -DskipTests
91+
./scripts/link-jar.sh
92+
```
93+
94+
The script links the jar it finds in `target/` into every project under
95+
`docs/archives`, so any of them can be opened directly with BlueJ or VS Code.
96+
It symlinks where it can and copies where it cannot, so it works on Windows too.
97+
98+
**Those jars are for local testing only.** They are git-ignored, and `build.sh`
99+
removes them before the archives are zipped — a jar left in place would put 19 MB
100+
inside every project download.
101+
38102
## Automatic Release Flow
39103

40104
For the automatic release flow you have to commit a changeset. This is a `.md`-file in the `.changeset` directory.

0 commit comments

Comments
 (0)