diff --git a/README.md b/README.md
index c0bc81c..28d44a9 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@ Scratch for Java Studio is a desktop IDE for students moving from Scratch to
Java. It builds projects with [Scratch for Java](https://github.com/openpatch/scratch-for-java)
and keeps them as ordinary Java source files that also work in BlueJ and VS Code.
-**Status: 0.1.0-alpha.10.** The IDE and its
+**Status: 0.1.0-alpha.11.** The IDE and its
project format may change while we test it with students and teachers.

diff --git a/app/pom.xml b/app/pom.xml
index 31ea4f5..11cc3f6 100644
--- a/app/pom.xml
+++ b/app/pom.xml
@@ -7,7 +7,7 @@
org.openpatch.scratch4j
scratch4j-ide
- 0.1.0-alpha.10
+ 0.1.0-alpha.11
app
diff --git a/core/pom.xml b/core/pom.xml
index 7f0ac7a..9d9cd8e 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -7,7 +7,7 @@
org.openpatch.scratch4j
scratch4j-ide
- 0.1.0-alpha.10
+ 0.1.0-alpha.11
core
diff --git a/export/pom.xml b/export/pom.xml
index 9fe0b52..8cbe789 100644
--- a/export/pom.xml
+++ b/export/pom.xml
@@ -7,7 +7,7 @@
org.openpatch.scratch4j
scratch4j-ide
- 0.1.0-alpha.10
+ 0.1.0-alpha.11
export
diff --git a/pom.xml b/pom.xml
index 69c0889..cf09b41 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
org.openpatch.scratch4j
scratch4j-ide
- 0.1.0-alpha.10
+ 0.1.0-alpha.11
pom
Scratch for Java Studio
diff --git a/runner/pom.xml b/runner/pom.xml
index 2b209c2..81189d3 100644
--- a/runner/pom.xml
+++ b/runner/pom.xml
@@ -7,7 +7,7 @@
org.openpatch.scratch4j
scratch4j-ide
- 0.1.0-alpha.10
+ 0.1.0-alpha.11
runner
diff --git a/runner/src/main/java/org/openpatch/scratch4j/runner/LauncherSource.java b/runner/src/main/java/org/openpatch/scratch4j/runner/LauncherSource.java
index 5fdd0b6..fe09f93 100644
--- a/runner/src/main/java/org/openpatch/scratch4j/runner/LauncherSource.java
+++ b/runner/src/main/java/org/openpatch/scratch4j/runner/LauncherSource.java
@@ -184,7 +184,9 @@ static void start() {
new Hook();
hooked = true;
}
- System.err.println(MARK + "frames " + frames);
+ // "loading" while the library's loading screen shows (slow PCs stop
+ // drawing for a while when the stage takes over)
+ System.err.println(MARK + "frames " + frames + (loading() ? " loading" : ""));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
@@ -358,6 +360,24 @@ static Object call(Object target, String name, Class>[] types, Object... args)
}
/** The library's Processing sketch (reflective: no Processing types needed to compile). */
+ private static java.lang.reflect.Field state;
+
+ /** The library still shows its loading screen (before the stage runs). */
+ static boolean loading() {
+ Object applet = applet();
+ if (applet == null) return false;
+ try {
+ if (state == null) {
+ java.lang.reflect.Field field = applet.getClass().getDeclaredField("state");
+ field.setAccessible(true);
+ state = field;
+ }
+ return String.valueOf(state.get(applet)).equals("LOADING");
+ } catch (ReflectiveOperationException | RuntimeException e) {
+ return false; // an older library: no loading screen to wait for
+ }
+ }
+
static Object applet() {
try {
return Class.forName("org.openpatch.scratch.internal.Applet")
diff --git a/runner/src/main/java/org/openpatch/scratch4j/runner/RunHandle.java b/runner/src/main/java/org/openpatch/scratch4j/runner/RunHandle.java
index 0d4e655..0a9ee17 100644
--- a/runner/src/main/java/org/openpatch/scratch4j/runner/RunHandle.java
+++ b/runner/src/main/java/org/openpatch/scratch4j/runner/RunHandle.java
@@ -88,7 +88,9 @@ public void stop() {
private static void control(String message, RunListener listener) {
if (message.startsWith("frames ")) {
try {
- listener.onFrames(Long.parseLong(message.substring("frames ".length()).trim()));
+ String[] beat = message.substring("frames ".length()).trim().split(" ");
+ listener.onFrames(Long.parseLong(beat[0]),
+ beat.length > 1 && beat[1].equals("loading"));
} catch (NumberFormatException ignored) {
// a garbled heartbeat is skipped
}
diff --git a/runner/src/main/java/org/openpatch/scratch4j/runner/RunListener.java b/runner/src/main/java/org/openpatch/scratch4j/runner/RunListener.java
index 8db2bf3..a7a67b1 100644
--- a/runner/src/main/java/org/openpatch/scratch4j/runner/RunListener.java
+++ b/runner/src/main/java/org/openpatch/scratch4j/runner/RunListener.java
@@ -18,6 +18,14 @@ default void onCompileFailed(org.openpatch.scratch4j.core.compile.CompileResult
*/
default void onFrames(long frames) {}
+ /**
+ * The heartbeat with whether the library still shows its loading screen
+ * (where a slow PC may draw nothing for a while). Defaults to {@link #onFrames(long)}.
+ */
+ default void onFrames(long frames, boolean loading) {
+ onFrames(frames);
+ }
+
/** The game loop stopped ({@code pause}, or after a {@code step}) at this frame. */
default void onPaused(long frame) {}
diff --git a/runner/src/test/java/org/openpatch/scratch4j/runner/IdeControlIT.java b/runner/src/test/java/org/openpatch/scratch4j/runner/IdeControlIT.java
index 5f05c26..0a0b7ec 100644
--- a/runner/src/test/java/org/openpatch/scratch4j/runner/IdeControlIT.java
+++ b/runner/src/test/java/org/openpatch/scratch4j/runner/IdeControlIT.java
@@ -231,10 +231,14 @@ void aForeverLoopInRunStopsTheFrameCount() throws Exception {
+ " while (true) { }\n }\n }"));
ScratchProject project = ScratchProject.open(tmp.resolve("frozen"));
List beats = new CopyOnWriteArrayList<>();
+ List loading = new CopyOnWriteArrayList<>();
RunHandle handle = new ProjectRunner().run(project,
RunConfig.of(project.startStage()).withControl(),
new RunListener() {
- @Override public void onFrames(long f) { beats.add(f); }
+ @Override public void onFrames(long f, boolean l) {
+ beats.add(f);
+ loading.add(l);
+ }
});
try {
waitFor(() -> beats.size() >= 8 && beats.get(beats.size() - 1) > 0, 20000);
@@ -243,6 +247,8 @@ void aForeverLoopInRunStopsTheFrameCount() throws Exception {
assertThat(beats.get(beats.size() - 1)).as("frozen: no new frames").isEqualTo(last);
// run() starts a few frames after the first drawn frame
assertThat(last).isBetween(55L, 200L);
+ // past the loading screen: the IDE's short freeze limit applies
+ assertThat(loading.get(loading.size() - 1)).as("loading flags %s", loading).isFalse();
} finally {
handle.stop();
}
diff --git a/sound/pom.xml b/sound/pom.xml
index c512779..861d009 100644
--- a/sound/pom.xml
+++ b/sound/pom.xml
@@ -7,7 +7,7 @@
org.openpatch.scratch4j
scratch4j-ide
- 0.1.0-alpha.10
+ 0.1.0-alpha.11
sound
diff --git a/ui/pom.xml b/ui/pom.xml
index 2d76c54..12fb056 100644
--- a/ui/pom.xml
+++ b/ui/pom.xml
@@ -7,7 +7,7 @@
org.openpatch.scratch4j
scratch4j-ide
- 0.1.0-alpha.10
+ 0.1.0-alpha.11
ui
diff --git a/ui/src/main/java/org/openpatch/scratch4j/ui/EditorTabs.java b/ui/src/main/java/org/openpatch/scratch4j/ui/EditorTabs.java
index f172f35..5bcf330 100644
--- a/ui/src/main/java/org/openpatch/scratch4j/ui/EditorTabs.java
+++ b/ui/src/main/java/org/openpatch/scratch4j/ui/EditorTabs.java
@@ -412,6 +412,13 @@ boolean close(Tab tab) {
return true;
}
+ /** Drops every tab without asking (the project closes or another one opens). */
+ void clear() {
+ getTabs().clear();
+ openTabs.clear();
+ toolTabs.clear();
+ }
+
/** Right-click on a tab: close it, the others, or all. */
private javafx.scene.control.ContextMenu tabMenu(Tab tab) {
javafx.scene.control.MenuItem close = new javafx.scene.control.MenuItem(
diff --git a/ui/src/main/java/org/openpatch/scratch4j/ui/StudioApp.java b/ui/src/main/java/org/openpatch/scratch4j/ui/StudioApp.java
index b4bd359..ca23b1a 100644
--- a/ui/src/main/java/org/openpatch/scratch4j/ui/StudioApp.java
+++ b/ui/src/main/java/org/openpatch/scratch4j/ui/StudioApp.java
@@ -596,7 +596,7 @@ private void closeProject() {
checkDelay.stop();
hotSwapDelay.stop();
stuckDelay.stop();
- editor.getTabs().clear();
+ editor.clear();
project.set(null);
hasProject.set(false);
stage.setTitle("Scratch for Java Studio");
@@ -1031,7 +1031,7 @@ private void open(ScratchProject p) {
projectTitle.setText(p.name());
hasProject.set(true);
fileTree.setRoot(p.root());
- editor.getTabs().clear();
+ editor.clear();
selector = new StageSelectorView(p, this::openDesigner,
cls -> editor.open(p.root().resolve(cls + ".java")),
() -> {
@@ -1531,8 +1531,10 @@ private Path mediaFile(String extension) {
* A program whose frame count stops for 3 seconds (or that never shows a
* window) is frozen - almost always a loop that never ends. The console
* explains it once; the Problems tab shows where the transition lint found it.
+ * On the loading screen it gets 20 seconds: a slow PC draws nothing for a
+ * while when the stage takes over.
*/
- void watchdog(long frames) {
+ void watchdog(long frames, boolean loading) {
long now = System.currentTimeMillis();
if (frames != watchFrames) {
if (watchWarned && frames > watchFrames && watchFrames >= 0) {
@@ -1558,7 +1560,7 @@ void watchdog(long frames) {
watchChanged = now;
return;
}
- boolean stalled = frames >= 0 && now - watchChanged >= 3000;
+ boolean stalled = frames >= 0 && now - watchChanged >= (loading ? 20000 : 3000);
boolean noWindow = frames < 0 && now - watchStarted >= 10000;
if ((stalled || noWindow) && !watchWarned && running.get()) {
watchWarned = true;
@@ -3092,8 +3094,8 @@ private void runStage(String startStage, boolean debug) {
liveSwap = new org.openpatch.scratch4j.runner.HotSwap(p, session);
RunHandle handle = runner.run(p, config,
new RunListener() {
- @Override public void onFrames(long frames) {
- Platform.runLater(() -> watchdog(frames));
+ @Override public void onFrames(long frames, boolean loading) {
+ Platform.runLater(() -> watchdog(frames, loading));
}
@Override public void onPaused(long frame) {