package edu.gatech.cs2340.trydent.internal; import java.awt.event.KeyAdapter; import javafx.animation.KeyFrame; import javafx.animation.Timeline; import javafx.application.Platform; import javafx.embed.swing.JFXPanel; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.input.MouseEvent; import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; import javafx.util.Duration; import javax.swing.JFrame; import javax.swing.SwingUtilities; import edu.gatech.cs2340.trydent.ContinuousEvent; import edu.gatech.cs2340.trydent.log.Log; public class SwingManager implements JavaFXManager { private volatile JFrame frame; private volatile boolean starting = false; private volatile boolean stopping = false; private String windowTitle = "TrydentEngine"; private int width = 640, height = 480; private Color backgroundColor = Color.TRANSPARENT; private Runnable updateAction; private Scene scene; private StackPane root; private Group background; private Timeline timeline; @Override public void startJavaFX() { if (starting) return; if (isRunning()) { Log.warn("TrydentEngine is already running."); return; } starting = true; createFrame(); } private void createFrame() { frame = new JFrame(windowTitle); final JFXPanel panel = new JFXPanel(); frame.add(panel); frame.setSize(width, height); frame.setLocationRelativeTo(null); frame.setVisible(true); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Platform.setImplicitExit(false); Platform.runLater(new Runnable() { @Override public void run() { createScene(panel); } }); } private void createScene(JFXPanel panel) { root = new StackPane(); scene = new Scene(root, backgroundColor); panel.setScene(scene); background = new Group(); root.getChildren().addAll(new Pane(background)); addEventHooks(panel); long sleepTimeMillis = 5; timeline = new Timeline(new KeyFrame(Duration.ZERO, new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { if (updateAction == null) return; updateAction.run(); } }), new KeyFrame(Duration.millis(sleepTimeMillis))); timeline.setCycleCount(Timeline.INDEFINITE); timeline.play(); starting = false; } private void addEventHooks(JFXPanel panel) { panel.addKeyListener(new KeyAdapter() { @Override public void keyPressed(java.awt.event.KeyEvent e) { KeyboardImpl.pressed(KeyboardImpl.convertSwingToJavaFXKeyEvent(e.getKeyCode())); } @Override public void keyReleased(java.awt.event.KeyEvent e) { KeyboardImpl.released(KeyboardImpl.convertSwingToJavaFXKeyEvent(e.getKeyCode())); } }); root.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> MouseImpl.pressed(event)); root.addEventHandler(MouseEvent.MOUSE_RELEASED, event -> MouseImpl.released(event)); root.addEventHandler(MouseEvent.MOUSE_MOVED, event -> MouseImpl.moved(event)); root.addEventHandler(MouseEvent.MOUSE_DRAGGED, event -> MouseImpl.dragged(event)); new ContinuousEvent() { @Override public void onUpdate() { MouseImpl.newFrame(); KeyboardImpl.newFrame(); } }; } @Override public void stopJavaFX() { if (stopping) return; if (frame != null && !starting) { stopping = true; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { frame.setVisible(false); frame.dispose(); Platform.runLater(new Runnable() { @Override public void run() { timeline.stop(); frame = null; stopping = false; } }); } }); } else { Log.warn("Stop called before JavaFX started."); } } @Override public void setUpdateAction(Runnable updateAction) { this.updateAction = updateAction; } @Override public boolean isRunning() { return frame != null; } @Override public void setWindowTitle(String title) { windowTitle = String.valueOf(title); if (frame != null) { frame.setTitle(windowTitle); } } @Override public void setWindowSize(int width, int height) { this.width = width; this.height = height; if (frame != null) { frame.setSize(width, height); frame.setLocationRelativeTo(null); } } @Override public void setFullscreen(boolean fullscreen) { if (fullscreen) throw new TrydentInternalException("Fullscreen not implemented yet."); } @Override public Group getBackground() { return background; } @Override public void setBackgroundColor(Color color) { backgroundColor = color; if (scene != null) { scene.setFill(color); } } @Override public Scene getScene() { return scene; } @Override public Node setForeground(Node foreground) { if (root.getChildren().size() == 1) { root.getChildren().add(foreground); return null; } else { Node previous = root.getChildren().get(1); root.getChildren().set(1, foreground); return previous; } } }