diff --git a/app/src/main/java/com/annimon/hotarufx/exceptions/RendererException.java b/app/src/main/java/com/annimon/hotarufx/exceptions/RendererException.java new file mode 100644 index 0000000..39cd0b6 --- /dev/null +++ b/app/src/main/java/com/annimon/hotarufx/exceptions/RendererException.java @@ -0,0 +1,8 @@ +package com.annimon.hotarufx.exceptions; + +public class RendererException extends RuntimeException { + + public RendererException(String message) { + super(message); + } +} diff --git a/app/src/main/java/com/annimon/hotarufx/ui/Renderer.java b/app/src/main/java/com/annimon/hotarufx/ui/Renderer.java new file mode 100644 index 0000000..38d13c3 --- /dev/null +++ b/app/src/main/java/com/annimon/hotarufx/ui/Renderer.java @@ -0,0 +1,117 @@ +package com.annimon.hotarufx.ui; + +import com.annimon.hotarufx.bundles.Bundle; +import com.annimon.hotarufx.bundles.BundleLoader; +import com.annimon.hotarufx.exceptions.RendererException; +import com.annimon.hotarufx.lexer.HotaruLexer; +import com.annimon.hotarufx.lib.Context; +import com.annimon.hotarufx.parser.HotaruParser; +import com.annimon.hotarufx.parser.visitors.InterpreterVisitor; +import com.annimon.hotarufx.visual.Composition; +import java.util.List; +import java.util.function.BiConsumer; +import javafx.stage.Modality; +import javafx.stage.Stage; +import lombok.val; + +public class Renderer { + + public static Renderer init() { + return new Renderer(); + } + + private Renderer() { + } + + public WithInput input(String input) { + return new WithInput(input); + } + + + public class WithInput { + + private final String input; + + private WithInput(String input) { + this.input = input; + } + + public WithContext context(Context context) { + return new WithContext(this, context); + } + } + + + public class WithContext { + + private final WithInput source; + private final Context context; + + private WithContext(WithInput source, Context context) { + this.source = source; + this.context = context; + } + + public Evaluated evaluateWithRuntimeBundle() { + return evaluateWithBundles(BundleLoader.runtimeBundles()); + } + + public Evaluated evaluateWithBundles(List> bundles) { + BundleLoader.load(context, bundles); + return evaluate(); + } + + private Evaluated evaluate() { + val parser = new HotaruParser(HotaruLexer.tokenize(source.input)); + val program = parser.parse(); + if (parser.getParseErrors().hasErrors()) { + throw new RendererException(parser.getParseErrors().toString()); + } + program.accept(new InterpreterVisitor(), context); + return new Evaluated(this); + } + } + + + public class Evaluated { + + private final WithContext source; + + private Evaluated(WithContext source) { + this.source = source; + } + + public WithStage prepareStage(Stage primaryStage) { + checkCompositionExists(); + val stage = new Stage(); + stage.initOwner(primaryStage); + stage.initModality(Modality.WINDOW_MODAL); + val composition = source.context.composition(); + stage.setScene(composition.produceAnimationScene()); + return new WithStage(composition, stage); + } + + private void checkCompositionExists() { + if (source.context.composition() == null) { + throw new RendererException("There is no composition.\n" + + "Make sure you call composition method."); + } + } + } + + public class WithStage { + + private final Composition composition; + private final Stage stage; + + private WithStage(Composition composition, Stage stage) { + this.composition = composition; + this.stage = stage; + } + + public WithStage peek(BiConsumer consumer) { + consumer.accept(stage, composition); + return this; + } + } +} diff --git a/app/src/main/java/com/annimon/hotarufx/ui/controller/EditorController.java b/app/src/main/java/com/annimon/hotarufx/ui/controller/EditorController.java index 810ad03..f023c60 100644 --- a/app/src/main/java/com/annimon/hotarufx/ui/controller/EditorController.java +++ b/app/src/main/java/com/annimon/hotarufx/ui/controller/EditorController.java @@ -1,16 +1,14 @@ package com.annimon.hotarufx.ui.controller; import com.annimon.hotarufx.Main; -import com.annimon.hotarufx.bundles.BundleLoader; import com.annimon.hotarufx.exceptions.Exceptions; +import com.annimon.hotarufx.exceptions.RendererException; import com.annimon.hotarufx.io.DocumentListener; import com.annimon.hotarufx.io.DocumentManager; import com.annimon.hotarufx.io.FileManager; import com.annimon.hotarufx.io.IOStream; -import com.annimon.hotarufx.lexer.HotaruLexer; import com.annimon.hotarufx.lib.Context; -import com.annimon.hotarufx.parser.HotaruParser; -import com.annimon.hotarufx.parser.visitors.InterpreterVisitor; +import com.annimon.hotarufx.ui.Renderer; import com.annimon.hotarufx.ui.SyntaxHighlighter; import com.annimon.hotarufx.ui.control.LibraryItem; import java.io.IOException; @@ -129,47 +127,34 @@ public class EditorController implements Initializable, DocumentListener { if (input.isEmpty()) { return; } - - val context = new Context(); - BundleLoader.load(context, BundleLoader.runtimeBundles()); - - val parser = new HotaruParser(HotaruLexer.tokenize(input)); - val program = parser.parse(); - if (parser.getParseErrors().hasErrors()) { - log.setText(parser.getParseErrors().toString()); - logPane.setExpanded(true); - return; - } try { - program.accept(new InterpreterVisitor(), context); + Renderer.init() + .input(input) + .context(new Context()) + .evaluateWithRuntimeBundle() + .prepareStage(primaryStage) + .peek((stage, composition) -> { + val timeline = composition.getTimeline(); + timeline.getFxTimeline().currentTimeProperty().addListener((o, oldValue, d) -> { + val min = (int) d.toMinutes(); + val durationSec = d.subtract(Duration.minutes(min)); + val sec = (int) durationSec.toSeconds(); + val durationMs = durationSec.subtract(Duration.seconds(sec)); + val frame = (int) (durationMs.toMillis() * timeline.getFrameRate() / 1000d); + val allFrame = (int) (d.toMillis() * timeline.getFrameRate() / 1000d); + stage.setTitle(String.format("%02d:%02d.%02d %d", min, sec, frame, allFrame)); + }); + + stage.setOnShown(e -> timeline.getFxTimeline().play()); + stage.show(); + }); + } catch (RendererException re) { + logError(re.getMessage()); + logPane.setExpanded(true); } catch (RuntimeException e) { logError(Exceptions.stackTraceToString(e)); logPane.setExpanded(true); - return; } - - val stage = new Stage(); - val composition = context.composition(); - if (composition == null) { - logError("There is no composition.\nMake sure you call composition method."); - logPane.setExpanded(true); - return; - } - stage.initOwner(primaryStage); - stage.initModality(Modality.WINDOW_MODAL); - stage.setScene(composition.produceAnimationScene()); - val timeline = composition.getTimeline(); - timeline.getFxTimeline().currentTimeProperty().addListener((o, oldValue, d) -> { - val min = (int) d.toMinutes(); - val durationSec = d.subtract(Duration.minutes(min)); - val sec = (int) durationSec.toSeconds(); - val durationMs = durationSec.subtract(Duration.seconds(sec)); - val frame = (int) (durationMs.toMillis() * timeline.getFrameRate() / 1000d); - val allFrame = (int) (d.toMillis() * timeline.getFrameRate() / 1000d); - stage.setTitle(String.format("%02d:%02d.%02d %d", min, sec, frame, allFrame)); - }); - stage.setOnShown(e -> timeline.getFxTimeline().play()); - stage.show(); } @Override