Open code editor when lua file is opened and allow live error-resistant editing

pull/82/head
James Ball 2022-06-07 22:19:15 +01:00 zatwierdzone przez James H Ball
rodzic d2cd0ea2a1
commit c37bab596d
5 zmienionych plików z 129 dodań i 36 usunięć

Wyświetl plik

@ -5,21 +5,15 @@ import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import sh.ball.audio.ShapeAudioPlayer;
import sh.ball.audio.engine.AudioDevice;
import sh.ball.audio.engine.ConglomerateAudioEngine;
import sh.ball.audio.midi.MidiCommunicator;
import sh.ball.engine.Vector3;
import sh.ball.gui.components.CodeEditor;
import sh.ball.gui.controller.MainController;
import sh.ball.shapes.Vector2;
@ -32,6 +26,9 @@ public class Gui extends Application {
public static final MidiCommunicator midiCommunicator = new MidiCommunicator();
public static ShapeAudioPlayer audioPlayer;
public static AudioDevice defaultDevice;
public static CodeEditor editor;
public static Stage editorStage;
public static Scene editorScene;
static {
try {
@ -47,6 +44,15 @@ public class Gui extends Application {
public void start(Stage stage) throws Exception {
System.setProperty("prism.lcdtext", "false");
editor = new CodeEditor();
editor.initialize();
editorStage = new Stage();
editorScene = new Scene(editor);
editorStage.setScene(editorScene);
editorStage.getIcons().add(new Image(Objects.requireNonNull(Gui.class.getResourceAsStream("/icons/icon.png"))));
editor.prefHeightProperty().bind(editorStage.heightProperty());
editor.prefWidthProperty().bind(editorStage.widthProperty());
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/main.fxml"));
Parent root = loader.load();
MainController controller = loader.getController();
@ -103,19 +109,18 @@ public class Gui extends Application {
Platform.exit();
System.exit(0);
});
}
CodeEditor editor = new CodeEditor("theta = theta + math.sqrt(step) / 1000000000\n" +
"return {0.3 * math.tan(theta * step), 0.3 * math.tan(theta * step + math.pi / 2), theta}");
// display the scene.
Scene editorScene = new Scene(editor);
Stage editorStage = new Stage();
editor.prefHeightProperty().bind(stage.heightProperty());
editor.prefWidthProperty().bind(stage.widthProperty());
editorStage.setScene(editorScene);
public static void launchCodeEditor(String code, String fileName) {
editor.setCode(code, fileName);
editorStage.show();
editorStage.setTitle(fileName);
}
public static void closeCodeEditor() {
if (editorStage != null) {
editorStage.close();
}
}
public static void main(String[] args) {

Wyświetl plik

@ -10,28 +10,46 @@ import netscape.javascript.JSObject;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
public class CodeEditor extends StackPane {
final WebView webview = new WebView();
private WebView webview;
private String editingCode;
private String editingCode = "";
private String fileName = "";
private BiConsumer<byte[], String> callback;
private String applyEditingTemplate() throws URISyntaxException, IOException {
String template = Files.readString(Path.of(getClass().getResource("/html/code_editor.html").toURI()));
return template.replace("${code}", editingCode);
}
public void updateCode() {
this.editingCode = (String) webview.getEngine().executeScript("editor.getValue();");
System.out.println(editingCode);
public void setCode(String editingCode, String fileName) {
this.fileName = fileName;
this.editingCode = editingCode;
JSObject window = (JSObject) webview.getEngine().executeScript("window");
window.setMember("newCode", editingCode);
webview.getEngine().executeScript("editor.setValue(newCode);");
}
public CodeEditor(String editingCode) throws URISyntaxException, IOException {
this.editingCode = editingCode;
public void updateCode() throws Exception {
editingCode = (String) webview.getEngine().executeScript("editor.getValue();");
if (callback != null) {
callback.accept(editingCode.getBytes(StandardCharsets.UTF_8), fileName);
}
}
public void setCallback(BiConsumer<byte[], String> callback) {
this.callback = callback;
}
public CodeEditor() {}
public void initialize() throws URISyntaxException, IOException {
webview = new WebView();
webview.getEngine().getLoadWorker().stateProperty().addListener((e, old, state) -> {
if (state == Worker.State.SUCCEEDED) {
JSObject window = (JSObject) webview.getEngine().executeScript("window");
@ -47,4 +65,9 @@ public class CodeEditor extends StackPane {
this.getChildren().add(webview);
}
@FunctionalInterface
public interface BiConsumer<T, U> {
void accept(T t, U u) throws Exception;
}
}

Wyświetl plik

@ -16,7 +16,9 @@ import org.w3c.dom.NodeList;
import sh.ball.audio.*;
import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.*;
import java.util.*;
import java.util.concurrent.*;
@ -59,8 +61,7 @@ import sh.ball.parser.ParserFactory;
import sh.ball.shapes.Shape;
import sh.ball.shapes.Vector2;
import static sh.ball.gui.Gui.audioPlayer;
import static sh.ball.gui.Gui.defaultDevice;
import static sh.ball.gui.Gui.*;
import static sh.ball.math.Math.parseable;
public class MainController implements Initializable, FrequencyListener, MidiListener, AudioInputListener {
@ -100,6 +101,7 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
private List<String> frameSourcePaths = new ArrayList<>();
private List<FrameSource<Vector2>> sampleSources = new ArrayList<>();
private List<FrameSource<List<Shape>>> frameSources = new ArrayList<>();
private LuaParser luaParser = new LuaParser();
private FrameSource<Vector2> sampleSource;
private FrameProducer<List<Shape>> producer;
private int currentFrameSource;
@ -498,6 +500,8 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
}
}
editor.setCallback(this::updateFileData);
objectServer = new ObjectServer(this::enableObjectServerRendering, this::disableObjectServerRendering);
new Thread(objectServer).start();
@ -620,6 +624,7 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
if (frames != null) {
frames.enable();
audioPlayer.removeSampleSource();
closeCodeEditor();
Object oldSettings = producer.getFrameSettings();
producer = new FrameProducer<>(audioPlayer, frames);
@ -635,6 +640,9 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
executor.submit(producer);
} else if (samples != null) {
samples.enable();
String code = new String(openFiles.get(currentFrameSource), StandardCharsets.UTF_8);
closeCodeEditor();
launchCodeEditor(code, frameSourcePaths.get(currentFrameSource));
audioPlayer.setSampleSource(samples);
} else {
throw new RuntimeException("Expected to have either a frame source or sample source");
@ -647,6 +655,32 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
objTitledPane.setDisable(!ObjParser.isObjFile(frameSourcePaths.get(index)));
}
void updateFileData(byte[] file, String name) throws Exception {
int index = frameSourcePaths.indexOf(name);
if (index == -1) {
throw new RuntimeException("Can't find open file with name: " + name);
}
if (LuaParser.isLuaFile(name)) {
luaParser.setScriptFromInputStream(new ByteArrayInputStream(file));
sampleSources.set(index, luaParser.parse());
frameSources.set(index, null);
} else {
frameSources.set(index, ParserFactory.getParser(name, file).parse());
sampleSources.set(index, null);
}
frameSourcePaths.set(index, name);
openFiles.set(index, file);
frameSources.stream().filter(Objects::nonNull).forEach(FrameSource::disable);
sampleSources.stream().filter(Objects::nonNull).forEach(FrameSource::disable);
FrameSource<Vector2> samples = sampleSources.get(index);
if (samples != null) {
samples.enable();
audioPlayer.setSampleSource(samples);
}
}
void updateFiles(List<byte[]> files, List<String> names, int startingFrameSource) throws Exception {
List<FrameSource<Vector2>> newSampleSources = new ArrayList<>();
List<FrameSource<List<Shape>>> newFrameSources = new ArrayList<>();
@ -661,7 +695,8 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
for (int i = 0; i < files.size(); i++) {
try {
if (LuaParser.isLuaFile(names.get(i))) {
newSampleSources.add(new LuaParser(new ByteArrayInputStream(files.get(i))).parse());
luaParser.setScriptFromInputStream(new ByteArrayInputStream(files.get(i)));
newSampleSources.add(luaParser.parse());
newFrameSources.add(null);
} else {
newFrameSources.add(ParserFactory.getParser(names.get(i), files.get(i)).parse());

Wyświetl plik

@ -1,6 +1,5 @@
package sh.ball.parser.lua;
import org.luaj.vm2.lib.jse.JsePlatform;
import sh.ball.audio.FrameSource;
import sh.ball.parser.FileParser;
import sh.ball.shapes.Vector2;
@ -11,9 +10,11 @@ import java.util.stream.Collectors;
public class LuaParser extends FileParser<FrameSource<Vector2>> {
private final LuaSampleSource sampleSource = new LuaSampleSource();
private String script;
public LuaParser(InputStream inputStream) {
public void setScriptFromInputStream(InputStream inputStream) {
this.script = new BufferedReader(new InputStreamReader(inputStream))
.lines().collect(Collectors.joining("\n"));
}
@ -29,7 +30,9 @@ public class LuaParser extends FileParser<FrameSource<Vector2>> {
Compilable e = (Compilable) sem.getEngineByName("luaj");
CompiledScript compiledScript = e.compile(script);
return new LuaSampleSource(compiledScript);
sampleSource.setScript(script, compiledScript);
return sampleSource;
}
public static boolean isLuaFile(String path) {

Wyświetl plik

@ -10,29 +10,56 @@ import javax.script.SimpleBindings;
public class LuaSampleSource implements FrameSource<Vector2> {
private final CompiledScript script;
private final Bindings bindings = new SimpleBindings();
private CompiledScript lastWorkingScript;
private String lastWorkingTextScript;
private CompiledScript script;
private String textScript;
private boolean active = true;
private long step = 1;
private double theta = 0.0;
public LuaSampleSource(CompiledScript script) {
public void setScript(String textScript, CompiledScript script) {
if (lastWorkingScript == null) {
lastWorkingScript = script;
lastWorkingTextScript = textScript;
}
this.script = script;
this.textScript = textScript;
}
@Override
public Vector2 next() {
try {
boolean updatedFile = false;
bindings.put("step", (double) step);
bindings.put("theta", theta);
LuaValue result = (LuaValue) script.eval(bindings);
LuaValue result;
try {
result = (LuaValue) script.eval(bindings);
if (!lastWorkingTextScript.equals(textScript)) {
lastWorkingScript = script;
lastWorkingTextScript = textScript;
updatedFile = true;
}
} catch (Exception e) {
result = (LuaValue) lastWorkingScript.eval(bindings);
}
step++;
theta = result.get(3).checkdouble();
return new Vector2(result.get(1).checkdouble(), result.get(2).checkdouble());
} catch (Exception e) {
e.printStackTrace();
if (updatedFile) {
// reset variables
step = 1;
theta = 0;
}
return new Vector2(result.get(1).checkdouble(), result.get(2).checkdouble());
} catch (Exception ignored) {}
return new Vector2();
}