Backported changes from Lawless Legends app experience; namely better boot behavior and also UI Controls Overlay. :)
@ -18,8 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
package jace;
|
package jace;
|
||||||
|
|
||||||
import jace.apple2e.Apple2e;
|
|
||||||
import jace.config.Configuration;
|
import jace.config.Configuration;
|
||||||
|
import jace.apple2e.Apple2e;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -18,10 +18,10 @@
|
|||||||
*/
|
*/
|
||||||
package jace;
|
package jace;
|
||||||
|
|
||||||
import com.sun.javafx.tk.quantum.OverlayWarning;
|
|
||||||
import jace.apple2e.MOS65C02;
|
import jace.apple2e.MOS65C02;
|
||||||
import jace.apple2e.RAM128k;
|
import jace.apple2e.RAM128k;
|
||||||
import jace.apple2e.SoftSwitches;
|
import jace.apple2e.SoftSwitches;
|
||||||
|
import jace.config.ConfigurableField;
|
||||||
import jace.config.ConfigurationUIController;
|
import jace.config.ConfigurationUIController;
|
||||||
import jace.config.InvokableAction;
|
import jace.config.InvokableAction;
|
||||||
import jace.config.Reconfigurable;
|
import jace.config.Reconfigurable;
|
||||||
@ -82,6 +82,12 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ConfigurableField(
|
||||||
|
category = "General",
|
||||||
|
name = "Show Drives"
|
||||||
|
)
|
||||||
|
public boolean showDrives = false;
|
||||||
|
|
||||||
public static void updateCPURegisters(MOS65C02 cpu) {
|
public static void updateCPURegisters(MOS65C02 cpu) {
|
||||||
// DebuggerPanel debuggerPanel = Emulator.getFrame().getDebuggerPanel();
|
// DebuggerPanel debuggerPanel = Emulator.getFrame().getDebuggerPanel();
|
||||||
// debuggerPanel.valueA.setText(Integer.toHexString(cpu.A));
|
// debuggerPanel.valueA.setText(Integer.toHexString(cpu.A));
|
||||||
@ -246,7 +252,7 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||||||
name = "Toggle Debug",
|
name = "Toggle Debug",
|
||||||
category = "debug",
|
category = "debug",
|
||||||
description = "Show/hide the debug panel",
|
description = "Show/hide the debug panel",
|
||||||
alternatives = "Show Debug;Hide Debug",
|
alternatives = "Show Debug;Hide Debug;Inspect",
|
||||||
defaultKeyMapping = "ctrl+shift+d")
|
defaultKeyMapping = "ctrl+shift+d")
|
||||||
public static void toggleDebugPanel() {
|
public static void toggleDebugPanel() {
|
||||||
// AbstractEmulatorFrame frame = Emulator.getFrame();
|
// AbstractEmulatorFrame frame = Emulator.getFrame();
|
||||||
@ -262,13 +268,14 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||||||
name = "Toggle fullscreen",
|
name = "Toggle fullscreen",
|
||||||
category = "general",
|
category = "general",
|
||||||
description = "Activate/deactivate fullscreen mode",
|
description = "Activate/deactivate fullscreen mode",
|
||||||
alternatives = "fullscreen,maximize",
|
alternatives = "fullscreen;maximize",
|
||||||
defaultKeyMapping = "ctrl+shift+f")
|
defaultKeyMapping = "ctrl+shift+f")
|
||||||
public static void toggleFullscreen() {
|
public static void toggleFullscreen() {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
Stage stage = JaceApplication.getApplication().primaryStage;
|
Stage stage = JaceApplication.getApplication().primaryStage;
|
||||||
stage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
|
stage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
|
||||||
stage.setFullScreen(!stage.isFullScreen());
|
stage.setFullScreen(!stage.isFullScreen());
|
||||||
|
JaceApplication.getApplication().controller.setAspectRatioEnabled(stage.isFullScreen());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,7 +283,7 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||||||
name = "Save Raw Screenshot",
|
name = "Save Raw Screenshot",
|
||||||
category = "general",
|
category = "general",
|
||||||
description = "Save raw (RAM) format of visible screen",
|
description = "Save raw (RAM) format of visible screen",
|
||||||
alternatives = "screendump, raw screenshot",
|
alternatives = "screendump;raw screenshot",
|
||||||
defaultKeyMapping = "ctrl+shift+z")
|
defaultKeyMapping = "ctrl+shift+z")
|
||||||
public static void saveScreenshotRaw() throws FileNotFoundException, IOException {
|
public static void saveScreenshotRaw() throws FileNotFoundException, IOException {
|
||||||
SimpleDateFormat df = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss");
|
SimpleDateFormat df = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss");
|
||||||
@ -317,7 +324,7 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||||||
name = "Save Screenshot",
|
name = "Save Screenshot",
|
||||||
category = "general",
|
category = "general",
|
||||||
description = "Save image of visible screen",
|
description = "Save image of visible screen",
|
||||||
alternatives = "Save image,save framebuffer,screenshot",
|
alternatives = "Save image;save framebuffer;screenshot",
|
||||||
defaultKeyMapping = "ctrl+shift+s")
|
defaultKeyMapping = "ctrl+shift+s")
|
||||||
public static void saveScreenshot() throws IOException {
|
public static void saveScreenshot() throws IOException {
|
||||||
FileChooser select = new FileChooser();
|
FileChooser select = new FileChooser();
|
||||||
@ -346,7 +353,7 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||||||
name = "Configuration",
|
name = "Configuration",
|
||||||
category = "general",
|
category = "general",
|
||||||
description = "Edit emulator configuraion",
|
description = "Edit emulator configuraion",
|
||||||
alternatives = "Reconfigure,Preferences,Settings",
|
alternatives = "Reconfigure;Preferences;Settings;Config",
|
||||||
defaultKeyMapping = {"f4", "ctrl+shift+c"})
|
defaultKeyMapping = {"f4", "ctrl+shift+c"})
|
||||||
public static void showConfig() {
|
public static void showConfig() {
|
||||||
FXMLLoader fxmlLoader = new FXMLLoader(EmulatorUILogic.class.getResource("/fxml/Configuration.fxml"));
|
FXMLLoader fxmlLoader = new FXMLLoader(EmulatorUILogic.class.getResource("/fxml/Configuration.fxml"));
|
||||||
@ -368,7 +375,7 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||||||
name = "Open IDE",
|
name = "Open IDE",
|
||||||
category = "development",
|
category = "development",
|
||||||
description = "Open new IDE window for Basic/Assembly/Plasma coding",
|
description = "Open new IDE window for Basic/Assembly/Plasma coding",
|
||||||
alternatives = "dev,development,acme,assembler,editor",
|
alternatives = "IDE;dev;development;acme;assembler;editor",
|
||||||
defaultKeyMapping = {"ctrl+shift+i"})
|
defaultKeyMapping = {"ctrl+shift+i"})
|
||||||
public static void showIDE() {
|
public static void showIDE() {
|
||||||
FXMLLoader fxmlLoader = new FXMLLoader(EmulatorUILogic.class.getResource("/fxml/editor.fxml"));
|
FXMLLoader fxmlLoader = new FXMLLoader(EmulatorUILogic.class.getResource("/fxml/editor.fxml"));
|
||||||
@ -392,44 +399,61 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||||||
name = "Resize window",
|
name = "Resize window",
|
||||||
category = "general",
|
category = "general",
|
||||||
description = "Resize the screen to 1x/1.5x/2x/3x video size",
|
description = "Resize the screen to 1x/1.5x/2x/3x video size",
|
||||||
alternatives = "Adjust screen;Adjust window size;Adjust aspect ratio;Fix screen;Fix window size;Fix aspect ratio;Correct aspect ratio;",
|
alternatives = "Aspect;Adjust screen;Adjust window size;Adjust aspect ratio;Fix screen;Fix window size;Fix aspect ratio;Correct aspect ratio;",
|
||||||
defaultKeyMapping = {"ctrl+shift+a"})
|
defaultKeyMapping = {"ctrl+shift+a"})
|
||||||
public static void scaleIntegerRatio() {
|
public static void scaleIntegerRatio() {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
JaceApplication.getApplication().primaryStage.setFullScreen(false);
|
if (JaceApplication.getApplication() == null
|
||||||
|
|| JaceApplication.getApplication().primaryStage == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Stage stage = JaceApplication.getApplication().primaryStage;
|
||||||
size++;
|
size++;
|
||||||
if (size > 3) {
|
if (size > 3) {
|
||||||
size = 0;
|
size = 0;
|
||||||
}
|
}
|
||||||
int width = 0, height = 0;
|
if (stage.isFullScreen()) {
|
||||||
switch (size) {
|
JaceApplication.getApplication().controller.toggleAspectRatio();
|
||||||
case 0: // 1x
|
} else {
|
||||||
width = 560;
|
int width = 0, height = 0;
|
||||||
height = 384;
|
switch (size) {
|
||||||
break;
|
case 0: // 1x
|
||||||
case 1: // 1.5x
|
width = 560;
|
||||||
width = 840;
|
height = 384;
|
||||||
height = 576;
|
break;
|
||||||
break;
|
case 1: // 1.5x
|
||||||
case 2: // 2x
|
width = 840;
|
||||||
width = 560*2;
|
height = 576;
|
||||||
height = 384*2;
|
break;
|
||||||
break;
|
case 2: // 2x
|
||||||
case 3: // 3x (retina) 2880x1800
|
width = 560 * 2;
|
||||||
width = 560*3;
|
height = 384 * 2;
|
||||||
height = 384*3;
|
break;
|
||||||
break;
|
case 3: // 3x (retina) 2880x1800
|
||||||
default: // 2x
|
width = 560 * 3;
|
||||||
width = 560*2;
|
height = 384 * 3;
|
||||||
height = 384*2;
|
break;
|
||||||
|
default: // 2x
|
||||||
|
width = 560 * 2;
|
||||||
|
height = 384 * 2;
|
||||||
|
}
|
||||||
|
double vgap = stage.getScene().getY();
|
||||||
|
double hgap = stage.getScene().getX();
|
||||||
|
stage.setWidth(hgap * 2 + width);
|
||||||
|
stage.setHeight(vgap + height);
|
||||||
}
|
}
|
||||||
Stage stage = JaceApplication.getApplication().primaryStage;
|
|
||||||
double vgap = stage.getScene().getY();
|
|
||||||
double hgap = stage.getScene().getX();
|
|
||||||
stage.setWidth(hgap*2 + width);
|
|
||||||
stage.setHeight(vgap + height);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@InvokableAction(
|
||||||
|
name = "About",
|
||||||
|
category = "general",
|
||||||
|
description = "Display about window",
|
||||||
|
alternatives = "info;credits",
|
||||||
|
defaultKeyMapping = {"ctrl+shift+."})
|
||||||
|
public static void showAboutWindow() {
|
||||||
|
//TODO: Implement
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean confirm(String message) {
|
public static boolean confirm(String message) {
|
||||||
// return JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(Emulator.getFrame(), message);
|
// return JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(Emulator.getFrame(), message);
|
||||||
|
@ -54,14 +54,15 @@ public class JaceApplication extends Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
primaryStage.show();
|
primaryStage.show();
|
||||||
Emulator emulator = new Emulator(getParameters().getRaw());
|
new Thread(() -> {
|
||||||
javafx.application.Platform.runLater(() -> {
|
new Emulator(getParameters().getRaw());
|
||||||
|
reconnectUIHooks();
|
||||||
|
EmulatorUILogic.scaleIntegerRatio();
|
||||||
while (Emulator.computer.getVideo() == null || Emulator.computer.getVideo().getFrameBuffer() == null) {
|
while (Emulator.computer.getVideo() == null || Emulator.computer.getVideo().getFrameBuffer() == null) {
|
||||||
Thread.yield();
|
Thread.yield();
|
||||||
}
|
}
|
||||||
controller.connectComputer(Emulator.computer, primaryStage);
|
|
||||||
bootWatchdog();
|
bootWatchdog();
|
||||||
});
|
}).start();
|
||||||
primaryStage.setOnCloseRequest(event -> {
|
primaryStage.setOnCloseRequest(event -> {
|
||||||
Emulator.computer.deactivate();
|
Emulator.computer.deactivate();
|
||||||
Platform.exit();
|
Platform.exit();
|
||||||
@ -69,6 +70,10 @@ public class JaceApplication extends Application {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void reconnectUIHooks() {
|
||||||
|
controller.connectComputer(Emulator.computer, primaryStage);
|
||||||
|
}
|
||||||
|
|
||||||
public static JaceApplication getApplication() {
|
public static JaceApplication getApplication() {
|
||||||
return singleton;
|
return singleton;
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,10 @@
|
|||||||
package jace;
|
package jace;
|
||||||
|
|
||||||
import com.sun.glass.ui.Application;
|
import com.sun.glass.ui.Application;
|
||||||
import jace.cheat.MetaCheat;
|
|
||||||
import jace.core.Card;
|
import jace.core.Card;
|
||||||
import jace.core.Computer;
|
import jace.core.Computer;
|
||||||
import jace.core.Keyboard;
|
import jace.core.Motherboard;
|
||||||
|
import jace.core.Utility;
|
||||||
import jace.library.MediaCache;
|
import jace.library.MediaCache;
|
||||||
import jace.library.MediaConsumer;
|
import jace.library.MediaConsumer;
|
||||||
import jace.library.MediaConsumerParent;
|
import jace.library.MediaConsumerParent;
|
||||||
@ -30,10 +30,22 @@ import java.util.concurrent.ScheduledFuture;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
import javafx.animation.FadeTransition;
|
||||||
|
import javafx.animation.KeyFrame;
|
||||||
|
import javafx.animation.Timeline;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.beans.binding.NumberBinding;
|
||||||
|
import javafx.beans.binding.When;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.event.EventHandler;
|
import javafx.event.EventHandler;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.Slider;
|
||||||
import javafx.scene.effect.DropShadow;
|
import javafx.scene.effect.DropShadow;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.input.DragEvent;
|
import javafx.scene.input.DragEvent;
|
||||||
@ -43,11 +55,14 @@ import javafx.scene.input.TransferMode;
|
|||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
import javafx.scene.layout.Background;
|
import javafx.scene.layout.Background;
|
||||||
import javafx.scene.layout.BackgroundFill;
|
import javafx.scene.layout.BackgroundFill;
|
||||||
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.scene.layout.CornerRadii;
|
import javafx.scene.layout.CornerRadii;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
import javafx.util.Duration;
|
||||||
|
import javafx.util.StringConverter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -70,29 +85,197 @@ public class JaceUIController {
|
|||||||
@FXML
|
@FXML
|
||||||
private ImageView appleScreen;
|
private ImageView appleScreen;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private BorderPane controlOverlay;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Slider speedSlider;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private AnchorPane menuButtonPane;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button menuButton;
|
||||||
|
|
||||||
Computer computer;
|
Computer computer;
|
||||||
|
|
||||||
|
private final BooleanProperty aspectRatioCorrectionEnabled = new SimpleBooleanProperty(false);
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void initialize() {
|
void initialize() {
|
||||||
assert rootPane != null : "fx:id=\"rootPane\" was not injected: check your FXML file 'JaceUI.fxml'.";
|
assert rootPane != null : "fx:id=\"rootPane\" was not injected: check your FXML file 'JaceUI.fxml'.";
|
||||||
assert stackPane != null : "fx:id=\"stackPane\" was not injected: check your FXML file 'JaceUI.fxml'.";
|
assert stackPane != null : "fx:id=\"stackPane\" was not injected: check your FXML file 'JaceUI.fxml'.";
|
||||||
assert notificationBox != null : "fx:id=\"notificationBox\" was not injected: check your FXML file 'JaceUI.fxml'.";
|
assert notificationBox != null : "fx:id=\"notificationBox\" was not injected: check your FXML file 'JaceUI.fxml'.";
|
||||||
assert appleScreen != null : "fx:id=\"appleScreen\" was not injected: check your FXML file 'JaceUI.fxml'.";
|
assert appleScreen != null : "fx:id=\"appleScreen\" was not injected: check your FXML file 'JaceUI.fxml'.";
|
||||||
appleScreen.fitWidthProperty().bind(rootPane.widthProperty());
|
controlOverlay.setVisible(false);
|
||||||
|
menuButtonPane.setVisible(false);
|
||||||
|
NumberBinding aspectCorrectedWidth = rootPane.heightProperty().multiply(3.0).divide(2.0);
|
||||||
|
NumberBinding width = new When(
|
||||||
|
aspectRatioCorrectionEnabled.and(aspectCorrectedWidth.lessThan(rootPane.widthProperty()))
|
||||||
|
).then(aspectCorrectedWidth).otherwise(rootPane.widthProperty());
|
||||||
|
appleScreen.fitWidthProperty().bind(width);
|
||||||
appleScreen.fitHeightProperty().bind(rootPane.heightProperty());
|
appleScreen.fitHeightProperty().bind(rootPane.heightProperty());
|
||||||
|
appleScreen.setVisible(false);
|
||||||
rootPane.setOnDragEntered(this::processDragEnteredEvent);
|
rootPane.setOnDragEntered(this::processDragEnteredEvent);
|
||||||
rootPane.setOnDragExited(this::processDragExitedEvent);
|
rootPane.setOnDragExited(this::processDragExitedEvent);
|
||||||
|
rootPane.setBackground(new Background(new BackgroundFill(Color.BLACK, null, null)));
|
||||||
|
rootPane.setOnMouseMoved(this::showMenuButton);
|
||||||
|
rootPane.setOnMouseExited(this::hideControlOverlay);
|
||||||
|
menuButton.setOnMouseClicked(this::showControlOverlay);
|
||||||
|
controlOverlay.setOnMouseClicked(this::hideControlOverlay);
|
||||||
|
delayTimer.getKeyFrames().add(new KeyFrame(Duration.millis(3000), evt -> {
|
||||||
|
hideControlOverlay(null);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showMenuButton(MouseEvent evt) {
|
||||||
|
if (!evt.isPrimaryButtonDown() && !evt.isSecondaryButtonDown() && !controlOverlay.isVisible()) {
|
||||||
|
resetMenuButtonTimer();
|
||||||
|
if (!menuButtonPane.isVisible()) {
|
||||||
|
menuButtonPane.setVisible(true);
|
||||||
|
FadeTransition ft = new FadeTransition(Duration.millis(500), menuButtonPane);
|
||||||
|
ft.setFromValue(0.0);
|
||||||
|
ft.setToValue(1.0);
|
||||||
|
ft.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timeline delayTimer = new Timeline();
|
||||||
|
private void resetMenuButtonTimer() {
|
||||||
|
delayTimer.playFromStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showControlOverlay(MouseEvent evt) {
|
||||||
|
if (!evt.isPrimaryButtonDown() && !evt.isSecondaryButtonDown()) {
|
||||||
|
delayTimer.stop();
|
||||||
|
menuButtonPane.setVisible(false);
|
||||||
|
controlOverlay.setVisible(true);
|
||||||
|
FadeTransition ft = new FadeTransition(Duration.millis(500), controlOverlay);
|
||||||
|
ft.setFromValue(0.0);
|
||||||
|
ft.setToValue(1.0);
|
||||||
|
ft.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideControlOverlay(MouseEvent evt) {
|
||||||
|
if (menuButtonPane.isVisible()) {
|
||||||
|
FadeTransition ft1 = new FadeTransition(Duration.millis(500), menuButtonPane);
|
||||||
|
ft1.setFromValue(1.0);
|
||||||
|
ft1.setToValue(0.0);
|
||||||
|
ft1.setOnFinished(evt1 -> menuButtonPane.setVisible(false));
|
||||||
|
ft1.play();
|
||||||
|
}
|
||||||
|
if (controlOverlay.isVisible()) {
|
||||||
|
FadeTransition ft2 = new FadeTransition(Duration.millis(500), controlOverlay);
|
||||||
|
ft2.setFromValue(1.0);
|
||||||
|
ft2.setToValue(0.0);
|
||||||
|
ft2.setOnFinished(evt1 -> controlOverlay.setVisible(false));
|
||||||
|
ft2.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private double convertSpeedToRatio(Double setting) {
|
||||||
|
if (setting < 1.0) {
|
||||||
|
return 0.5;
|
||||||
|
} else if (setting == 1.0) {
|
||||||
|
return 1.0;
|
||||||
|
} else if (setting >= 10) {
|
||||||
|
return Double.MAX_VALUE;
|
||||||
|
} else {
|
||||||
|
double val = Math.pow(2.0, (setting - 1.0) / 1.5);
|
||||||
|
val = Math.floor(val * 2.0) / 2.0;
|
||||||
|
if (val > 2.0) {
|
||||||
|
val = Math.floor(val);
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void connectControls(Stage primaryStage) {
|
||||||
|
connectButtons(controlOverlay);
|
||||||
|
if (computer.getKeyboard() != null) {
|
||||||
|
EventHandler<KeyEvent> keyboardHandler = computer.getKeyboard().getListener();
|
||||||
|
primaryStage.setOnShowing(evt -> computer.getKeyboard().resetState());
|
||||||
|
rootPane.setOnKeyPressed(keyboardHandler);
|
||||||
|
rootPane.setOnKeyReleased(keyboardHandler);
|
||||||
|
rootPane.setFocusTraversable(true);
|
||||||
|
}
|
||||||
|
speedSlider.setValue(1.0);
|
||||||
|
speedSlider.setMinorTickCount(0);
|
||||||
|
speedSlider.setMajorTickUnit(1);
|
||||||
|
speedSlider.setLabelFormatter(new StringConverter<Double>() {
|
||||||
|
@Override
|
||||||
|
public String toString(Double val) {
|
||||||
|
if (val < 1.0) {
|
||||||
|
return "Half";
|
||||||
|
} else if (val >= 10.0) {
|
||||||
|
return "∞";
|
||||||
|
}
|
||||||
|
double v = convertSpeedToRatio(val);
|
||||||
|
if (v != Math.floor(v)) {
|
||||||
|
return String.valueOf(v) + "x";
|
||||||
|
} else {
|
||||||
|
return String.valueOf((int) v) + "x";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Double fromString(String string) {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
speedSlider.valueProperty().addListener((val, oldValue, newValue) -> setSpeed(newValue.doubleValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void connectButtons(Node n) {
|
||||||
|
if (n instanceof Button) {
|
||||||
|
Button button = (Button) n;
|
||||||
|
Runnable action = Utility.getNamedInvokableAction(button.getText());
|
||||||
|
button.setOnMouseClicked(evt -> action.run());
|
||||||
|
} else if (n instanceof Parent) {
|
||||||
|
for (Node child : ((Parent) n).getChildrenUnmodifiable()) {
|
||||||
|
connectButtons(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setSpeed(double speed) {
|
||||||
|
double speedRatio = convertSpeedToRatio(speed);
|
||||||
|
if (speedRatio > 100.0) {
|
||||||
|
Emulator.computer.getMotherboard().maxspeed = true;
|
||||||
|
Motherboard.cpuPerClock = 3;
|
||||||
|
} else {
|
||||||
|
if (speedRatio > 25) {
|
||||||
|
Motherboard.cpuPerClock = 2;
|
||||||
|
} else {
|
||||||
|
Motherboard.cpuPerClock = 1;
|
||||||
|
}
|
||||||
|
Emulator.computer.getMotherboard().maxspeed = false;
|
||||||
|
Emulator.computer.getMotherboard().speedRatio = (int) (speedRatio * 100);
|
||||||
|
}
|
||||||
|
Emulator.computer.getMotherboard().reconfigure();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void toggleAspectRatio() {
|
||||||
|
setAspectRatioEnabled(aspectRatioCorrectionEnabled.not().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAspectRatioEnabled(boolean enabled) {
|
||||||
|
aspectRatioCorrectionEnabled.set(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connectComputer(Computer computer, Stage primaryStage) {
|
public void connectComputer(Computer computer, Stage primaryStage) {
|
||||||
|
if (computer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.computer = computer;
|
this.computer = computer;
|
||||||
appleScreen.setImage(computer.getVideo().getFrameBuffer());
|
Platform.runLater(() -> {
|
||||||
EventHandler<KeyEvent> keyboardHandler = computer.getKeyboard().getListener();
|
connectControls(primaryStage);
|
||||||
primaryStage.setOnShowing(evt -> computer.getKeyboard().resetState());
|
appleScreen.setImage(computer.getVideo().getFrameBuffer());
|
||||||
rootPane.setFocusTraversable(true);
|
appleScreen.setVisible(true);
|
||||||
rootPane.setOnKeyPressed(keyboardHandler);
|
rootPane.requestFocus();
|
||||||
rootPane.setOnKeyReleased(keyboardHandler);
|
});
|
||||||
rootPane.requestFocus();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processDragEnteredEvent(DragEvent evt) {
|
private void processDragEnteredEvent(DragEvent evt) {
|
||||||
@ -245,24 +428,25 @@ public class JaceUIController {
|
|||||||
public void removeMouseListener(EventHandler<MouseEvent> handler) {
|
public void removeMouseListener(EventHandler<MouseEvent> handler) {
|
||||||
appleScreen.removeEventHandler(MouseEvent.ANY, handler);
|
appleScreen.removeEventHandler(MouseEvent.ANY, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
Label currentNotification = null;
|
Label currentNotification = null;
|
||||||
|
|
||||||
public void displayNotification(String message) {
|
public void displayNotification(String message) {
|
||||||
Label oldNotification = currentNotification;
|
Label oldNotification = currentNotification;
|
||||||
Label notification = new Label(message);
|
Label notification = new Label(message);
|
||||||
currentNotification = notification;
|
currentNotification = notification;
|
||||||
notification.setEffect(new DropShadow(2.0, Color.BLACK));
|
notification.setEffect(new DropShadow(2.0, Color.BLACK));
|
||||||
notification.setTextFill(Color.WHITE);
|
notification.setTextFill(Color.WHITE);
|
||||||
notification.setBackground(new Background(new BackgroundFill(Color.rgb(0,0,80, 0.7), new CornerRadii(5.0), new Insets(-5.0))));
|
notification.setBackground(new Background(new BackgroundFill(Color.rgb(0, 0, 80, 0.7), new CornerRadii(5.0), new Insets(-5.0))));
|
||||||
Application.invokeLater(() -> {
|
Application.invokeLater(() -> {
|
||||||
stackPane.getChildren().remove(oldNotification);
|
stackPane.getChildren().remove(oldNotification);
|
||||||
stackPane.getChildren().add(notification);
|
stackPane.getChildren().add(notification);
|
||||||
});
|
});
|
||||||
|
|
||||||
notificationExecutor.schedule(()->{
|
notificationExecutor.schedule(() -> {
|
||||||
Application.invokeLater(() -> {
|
Application.invokeLater(() -> {
|
||||||
stackPane.getChildren().remove(notification);
|
stackPane.getChildren().remove(notification);
|
||||||
});
|
});
|
||||||
}, 4, TimeUnit.SECONDS);
|
}, 4, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
package jace.apple2e;
|
package jace.apple2e;
|
||||||
|
|
||||||
import jace.Emulator;
|
import jace.Emulator;
|
||||||
|
import jace.JaceApplication;
|
||||||
|
import jace.apple2e.softswitch.VideoSoftSwitch;
|
||||||
import jace.cheat.Cheats;
|
import jace.cheat.Cheats;
|
||||||
import jace.config.ClassSelection;
|
import jace.config.ClassSelection;
|
||||||
import jace.config.ConfigurableField;
|
import jace.config.ConfigurableField;
|
||||||
@ -40,21 +42,22 @@ import jace.hardware.massStorage.CardMassStorage;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apple2e is a computer with a 65c02 CPU, 128k of bankswitched ram,
|
* Apple2e is a computer with a 65c02 CPU, 128k of bankswitched ram, double-hires graphics, and up to seven peripheral
|
||||||
* double-hires graphics, and up to seven peripheral I/O cards installed. Pause
|
* I/O cards installed. Pause and resume are implemented by the Motherboard class. This class provides overall
|
||||||
* and resume are implemented by the Motherboard class. This class provides
|
* configuration of the computer, but the actual operation of the computer and its timing characteristics are managed in
|
||||||
* overall configuration of the computer, but the actual operation of the
|
* the Motherboard class.
|
||||||
* computer and its timing characteristics are managed in the Motherboard class.
|
|
||||||
*
|
*
|
||||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||||
*/
|
*/
|
||||||
@ -121,7 +124,7 @@ public class Apple2e extends Computer {
|
|||||||
return "Computer (Apple //e)";
|
return "Computer (Apple //e)";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reinitMotherboard() {
|
protected void reinitMotherboard() {
|
||||||
if (motherboard != null && motherboard.isRunning()) {
|
if (motherboard != null && motherboard.isRunning()) {
|
||||||
motherboard.suspend();
|
motherboard.suspend();
|
||||||
}
|
}
|
||||||
@ -134,16 +137,25 @@ public class Apple2e extends Computer {
|
|||||||
public void coldStart() {
|
public void coldStart() {
|
||||||
pause();
|
pause();
|
||||||
reinitMotherboard();
|
reinitMotherboard();
|
||||||
|
RAM128k ram = (RAM128k) getMemory();
|
||||||
|
ram.initMemoryPattern(ram.mainMemory);
|
||||||
|
ram.initMemoryPattern(ram.getAuxMemory());
|
||||||
for (SoftSwitches s : SoftSwitches.values()) {
|
for (SoftSwitches s : SoftSwitches.values()) {
|
||||||
s.getSwitch().reset();
|
s.getSwitch().reset();
|
||||||
}
|
}
|
||||||
getMemory().configureActiveMemory();
|
getMemory().configureActiveMemory();
|
||||||
getVideo().configureVideoMode();
|
getVideo().configureVideoMode();
|
||||||
for (Optional<Card> c : getMemory().getAllCards()) {
|
try {
|
||||||
c.ifPresent(Card::reset);
|
for (Optional<Card> c : getMemory().getAllCards()) {
|
||||||
|
c.ifPresent(Card::reset);
|
||||||
|
waitForVBL();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
} finally {
|
||||||
|
getCpu().resume();
|
||||||
|
reboot();
|
||||||
}
|
}
|
||||||
reboot();
|
|
||||||
resume();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reboot() {
|
public void reboot() {
|
||||||
@ -158,7 +170,9 @@ public class Apple2e extends Computer {
|
|||||||
public void warmStart() {
|
public void warmStart() {
|
||||||
boolean restart = pause();
|
boolean restart = pause();
|
||||||
for (SoftSwitches s : SoftSwitches.values()) {
|
for (SoftSwitches s : SoftSwitches.values()) {
|
||||||
s.getSwitch().reset();
|
if (!(s.getSwitch() instanceof VideoSoftSwitch)) {
|
||||||
|
s.getSwitch().reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
getMemory().configureActiveMemory();
|
getMemory().configureActiveMemory();
|
||||||
getVideo().configureVideoMode();
|
getVideo().configureVideoMode();
|
||||||
@ -194,11 +208,11 @@ public class Apple2e extends Computer {
|
|||||||
@Override
|
@Override
|
||||||
public final void reconfigure() {
|
public final void reconfigure() {
|
||||||
boolean restart = pause();
|
boolean restart = pause();
|
||||||
|
|
||||||
if (Utility.isHeadlessMode()) {
|
if (Utility.isHeadlessMode()) {
|
||||||
joy1enabled = false;
|
joy1enabled = false;
|
||||||
joy2enabled = false;
|
joy2enabled = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
super.reconfigure();
|
super.reconfigure();
|
||||||
@ -216,9 +230,7 @@ public class Apple2e extends Computer {
|
|||||||
if (getMemory() == null) {
|
if (getMemory() == null) {
|
||||||
try {
|
try {
|
||||||
currentMemory = (RAM128k) ramCard.getValue().getConstructor(Computer.class).newInstance(this);
|
currentMemory = (RAM128k) ramCard.getValue().getConstructor(Computer.class).newInstance(this);
|
||||||
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException ex) {
|
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException ex) {
|
||||||
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
|
|
||||||
} catch (IllegalArgumentException | InvocationTargetException ex) {
|
|
||||||
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
|
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@ -291,6 +303,9 @@ public class Apple2e extends Computer {
|
|||||||
getVideo().configureVideoMode();
|
getVideo().configureVideoMode();
|
||||||
getVideo().reconfigure();
|
getVideo().reconfigure();
|
||||||
Emulator.resizeVideo();
|
Emulator.resizeVideo();
|
||||||
|
if (JaceApplication.getApplication() != null) {
|
||||||
|
JaceApplication.getApplication().reconnectUIHooks();
|
||||||
|
}
|
||||||
getVideo().resume();
|
getVideo().resume();
|
||||||
} catch (InstantiationException | IllegalAccessException ex) {
|
} catch (InstantiationException | IllegalAccessException ex) {
|
||||||
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
|
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
@ -377,6 +392,35 @@ public class Apple2e extends Computer {
|
|||||||
// }
|
// }
|
||||||
private List<RAMListener> hints = new ArrayList<>();
|
private List<RAMListener> hints = new ArrayList<>();
|
||||||
|
|
||||||
|
List<Runnable> vblCallbacks = Collections.synchronizedList(new ArrayList<>());
|
||||||
|
|
||||||
|
public void waitForVBL() throws InterruptedException {
|
||||||
|
waitForVBL(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void waitForVBL(int count) throws InterruptedException {
|
||||||
|
Semaphore s = new Semaphore(0);
|
||||||
|
onNextVBL(s::release);
|
||||||
|
s.acquire();
|
||||||
|
if (count > 1) {
|
||||||
|
waitForVBL(count - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onNextVBL(Runnable r) {
|
||||||
|
vblCallbacks.add(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void notifyVBLStateChanged(boolean state) {
|
||||||
|
super.notifyVBLStateChanged(state);
|
||||||
|
if (state) {
|
||||||
|
while (vblCallbacks != null && !vblCallbacks.isEmpty()) {
|
||||||
|
vblCallbacks.remove(0).run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ScheduledExecutorService animationTimer = new ScheduledThreadPoolExecutor(1);
|
ScheduledExecutorService animationTimer = new ScheduledThreadPoolExecutor(1);
|
||||||
Runnable drawHints = () -> {
|
Runnable drawHints = () -> {
|
||||||
if (getCpu().getProgramCounter() >> 8 != 0x0c6) {
|
if (getCpu().getProgramCounter() >> 8 != 0x0c6) {
|
||||||
@ -438,10 +482,10 @@ public class Apple2e extends Computer {
|
|||||||
|
|
||||||
private void enableHints() {
|
private void enableHints() {
|
||||||
if (hints.isEmpty()) {
|
if (hints.isEmpty()) {
|
||||||
hints.add(getMemory().observe(RAMEvent.TYPE.EXECUTE, 0x0FB63, (e)->{
|
hints.add(getMemory().observe(RAMEvent.TYPE.EXECUTE, 0x0FB63, (e) -> {
|
||||||
animationTimer.schedule(drawHints, 1, TimeUnit.SECONDS);
|
animationTimer.schedule(drawHints, 1, TimeUnit.SECONDS);
|
||||||
animationSchedule =
|
animationSchedule
|
||||||
animationTimer.scheduleAtFixedRate(doAnimation, 1250, 100, TimeUnit.MILLISECONDS);
|
= animationTimer.scheduleAtFixedRate(doAnimation, 1250, 100, TimeUnit.MILLISECONDS);
|
||||||
}));
|
}));
|
||||||
// Latch to the PRODOS SYNTAX CHECK parser
|
// Latch to the PRODOS SYNTAX CHECK parser
|
||||||
/*
|
/*
|
||||||
@ -475,4 +519,4 @@ public class Apple2e extends Computer {
|
|||||||
public String getShortName() {
|
public String getShortName() {
|
||||||
return "computer";
|
return "computer";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,14 +57,14 @@ public class VideoNTSC extends VideoDHGR {
|
|||||||
public boolean enableVideo7 = true;
|
public boolean enableVideo7 = true;
|
||||||
// Scanline represents 560 bits, divided up into 28-bit words
|
// Scanline represents 560 bits, divided up into 28-bit words
|
||||||
int[] scanline = new int[20];
|
int[] scanline = new int[20];
|
||||||
static int[] divBy28 = new int[560];
|
static public int[] divBy28 = new int[560];
|
||||||
|
|
||||||
static {
|
static {
|
||||||
for (int i = 0; i < 560; i++) {
|
for (int i = 0; i < 560; i++) {
|
||||||
divBy28[i] = i / 28;
|
divBy28[i] = i / 28;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
boolean[] colorActive = new boolean[80];
|
protected boolean[] colorActive = new boolean[80];
|
||||||
int rowStart = 0;
|
int rowStart = 0;
|
||||||
|
|
||||||
public VideoNTSC(Computer computer) {
|
public VideoNTSC(Computer computer) {
|
||||||
@ -89,7 +89,7 @@ public class VideoNTSC extends VideoDHGR {
|
|||||||
static int currentMode = -1;
|
static int currentMode = -1;
|
||||||
@InvokableAction(name = "Toggle video mode",
|
@InvokableAction(name = "Toggle video mode",
|
||||||
category = "video",
|
category = "video",
|
||||||
alternatives = "mode,color,b&w,monochrome",
|
alternatives = "Gfx mode;color;b&w;monochrome",
|
||||||
defaultKeyMapping = {"ctrl+shift+g"})
|
defaultKeyMapping = {"ctrl+shift+g"})
|
||||||
public static void changeVideoMode() {
|
public static void changeVideoMode() {
|
||||||
VideoNTSC thiss = (VideoNTSC) Emulator.computer.video;
|
VideoNTSC thiss = (VideoNTSC) Emulator.computer.video;
|
||||||
|
@ -41,7 +41,7 @@ public abstract class Cheats extends Device {
|
|||||||
super(computer);
|
super(computer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@InvokableAction(name = "Toggle Cheats", alternatives = "cheat", defaultKeyMapping = "ctrl+shift+m")
|
@InvokableAction(name = "Toggle Cheats", alternatives = "cheat;Plug-in", defaultKeyMapping = "ctrl+shift+m")
|
||||||
public void toggleCheats() {
|
public void toggleCheats() {
|
||||||
cheatsActive = !cheatsActive;
|
cheatsActive = !cheatsActive;
|
||||||
if (cheatsActive) {
|
if (cheatsActive) {
|
||||||
@ -97,7 +97,7 @@ public abstract class Cheats extends Device {
|
|||||||
super.detach();
|
super.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract void registerListeners();
|
public abstract void registerListeners();
|
||||||
|
|
||||||
protected void unregisterListeners() {
|
protected void unregisterListeners() {
|
||||||
listeners.stream().forEach((l) -> {
|
listeners.stream().forEach((l) -> {
|
||||||
|
@ -134,7 +134,7 @@ public class MetaCheat extends Cheats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void registerListeners() {
|
public void registerListeners() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addCheat(DynamicCheat cheat) {
|
public void addCheat(DynamicCheat cheat) {
|
||||||
|
@ -70,7 +70,7 @@ public class MontezumasRevengeCheats extends Cheats {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void registerListeners() {
|
public void registerListeners() {
|
||||||
RAM memory = Emulator.computer.memory;
|
RAM memory = Emulator.computer.memory;
|
||||||
if (repulsiveHack) {
|
if (repulsiveHack) {
|
||||||
addCheat(RAMEvent.TYPE.WRITE, this::repulsiveBehavior, 0x1508, 0x1518);
|
addCheat(RAMEvent.TYPE.WRITE, this::repulsiveBehavior, 0x1508, 0x1518);
|
||||||
|
@ -49,6 +49,7 @@ import java.util.Set;
|
|||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
import java.util.stream.Stream;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.scene.control.TreeItem;
|
import javafx.scene.control.TreeItem;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
@ -255,6 +256,12 @@ public class Configuration implements Reconfigurable {
|
|||||||
getChangedIcon().ifPresent(this::setGraphic);
|
getChangedIcon().ifPresent(this::setGraphic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stream<ConfigNode> getTreeAsStream() {
|
||||||
|
return Stream.concat(
|
||||||
|
Stream.of(this),
|
||||||
|
children.stream().flatMap(ConfigNode::getTreeAsStream));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public static ConfigNode BASE;
|
public static ConfigNode BASE;
|
||||||
public static EmulatorUILogic ui = Emulator.logic;
|
public static EmulatorUILogic ui = Emulator.logic;
|
||||||
@ -301,7 +308,7 @@ public class Configuration implements Reconfigurable {
|
|||||||
node.setRawFieldValue(f.getName(), (Serializable) o);
|
node.setRawFieldValue(f.getName(), (Serializable) o);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (o == null) {
|
if (o == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -494,7 +501,9 @@ public class Configuration implements Reconfigurable {
|
|||||||
newRoot.getChildren().stream().forEach((child) -> {
|
newRoot.getChildren().stream().forEach((child) -> {
|
||||||
String childName = child.toString();
|
String childName = child.toString();
|
||||||
ConfigNode oldChild = oldRoot.findChild(childName);
|
ConfigNode oldChild = oldRoot.findChild(childName);
|
||||||
if (oldChild == null) {oldChild = oldRoot.findChild(child.id);}
|
if (oldChild == null) {
|
||||||
|
oldChild = oldRoot.findChild(child.id);
|
||||||
|
}
|
||||||
// System.out.println("Applying settings for " + childName);
|
// System.out.println("Applying settings for " + childName);
|
||||||
applyConfigTree(child, oldChild);
|
applyConfigTree(child, oldChild);
|
||||||
});
|
});
|
||||||
|
@ -128,10 +128,18 @@ public abstract class Computer implements Reconfigurable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void deactivate() {
|
public void deactivate() {
|
||||||
cpu.suspend();
|
if (cpu != null) {
|
||||||
motherboard.suspend();
|
cpu.suspend();
|
||||||
video.suspend();
|
}
|
||||||
mixer.detach();
|
if (motherboard != null) {
|
||||||
|
motherboard.suspend();
|
||||||
|
}
|
||||||
|
if (video != null) {
|
||||||
|
video.suspend();
|
||||||
|
}
|
||||||
|
if (mixer != null) {
|
||||||
|
mixer.detach();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@InvokableAction(
|
@InvokableAction(
|
||||||
@ -161,7 +169,7 @@ public abstract class Computer implements Reconfigurable {
|
|||||||
name = "Warm boot",
|
name = "Warm boot",
|
||||||
description = "Process user-initatiated reboot (ctrl+apple+reset)",
|
description = "Process user-initatiated reboot (ctrl+apple+reset)",
|
||||||
category = "general",
|
category = "general",
|
||||||
alternatives = "reboot;reset;three-finger-salute",
|
alternatives = "reboot;reset;three-finger-salute;restart",
|
||||||
defaultKeyMapping = {"Ctrl+Ignore Alt+Ignore Meta+Backspace", "Ctrl+Ignore Alt+Ignore Meta+Delete"})
|
defaultKeyMapping = {"Ctrl+Ignore Alt+Ignore Meta+Backspace", "Ctrl+Ignore Alt+Ignore Meta+Delete"})
|
||||||
public void invokeWarmStart() {
|
public void invokeWarmStart() {
|
||||||
warmStart();
|
warmStart();
|
||||||
@ -185,7 +193,7 @@ public abstract class Computer implements Reconfigurable {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@InvokableAction(name = "Resume", description = "Resumes the computer if it was previously paused", alternatives = "unpause;unfreeze;resume", defaultKeyMapping = {"meta+shift+pause", "alt+shift+pause"})
|
@InvokableAction(name = "Resume", description = "Resumes the computer if it was previously paused", alternatives = "unpause;unfreeze;resume;play", defaultKeyMapping = {"meta+shift+pause", "alt+shift+pause"})
|
||||||
public void resume() {
|
public void resume() {
|
||||||
doResume();
|
doResume();
|
||||||
getRunningProperty().set(true);
|
getRunningProperty().set(true);
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package jace.core;
|
package jace.core;
|
||||||
|
|
||||||
|
import jace.Emulator;
|
||||||
import jace.apple2e.SoftSwitches;
|
import jace.apple2e.SoftSwitches;
|
||||||
import jace.config.InvokableAction;
|
import jace.config.InvokableAction;
|
||||||
import jace.config.Reconfigurable;
|
import jace.config.Reconfigurable;
|
||||||
@ -68,6 +69,7 @@ public class Keyboard implements Reconfigurable {
|
|||||||
return "kbd";
|
return "kbd";
|
||||||
}
|
}
|
||||||
static byte currentKey = 0;
|
static byte currentKey = 0;
|
||||||
|
public boolean shiftPressed = false;
|
||||||
|
|
||||||
public static void clearStrobe() {
|
public static void clearStrobe() {
|
||||||
currentKey = (byte) (currentKey & 0x07f);
|
currentKey = (byte) (currentKey & 0x07f);
|
||||||
@ -102,6 +104,7 @@ public class Keyboard implements Reconfigurable {
|
|||||||
registerKeyHandler(new KeyHandler(code) {
|
registerKeyHandler(new KeyHandler(code) {
|
||||||
@Override
|
@Override
|
||||||
public boolean handleKeyUp(KeyEvent e) {
|
public boolean handleKeyUp(KeyEvent e) {
|
||||||
|
Emulator.computer.getKeyboard().shiftPressed = e.isShiftDown();
|
||||||
if (action == null || !action.notifyOnRelease()) {
|
if (action == null || !action.notifyOnRelease()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -125,6 +128,7 @@ public class Keyboard implements Reconfigurable {
|
|||||||
@Override
|
@Override
|
||||||
public boolean handleKeyDown(KeyEvent e) {
|
public boolean handleKeyDown(KeyEvent e) {
|
||||||
// System.out.println("Key down: "+method.toString());
|
// System.out.println("Key down: "+method.toString());
|
||||||
|
Emulator.computer.getKeyboard().shiftPressed = e.isShiftDown();
|
||||||
Object returnValue = null;
|
Object returnValue = null;
|
||||||
try {
|
try {
|
||||||
if (method.getParameterCount() > 0) {
|
if (method.getParameterCount() > 0) {
|
||||||
@ -248,6 +252,7 @@ public class Keyboard implements Reconfigurable {
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Emulator.computer.getKeyboard().shiftPressed = e.isShiftDown();
|
||||||
if (e.isShiftDown()) {
|
if (e.isShiftDown()) {
|
||||||
c = fixShiftedChar(c);
|
c = fixShiftedChar(c);
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,8 @@ public abstract class TimedDevice extends Device {
|
|||||||
super(computer);
|
super(computer);
|
||||||
setSpeed(cyclesPerSecond);
|
setSpeed(cyclesPerSecond);
|
||||||
}
|
}
|
||||||
@ConfigurableField(name = "Speed", description = "(in hertz)")
|
@ConfigurableField(name = "Speed", description = "(Percentage)")
|
||||||
|
public int speedRatio = 100;
|
||||||
public long cyclesPerSecond = defaultCyclesPerSecond();
|
public long cyclesPerSecond = defaultCyclesPerSecond();
|
||||||
@ConfigurableField(name = "Max speed")
|
@ConfigurableField(name = "Max speed")
|
||||||
public boolean maxspeed = false;
|
public boolean maxspeed = false;
|
||||||
@ -170,6 +171,7 @@ public abstract class TimedDevice extends Device {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reconfigure() {
|
public void reconfigure() {
|
||||||
|
cyclesPerSecond = defaultCyclesPerSecond() * speedRatio / 100;
|
||||||
if (cyclesPerSecond == 0) {
|
if (cyclesPerSecond == 0) {
|
||||||
cyclesPerSecond = defaultCyclesPerSecond();
|
cyclesPerSecond = defaultCyclesPerSecond();
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
package jace.core;
|
package jace.core;
|
||||||
|
|
||||||
|
import jace.config.Configuration;
|
||||||
|
import jace.config.InvokableAction;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
@ -36,15 +38,18 @@ import java.util.Set;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.Alert;
|
import javafx.scene.control.Alert;
|
||||||
|
import javafx.scene.control.ButtonType;
|
||||||
import javafx.scene.control.ContentDisplay;
|
import javafx.scene.control.ContentDisplay;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.effect.DropShadow;
|
import javafx.scene.effect.DropShadow;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a set of helper functions which do not belong anywhere else.
|
* This is a set of helper functions which do not belong anywhere else.
|
||||||
* Functions vary from introspection, discovery, and string/pattern matching.
|
* Functions vary from introspection, discovery, and string/pattern matching.
|
||||||
@ -52,7 +57,9 @@ import javafx.scene.paint.Color;
|
|||||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||||
*/
|
*/
|
||||||
public class Utility {
|
public class Utility {
|
||||||
|
|
||||||
static Reflections reflections = new Reflections("jace");
|
static Reflections reflections = new Reflections("jace");
|
||||||
|
|
||||||
public static Set<Class> findAllSubclasses(Class clazz) {
|
public static Set<Class> findAllSubclasses(Class clazz) {
|
||||||
return reflections.getSubTypesOf(clazz);
|
return reflections.getSubTypesOf(clazz);
|
||||||
}
|
}
|
||||||
@ -65,11 +72,11 @@ public class Utility {
|
|||||||
*
|
*
|
||||||
* @param s
|
* @param s
|
||||||
* @param t
|
* @param t
|
||||||
* @return Distance (higher is better)
|
* @return Distance (lower means a closer match, zero is identical)
|
||||||
*/
|
*/
|
||||||
public static int levenshteinDistance(String s, String t) {
|
public static int levenshteinDistance(String s, String t) {
|
||||||
if (s == null || t == null || s.length() == 0 || t.length() == 0) {
|
if (s == null || t == null || s.length() == 0 || t.length() == 0) {
|
||||||
return -1;
|
return Integer.MAX_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
s = s.toLowerCase().replaceAll("[^a-zA-Z0-9\\s]", "");
|
s = s.toLowerCase().replaceAll("[^a-zA-Z0-9\\s]", "");
|
||||||
@ -95,8 +102,19 @@ public class Utility {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Math.max(m, n) - dist[m][n];
|
return dist[m][n];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize distance based on longest string
|
||||||
|
* @param s
|
||||||
|
* @param t
|
||||||
|
* @return Similarity ranking, higher is better
|
||||||
|
*/
|
||||||
|
public static int adjustedLevenshteinDistance(String s, String t) {
|
||||||
|
return Math.max(s.length(), t.length()) - levenshteinDistance(s, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compare strings based on a tally of similar patterns found, using a fixed
|
* Compare strings based on a tally of similar patterns found, using a fixed
|
||||||
@ -107,7 +125,7 @@ public class Utility {
|
|||||||
* @param c1
|
* @param c1
|
||||||
* @param c2
|
* @param c2
|
||||||
* @param width Search window size
|
* @param width Search window size
|
||||||
* @return Overall similarity score (higher is beter)
|
* @return Overall similarity score (higher is better)
|
||||||
*/
|
*/
|
||||||
public static double rankMatch(String c1, String c2, int width) {
|
public static double rankMatch(String c1, String c2, int width) {
|
||||||
double score = 0;
|
double score = 0;
|
||||||
@ -135,6 +153,7 @@ public class Utility {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isHeadless = false;
|
private static boolean isHeadless = false;
|
||||||
|
|
||||||
public static void setHeadlessMode(boolean headless) {
|
public static void setHeadlessMode(boolean headless) {
|
||||||
isHeadless = headless;
|
isHeadless = headless;
|
||||||
}
|
}
|
||||||
@ -142,7 +161,7 @@ public class Utility {
|
|||||||
public static boolean isHeadlessMode() {
|
public static boolean isHeadlessMode() {
|
||||||
return isHeadless;
|
return isHeadless;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Optional<Image> loadIcon(String filename) {
|
public static Optional<Image> loadIcon(String filename) {
|
||||||
if (isHeadless) {
|
if (isHeadless) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
@ -181,6 +200,20 @@ public class Utility {
|
|||||||
return Optional.of(label);
|
return Optional.of(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void confirm(String title, String message, Runnable accept) {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
Alert confirm = new Alert(Alert.AlertType.CONFIRMATION);
|
||||||
|
confirm.setContentText(message);
|
||||||
|
confirm.setTitle(title);
|
||||||
|
Optional<ButtonType> response = confirm.showAndWait();
|
||||||
|
response.ifPresent(b -> {
|
||||||
|
if (b.getButtonData().isDefaultButton()) {
|
||||||
|
(new Thread(accept)).start();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// public static void runModalProcess(String title, final Runnable runnable) {
|
// public static void runModalProcess(String title, final Runnable runnable) {
|
||||||
//// final JDialog frame = new JDialog(Emulator.getFrame());
|
//// final JDialog frame = new JDialog(Emulator.getFrame());
|
||||||
// final JProgressBar progressBar = new JProgressBar();
|
// final JProgressBar progressBar = new JProgressBar();
|
||||||
@ -201,7 +234,6 @@ public class Utility {
|
|||||||
// frame.dispose();
|
// frame.dispose();
|
||||||
// }).start();
|
// }).start();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
public static class RankingComparator implements Comparator<String> {
|
public static class RankingComparator implements Comparator<String> {
|
||||||
|
|
||||||
String match;
|
String match;
|
||||||
@ -215,8 +247,8 @@ public class Utility {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compare(String o1, String o2) {
|
public int compare(String o1, String o2) {
|
||||||
double s1 = levenshteinDistance(match, o1);
|
double s1 = adjustedLevenshteinDistance(match, o1);
|
||||||
double s2 = levenshteinDistance(match, o2);
|
double s2 = adjustedLevenshteinDistance(match, o2);
|
||||||
if (s2 == s1) {
|
if (s2 == s1) {
|
||||||
s1 = rankMatch(o1, match, 3) + rankMatch(o1, match, 2);
|
s1 = rankMatch(o1, match, 3) + rankMatch(o1, match, 2);
|
||||||
s2 = rankMatch(o2, match, 3) + rankMatch(o2, match, 2);
|
s2 = rankMatch(o2, match, 3) + rankMatch(o2, match, 2);
|
||||||
@ -256,7 +288,7 @@ public class Utility {
|
|||||||
// System.out.println(match + "->" + c + ":" + l + " -- "+ m2 + "," + m3 + "," + "(" + (m2 + m3) + ")");
|
// System.out.println(match + "->" + c + ":" + l + " -- "+ m2 + "," + m3 + "," + "(" + (m2 + m3) + ")");
|
||||||
// }
|
// }
|
||||||
// double score = rankMatch(match, candidates.get(0), 2);
|
// double score = rankMatch(match, candidates.get(0), 2);
|
||||||
double score = levenshteinDistance(match, candidates.get(0));
|
double score = adjustedLevenshteinDistance(match, candidates.get(0));
|
||||||
if (score > 1) {
|
if (score > 1) {
|
||||||
return candidates.get(0);
|
return candidates.get(0);
|
||||||
}
|
}
|
||||||
@ -450,4 +482,47 @@ public class Utility {
|
|||||||
}
|
}
|
||||||
return setChild(object, paths[paths.length - 1], value, hex);
|
return setChild(object, paths[paths.length - 1], value, hex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Map<InvokableAction, Runnable> allActions = null;
|
||||||
|
|
||||||
|
public static Map<InvokableAction, Runnable> getAllInvokableActions() {
|
||||||
|
if (allActions == null) {
|
||||||
|
allActions = new HashMap<>();
|
||||||
|
Configuration.BASE.getTreeAsStream().forEach((Configuration.ConfigNode node) -> {
|
||||||
|
for (Method m : node.subject.getClass().getMethods()) {
|
||||||
|
if (m.isAnnotationPresent(InvokableAction.class)) {
|
||||||
|
allActions.put(m.getAnnotation(InvokableAction.class), () -> {
|
||||||
|
try {
|
||||||
|
m.invoke(node.subject);
|
||||||
|
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
|
||||||
|
Logger.getLogger(Utility.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return allActions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Runnable getNamedInvokableAction(String action) {
|
||||||
|
Map<InvokableAction, Runnable> actions = getAllInvokableActions();
|
||||||
|
List<InvokableAction> actionsList = new ArrayList(actions.keySet());
|
||||||
|
actionsList.sort((a,b) -> Integer.compare(getActionNameMatch(action, a), getActionNameMatch(action, b)));
|
||||||
|
// for (InvokableAction a : actionsList) {
|
||||||
|
// String actionName = a.alternatives() == null ? a.name() : (a.name() + ";" + a.alternatives());
|
||||||
|
// System.out.println("Score for " + action + " evaluating " + a.name() + ": " + getActionNameMatch(action, a));
|
||||||
|
// }
|
||||||
|
return actions.get(actionsList.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getActionNameMatch(String str, InvokableAction action) {
|
||||||
|
int nameMatch = levenshteinDistance(str, action.name());
|
||||||
|
if (action.alternatives() != null) {
|
||||||
|
for (String alt : action.alternatives().split(";")) {
|
||||||
|
nameMatch = Math.min(nameMatch, levenshteinDistance(str, alt));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nameMatch;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,13 +132,15 @@ public abstract class Video extends Device {
|
|||||||
public static int MIN_SCREEN_REFRESH = 15;
|
public static int MIN_SCREEN_REFRESH = 15;
|
||||||
|
|
||||||
Runnable redrawScreen = () -> {
|
Runnable redrawScreen = () -> {
|
||||||
if (computer.getRunningProperty().get()) {
|
if (visible != null && video != null) {
|
||||||
visible.getPixelWriter().setPixels(0, 0, 560, 192, video.getPixelReader(), 0, 0);
|
// if (computer.getRunningProperty().get()) {
|
||||||
|
screenDirty = false;
|
||||||
|
visible.getPixelWriter().setPixels(0, 0, 560, 192, video.getPixelReader(), 0, 0);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public void redraw() {
|
public void redraw() {
|
||||||
screenDirty = false;
|
|
||||||
javafx.application.Platform.runLater(redrawScreen);
|
javafx.application.Platform.runLater(redrawScreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,19 +1,188 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<?import javafx.scene.image.*?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.canvas.*?>
|
<?import javafx.scene.control.Button?>
|
||||||
<?import java.lang.*?>
|
<?import javafx.scene.control.Slider?>
|
||||||
<?import java.util.*?>
|
<?import javafx.scene.image.Image?>
|
||||||
<?import javafx.scene.*?>
|
<?import javafx.scene.image.ImageView?>
|
||||||
<?import javafx.scene.control.*?>
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.BorderPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.Pane?>
|
||||||
|
<?import javafx.scene.layout.StackPane?>
|
||||||
|
<?import javafx.scene.layout.TilePane?>
|
||||||
|
|
||||||
<AnchorPane id="AnchorPane" fx:id="rootPane" prefHeight="384.0" prefWidth="560.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="jace.JaceUIController">
|
<AnchorPane id="AnchorPane" fx:id="rootPane" prefHeight="384.0" prefWidth="560.0" style="-fx-background-color: black;" stylesheets="@../styles/style.css" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="jace.JaceUIController">
|
||||||
<children>
|
<children>
|
||||||
<StackPane fx:id="stackPane" prefHeight="384.0" prefWidth="560.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
<StackPane fx:id="stackPane" prefHeight="384.0" prefWidth="560.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
<children>
|
<children>
|
||||||
<ImageView fx:id="appleScreen" fitHeight="384.0" fitWidth="560.0" pickOnBounds="true" />
|
<ImageView fx:id="appleScreen" fitHeight="384.0" fitWidth="560.0" pickOnBounds="true" style="-fx-background-color: BLACK;" />
|
||||||
<HBox fx:id="notificationBox" alignment="BOTTOM_RIGHT" blendMode="SCREEN" fillHeight="false" maxHeight="45.0" mouseTransparent="true" prefHeight="45.0" prefWidth="560.0" StackPane.alignment="BOTTOM_CENTER" />
|
<HBox fx:id="notificationBox" alignment="BOTTOM_RIGHT" blendMode="SCREEN" fillHeight="false" maxHeight="45.0" mouseTransparent="true" prefHeight="45.0" prefWidth="560.0" StackPane.alignment="BOTTOM_CENTER" />
|
||||||
|
<AnchorPane fx:id="menuButtonPane" prefHeight="200.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<Button fx:id="menuButton" layoutX="494.0" layoutY="14.0" mnemonicParsing="false" styleClass="menuButton" text="☰" AnchorPane.rightAnchor="14.0" AnchorPane.topAnchor="14.0" />
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
|
<BorderPane fx:id="controlOverlay" visible="false">
|
||||||
|
<center>
|
||||||
|
<HBox maxHeight="64.0" prefHeight="64.0" styleClass="uiSpeedSlider" BorderPane.alignment="CENTER">
|
||||||
|
<children>
|
||||||
|
<Pane prefHeight="200.0" prefWidth="200.0" HBox.hgrow="SOMETIMES" />
|
||||||
|
<AnchorPane>
|
||||||
|
<children>
|
||||||
|
<ImageView layoutX="2.0" layoutY="2.0">
|
||||||
|
<image>
|
||||||
|
<Image url="@../styles/icons/slow.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
|
<Slider fx:id="speedSlider" blockIncrement="1.0" majorTickUnit="5.0" max="10.0" minorTickCount="5" prefHeight="64.0" prefWidth="300.0" showTickLabels="true" showTickMarks="true" snapToTicks="true" value="1.0" HBox.hgrow="ALWAYS">
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets />
|
||||||
|
</HBox.margin>
|
||||||
|
<padding>
|
||||||
|
<Insets top="18.0" />
|
||||||
|
</padding>
|
||||||
|
</Slider>
|
||||||
|
<AnchorPane>
|
||||||
|
<children>
|
||||||
|
<ImageView layoutX="2.0" layoutY="2.0">
|
||||||
|
<image>
|
||||||
|
<Image url="@../styles/icons/fast.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
|
<Pane prefHeight="200.0" prefWidth="200.0" HBox.hgrow="SOMETIMES" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
</center>
|
||||||
|
<top>
|
||||||
|
<HBox fillHeight="false" nodeOrientation="LEFT_TO_RIGHT" BorderPane.alignment="CENTER">
|
||||||
|
<children>
|
||||||
|
<TilePane hgap="5.0" nodeOrientation="LEFT_TO_RIGHT" vgap="5.0" HBox.hgrow="SOMETIMES">
|
||||||
|
<children>
|
||||||
|
<Button contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="Info">
|
||||||
|
<graphic>
|
||||||
|
<ImageView>
|
||||||
|
<image>
|
||||||
|
<Image url="@../styles/icons/info.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
<Button contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="Config">
|
||||||
|
<graphic>
|
||||||
|
<ImageView>
|
||||||
|
<image>
|
||||||
|
<Image url="@../styles/icons/config.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
</children>
|
||||||
|
</TilePane>
|
||||||
|
<TilePane alignment="TOP_RIGHT" hgap="5.0" vgap="5.0" HBox.hgrow="ALWAYS">
|
||||||
|
<children>
|
||||||
|
<Button contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="IDE">
|
||||||
|
<graphic>
|
||||||
|
<ImageView>
|
||||||
|
<image>
|
||||||
|
<Image url="@../styles/icons/ide.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
<Button contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="Inspect">
|
||||||
|
<graphic>
|
||||||
|
<ImageView>
|
||||||
|
<image>
|
||||||
|
<Image url="@../styles/icons/inspect.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
<Button alignment="TOP_LEFT" contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="Plug-in" TilePane.alignment="TOP_RIGHT">
|
||||||
|
<graphic>
|
||||||
|
<ImageView>
|
||||||
|
<image>
|
||||||
|
<Image url="@../styles/icons/plugin.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
</children>
|
||||||
|
</TilePane>
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
</top>
|
||||||
|
<bottom>
|
||||||
|
<HBox fillHeight="false" nodeOrientation="LEFT_TO_RIGHT" BorderPane.alignment="CENTER">
|
||||||
|
<children>
|
||||||
|
<TilePane hgap="5.0" nodeOrientation="LEFT_TO_RIGHT" vgap="5.0" HBox.hgrow="SOMETIMES">
|
||||||
|
<children>
|
||||||
|
<Button contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="Fullscreen">
|
||||||
|
<graphic>
|
||||||
|
<ImageView>
|
||||||
|
<image>
|
||||||
|
<Image url="@../styles/icons/fullscreen.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
<Button contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="Aspect">
|
||||||
|
<graphic>
|
||||||
|
<ImageView>
|
||||||
|
<image>
|
||||||
|
<Image url="@../styles/icons/aspect.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
<Button contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="Gfx Mode">
|
||||||
|
<graphic>
|
||||||
|
<ImageView>
|
||||||
|
<image>
|
||||||
|
<Image url="@../styles/icons/screenshot.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
</children>
|
||||||
|
</TilePane>
|
||||||
|
<TilePane alignment="TOP_RIGHT" hgap="5.0" vgap="5.0" HBox.hgrow="ALWAYS">
|
||||||
|
<children>
|
||||||
|
<Button contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="Play">
|
||||||
|
<graphic>
|
||||||
|
<ImageView>
|
||||||
|
<image>
|
||||||
|
<Image url="@../styles/icons/play.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
<Button alignment="TOP_LEFT" contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="Restart" TilePane.alignment="TOP_RIGHT">
|
||||||
|
<graphic>
|
||||||
|
<ImageView>
|
||||||
|
<image>
|
||||||
|
<Image url="@../styles/icons/reboot.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
</children>
|
||||||
|
</TilePane>
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
</bottom>
|
||||||
|
<StackPane.margin>
|
||||||
|
<Insets />
|
||||||
|
</StackPane.margin>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||||
|
</padding>
|
||||||
|
</BorderPane>
|
||||||
</children>
|
</children>
|
||||||
</StackPane>
|
</StackPane>
|
||||||
</children>
|
</children>
|
||||||
|
BIN
src/main/resources/styles/icons/aspect.png
Normal file
After Width: | Height: | Size: 724 B |
BIN
src/main/resources/styles/icons/brun.png
Normal file
After Width: | Height: | Size: 976 B |
BIN
src/main/resources/styles/icons/config.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
src/main/resources/styles/icons/fast.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
src/main/resources/styles/icons/fullscreen.png
Normal file
After Width: | Height: | Size: 914 B |
BIN
src/main/resources/styles/icons/ide.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/main/resources/styles/icons/info.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/main/resources/styles/icons/inspect.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
src/main/resources/styles/icons/paste.png
Normal file
After Width: | Height: | Size: 800 B |
BIN
src/main/resources/styles/icons/play.png
Normal file
After Width: | Height: | Size: 567 B |
BIN
src/main/resources/styles/icons/plugin.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
src/main/resources/styles/icons/reboot.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/main/resources/styles/icons/rewind.png
Normal file
After Width: | Height: | Size: 701 B |
BIN
src/main/resources/styles/icons/screenshot.png
Normal file
After Width: | Height: | Size: 959 B |
BIN
src/main/resources/styles/icons/slow.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
src/main/resources/styles/icons/sound.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
@ -3,7 +3,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.setting-row {
|
.setting-row {
|
||||||
-fx-padding: 5 0 0 4;
|
-fx-padding: 5 0 0 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.setting-label, .setting-keyboard-shortcut {
|
.setting-label, .setting-keyboard-shortcut {
|
||||||
@ -19,3 +19,30 @@
|
|||||||
-fx-font-size: 9pt;
|
-fx-font-size: 9pt;
|
||||||
-fx-font-family: "Courier New";
|
-fx-font-family: "Courier New";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menuButton {
|
||||||
|
-fx-font-size:16pt;
|
||||||
|
-fx-border-radius: 10px;
|
||||||
|
-fx-background-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menuButton, .uiActionButton, .uiSpeedSlider ImageView, .uiSpeedSlider Slider, .uiSpeedSlider AnchorPane {
|
||||||
|
-fx-background-color: rgba(0, 0, 0, 0.75);
|
||||||
|
-fx-text-fill: #a0FFa0
|
||||||
|
}
|
||||||
|
|
||||||
|
.uiActionButton ImageView, .uiSpeedSlider ImageView {
|
||||||
|
-fx-effect: dropshadow(gaussian , rgba(128,255,128,0.75) , 2,1.0,0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.uiSpeedSlider AnchorPane {
|
||||||
|
-fx-padding: 0 5 0 5
|
||||||
|
}
|
||||||
|
|
||||||
|
.uiSpeedSlider Slider {
|
||||||
|
-fx-padding: 18 0 10 0
|
||||||
|
}
|
||||||
|
|
||||||
|
.uiSpeedSlider Slider NumberAxis {
|
||||||
|
-fx-tick-label-fill: #80ff80
|
||||||
|
}
|