mirror of
https://github.com/badvision/lawless-legends.git
synced 2024-09-30 18:54:47 +00:00
Removed all method reflections
This commit is contained in:
parent
e21c9ac5e6
commit
e867ab2611
@ -29,6 +29,14 @@
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.8bitbunch</groupId>
|
||||
<artifactId>lawlesslegends</artifactId>
|
||||
<version>3.0-SNAPSHOT</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
<annotationProcessors>jace.config.InvokableActionAnnotationProcessor</annotationProcessors>
|
||||
</configuration>
|
||||
<version>3.11.0</version>
|
||||
</plugin>
|
||||
@ -113,18 +121,6 @@
|
||||
}
|
||||
</moduleInfoSource>
|
||||
</module>
|
||||
<module>
|
||||
<artifact>
|
||||
<groupId>org.reflections</groupId>
|
||||
<artifactId>reflections</artifactId>
|
||||
<version>0.10.2</version>
|
||||
</artifact>
|
||||
<moduleInfoSource>
|
||||
module reflections {
|
||||
exports org.reflections;
|
||||
}
|
||||
</moduleInfoSource>
|
||||
</module>
|
||||
</modules>
|
||||
<overwriteExistingFiles>true</overwriteExistingFiles>
|
||||
</configuration>
|
||||
|
@ -13,6 +13,7 @@ import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@ -259,8 +260,8 @@ public class JaceUIController {
|
||||
|
||||
private void connectButtons(Node n) {
|
||||
if (n instanceof Button button) {
|
||||
Runnable action = Utility.getNamedInvokableAction(button.getText());
|
||||
button.setOnMouseClicked(evt -> action.run());
|
||||
Function<Boolean, Boolean> action = Utility.getNamedInvokableAction(button.getText());
|
||||
button.setOnMouseClicked(evt -> action.apply(false));
|
||||
} else if (n instanceof Parent parent) {
|
||||
parent.getChildrenUnmodifiable().forEach(child -> connectButtons(child));
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.GenericArrayType;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
@ -64,15 +63,6 @@ import javafx.scene.image.ImageView;
|
||||
*/
|
||||
public class Configuration implements Reconfigurable {
|
||||
|
||||
private static Method findAnyMethodByName(Class<? extends Reconfigurable> aClass, String m) {
|
||||
for (Method method : aClass.getMethods()) {
|
||||
if (method.getName().equals(m)) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static ConfigurableField getConfigurableFieldInfo(Reconfigurable subject, String settingName) {
|
||||
Field f;
|
||||
try {
|
||||
@ -88,15 +78,6 @@ public class Configuration implements Reconfigurable {
|
||||
return (f != null && !f.shortName().equals("")) ? f.shortName() : longName;
|
||||
}
|
||||
|
||||
public static InvokableAction getInvokableActionInfo(Reconfigurable subject, String actionName) {
|
||||
for (Method m : subject.getClass().getMethods()) {
|
||||
if (m.getName().equals(actionName) && m.isAnnotationPresent(InvokableAction.class)) {
|
||||
return m.getAnnotation(InvokableAction.class);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Optional<ImageView> getChangedIcon() {
|
||||
return Utility.loadIcon("icon_exclaim.gif").map(ImageView::new);
|
||||
}
|
||||
@ -301,13 +282,19 @@ public class Configuration implements Reconfigurable {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Method m : node.subject.getClass().getMethods()) {
|
||||
if (!m.isAnnotationPresent(InvokableAction.class)) {
|
||||
continue;
|
||||
InvokableActionRegistry registry = InvokableActionRegistry.getInstance();
|
||||
registry.getStaticMethodNames(node.subject.getClass()).stream().forEach((name) -> {
|
||||
InvokableAction action = registry.getStaticMethodInfo(name);
|
||||
if (action != null) {
|
||||
node.hotkeys.put(name, action.defaultKeyMapping());
|
||||
}
|
||||
InvokableAction action = m.getDeclaredAnnotation(InvokableAction.class);
|
||||
node.hotkeys.put(m.getName(), action.defaultKeyMapping());
|
||||
}
|
||||
});
|
||||
registry.getInstanceMethodNames(node.subject.getClass()).stream().forEach((name) -> {
|
||||
InvokableAction action = registry.getInstanceMethodInfo(name);
|
||||
if (action != null) {
|
||||
node.hotkeys.put(name, action.defaultKeyMapping());
|
||||
}
|
||||
});
|
||||
|
||||
for (Field f : node.subject.getClass().getFields()) {
|
||||
// System.out.println("Evaluating field " + f.getName());
|
||||
@ -530,12 +517,18 @@ public class Configuration implements Reconfigurable {
|
||||
private static void doApply(ConfigNode node) {
|
||||
List<String> removeList = new ArrayList<>();
|
||||
Keyboard.unregisterAllHandlers(node.subject);
|
||||
node.hotkeys.keySet().stream().forEach((m) -> {
|
||||
Method method = findAnyMethodByName(node.subject.getClass(), m);
|
||||
if (method != null) {
|
||||
InvokableAction action = method.getAnnotation(InvokableAction.class);
|
||||
for (String code : node.hotkeys.get(m)) {
|
||||
Keyboard.registerInvokableAction(action, node.subject, method, code);
|
||||
InvokableActionRegistry registry = InvokableActionRegistry.getInstance();
|
||||
node.hotkeys.keySet().stream().forEach((name) -> {
|
||||
InvokableAction action = registry.getStaticMethodInfo(name);
|
||||
if (action != null) {
|
||||
for (String code : node.hotkeys.get(name)) {
|
||||
Keyboard.registerInvokableAction(action, name, registry.getStaticFunction(name), code);
|
||||
}
|
||||
}
|
||||
action = registry.getInstanceMethodInfo(name);
|
||||
if (action != null) {
|
||||
for (String code : node.hotkeys.get(name)) {
|
||||
Keyboard.registerInvokableAction(action, name, registry.getInstanceFunction(name), code);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,18 +1,5 @@
|
||||
package jace.config;
|
||||
|
||||
import jace.config.Configuration.ConfigNode;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.text.Text;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
@ -25,6 +12,26 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import jace.config.Configuration.ConfigNode;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.ChoiceBox;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.control.SplitPane;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.control.TreeItem;
|
||||
import javafx.scene.control.TreeView;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.text.Text;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
public class ConfigurationUIController {
|
||||
public static final String DELIMITER = "~!~";
|
||||
|
||||
@ -177,7 +184,8 @@ public class ConfigurationUIController {
|
||||
}
|
||||
|
||||
private Node buildKeyShortcutRow(ConfigNode node, String actionName, String[] values) {
|
||||
InvokableAction actionInfo = Configuration.getInvokableActionInfo(node.subject, actionName);
|
||||
InvokableActionRegistry registry = InvokableActionRegistry.getInstance();
|
||||
InvokableAction actionInfo = registry.getInstanceMethodInfo(actionName);
|
||||
if (actionInfo == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -0,0 +1,168 @@
|
||||
package jace.config;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.processing.AbstractProcessor;
|
||||
import javax.annotation.processing.Messager;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||
import javax.annotation.processing.SupportedSourceVersion;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
|
||||
// Compile-time annotation processor which creates a registry of all static methods annotated with @InvokableAction.
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_17)
|
||||
@SupportedAnnotationTypes("jace.config.InvokableAction")
|
||||
public class InvokableActionAnnotationProcessor extends AbstractProcessor {
|
||||
Messager messager;
|
||||
Map<InvokableAction, ExecutableElement> staticMethods = new HashMap<>();
|
||||
Map<InvokableAction, ExecutableElement> instanceMethods = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public synchronized void init(ProcessingEnvironment processingEnv) {
|
||||
super.init(processingEnv);
|
||||
this.messager = processingEnv.getMessager();
|
||||
messager.printMessage(javax.tools.Diagnostic.Kind.NOTE, "InvokableActionAnnotationProcessor init()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||
messager.printMessage(javax.tools.Diagnostic.Kind.NOTE, "InvokableActionAnnotationProcessor process()");
|
||||
|
||||
// Get list of methods annotated with @InvokableAction.
|
||||
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(InvokableAction.class);
|
||||
for (Element element : elements) {
|
||||
if (element.getModifiers().contains(javax.lang.model.element.Modifier.STATIC)) {
|
||||
// If the annotation method is static, add it to the static method registry.
|
||||
trackStaticMethod(element);
|
||||
} else {
|
||||
// For non-static methods, track in a separate registry.
|
||||
trackInstanceMethod(element);
|
||||
}
|
||||
try {
|
||||
// Write class that contains static and instance methods.
|
||||
writeRegistryClass();
|
||||
} catch (IOException ex) {
|
||||
messager.printMessage(javax.tools.Diagnostic.Kind.ERROR, "Error writing InvokableActionRegistry.java: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void trackStaticMethod(Element element) {
|
||||
// Store the method in the static method registry.
|
||||
staticMethods.put(element.getAnnotation(InvokableAction.class), (ExecutableElement) element);
|
||||
}
|
||||
|
||||
private void trackInstanceMethod(Element element) {
|
||||
// Store the method in the instance method registry.
|
||||
instanceMethods.put(element.getAnnotation(InvokableAction.class), (ExecutableElement) element);
|
||||
}
|
||||
|
||||
private String serializeArrayOfStrings(String... strings) {
|
||||
return Arrays.stream(strings).map(s -> "\"" + s + "\"").collect(Collectors.joining(","));
|
||||
}
|
||||
|
||||
private void serializeInvokableAction(InvokableAction annotation, String variableName, PrintWriter writer) {
|
||||
writer.append("""
|
||||
%s = createInvokableAction("%s", "%s", "%s", "%s", %s, %s, new String[] {%s});
|
||||
""".formatted(
|
||||
variableName,
|
||||
annotation.name(),
|
||||
annotation.category(),
|
||||
annotation.description(),
|
||||
annotation.alternatives(),
|
||||
annotation.consumeKeyEvent(),
|
||||
annotation.notifyOnRelease(),
|
||||
serializeArrayOfStrings(annotation.defaultKeyMapping())
|
||||
));
|
||||
}
|
||||
|
||||
// Write the registry class.
|
||||
private void writeRegistryClass() throws IOException {
|
||||
Files.createDirectories(new File("target/generated-sources/jace/config").toPath());
|
||||
try (PrintWriter writer = new PrintWriter(new FileWriter("target/generated-sources/jace/config/InvokableActionRegistryImpl.java"))) {
|
||||
writer.write("""
|
||||
package jace.config;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class InvokableActionRegistryImpl extends InvokableActionRegistry {
|
||||
@Override
|
||||
public void init() {
|
||||
InvokableAction annotation;
|
||||
""");
|
||||
for (Map.Entry<InvokableAction, ExecutableElement> entry : staticMethods.entrySet()) {
|
||||
InvokableAction annotation = entry.getKey();
|
||||
ExecutableElement method = entry.getValue();
|
||||
String packageName = method.getEnclosingElement().getEnclosingElement().toString();
|
||||
String className = method.getEnclosingElement().getSimpleName().toString();
|
||||
String fqnClassName = packageName + "." + className;
|
||||
serializeInvokableAction(annotation, "annotation", writer);
|
||||
boolean takesBoolenParameter = method.getParameters().size() == 1 && method.getParameters().get(0).asType().toString().equalsIgnoreCase("boolean");
|
||||
boolean returnsBoolean = method.getReturnType().toString().equalsIgnoreCase("boolean");
|
||||
writer.write("""
|
||||
putStaticAction(annotation.name(), %s.class, annotation, (b) -> {
|
||||
try {
|
||||
%s %s.%s(%s);
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking %s", ex);
|
||||
%s
|
||||
}
|
||||
});
|
||||
""".formatted(
|
||||
fqnClassName,
|
||||
returnsBoolean ? "return " : "",
|
||||
fqnClassName,
|
||||
method.getSimpleName(),
|
||||
takesBoolenParameter ? "b" : "",
|
||||
fqnClassName + "." + method.getSimpleName(),
|
||||
returnsBoolean ? "return false;" : ""
|
||||
));
|
||||
}
|
||||
|
||||
// Now for the instance methods, do the same except use a biconsumer which takes the instance as well as the boolean parameter.
|
||||
for (Map.Entry<InvokableAction, ExecutableElement> entry : instanceMethods.entrySet()) {
|
||||
InvokableAction annotation = entry.getKey();
|
||||
ExecutableElement method = entry.getValue();
|
||||
String packageName = method.getEnclosingElement().getEnclosingElement().toString();
|
||||
String className = method.getEnclosingElement().getSimpleName().toString();
|
||||
String fqnClassName = packageName + "." + className;
|
||||
serializeInvokableAction(annotation, "annotation", writer);
|
||||
boolean takesBoolenParameter = method.getParameters().size() == 1 && method.getParameters().get(0).asType().toString().equalsIgnoreCase("boolean");
|
||||
boolean returnsBoolean = method.getReturnType().toString().equalsIgnoreCase("boolean");
|
||||
writer.write("""
|
||||
putInstanceAction(annotation.name(), %s.class, annotation, (o, b) -> {
|
||||
try {
|
||||
%s ((%s) o).%s(%s);
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking %s", ex);
|
||||
%s
|
||||
}
|
||||
});
|
||||
""".formatted(
|
||||
fqnClassName,
|
||||
returnsBoolean ? "return " : "",
|
||||
fqnClassName,
|
||||
method.getSimpleName(),
|
||||
takesBoolenParameter ? "b" : "",
|
||||
fqnClassName + "." + method.getSimpleName(),
|
||||
returnsBoolean ? "return false;" : ""
|
||||
));
|
||||
}
|
||||
writer.write("}\n}");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,148 @@
|
||||
package jace.config;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public abstract class InvokableActionRegistry {
|
||||
protected static final Logger logger = Logger.getLogger(InvokableActionRegistry.class.getName());
|
||||
private final Map<Class, Set<String>> staticMethodNames = new HashMap<>();
|
||||
private final Map<String, InvokableAction> staticMethodInfo = new HashMap<>();
|
||||
private final Map<String, Function<Boolean, Boolean>> staticMethodCallers = new HashMap<>();
|
||||
private final Map<Class, Set<String>> instanceMethodNames = new HashMap<>();
|
||||
private final Map<String, InvokableAction> instanceMethodInfo = new HashMap<>();
|
||||
private final Map<String, BiFunction<Object, Boolean, Boolean>> instanceMethodCallers = new HashMap<>();
|
||||
|
||||
protected static InvokableActionRegistry instance;
|
||||
|
||||
public static InvokableActionRegistry getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new InvokableActionRegistryImpl();
|
||||
instance.init();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
abstract public void init();
|
||||
|
||||
final public void putStaticAction(String name, Class c, InvokableAction action, Consumer<Boolean> caller) {
|
||||
putStaticAction(name, c, action, (b) -> {
|
||||
caller.accept(b);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
final public void putStaticAction(String name, Class c, InvokableAction action, Function<Boolean, Boolean> caller) {
|
||||
staticMethodInfo.put(name, action);
|
||||
staticMethodCallers.put(name, caller);
|
||||
staticMethodNames.computeIfAbsent(c, k -> new TreeSet<>()).add(name);
|
||||
}
|
||||
|
||||
public void putInstanceAction(String name, Class c, InvokableAction action, BiConsumer<Object, Boolean> caller) {
|
||||
putInstanceAction(name, c, action, (o, b) -> {
|
||||
caller.accept(o, b);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
public void putInstanceAction(String name, Class c, InvokableAction action, BiFunction<Object, Boolean, Boolean> caller) {
|
||||
instanceMethodInfo.put(name, action);
|
||||
instanceMethodCallers.put(name, caller);
|
||||
instanceMethodNames.computeIfAbsent(c, k -> new TreeSet<>()).add(name);
|
||||
}
|
||||
|
||||
public Set<String> getStaticMethodNames(Class c) {
|
||||
// Build a set of all the method names for this class and all its superclasses.
|
||||
Set<String> result = new TreeSet<>();
|
||||
Class current = c;
|
||||
while (current != null) {
|
||||
result.addAll(staticMethodNames.getOrDefault(current, Collections.EMPTY_SET));
|
||||
current = current.getSuperclass();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Set<String> getInstanceMethodNames(Class c) {
|
||||
// Build a set of all the method names for this class and all its superclasses.
|
||||
Set<String> result = new TreeSet<>();
|
||||
Class current = c;
|
||||
while (current != null) {
|
||||
result.addAll(instanceMethodNames.getOrDefault(current, Collections.EMPTY_SET));
|
||||
current = current.getSuperclass();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public InvokableAction getStaticMethodInfo(String name) {
|
||||
return staticMethodInfo.get(name);
|
||||
}
|
||||
|
||||
public InvokableAction getInstanceMethodInfo(String name) {
|
||||
return instanceMethodInfo.get(name);
|
||||
}
|
||||
|
||||
public Function<Boolean, Boolean> getStaticFunction(String name) {
|
||||
return staticMethodCallers.get(name);
|
||||
}
|
||||
|
||||
public BiFunction<Object, Boolean, Boolean> getInstanceFunction(String name) {
|
||||
return instanceMethodCallers.get(name);
|
||||
}
|
||||
|
||||
public Set<InvokableAction> getAllStaticActions() {
|
||||
return new HashSet<>(staticMethodInfo.values());
|
||||
}
|
||||
|
||||
protected InvokableAction createInvokableAction(String name, String category, String description, String alternatives, boolean consumeKeyEvent, boolean notifyOnRelease, String[] defaultKeyMapping) {
|
||||
return new InvokableAction() {
|
||||
@Override
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String category() {
|
||||
return category;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String alternatives() {
|
||||
return alternatives;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean consumeKeyEvent() {
|
||||
return consumeKeyEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean notifyOnRelease() {
|
||||
return notifyOnRelease;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] defaultKeyMapping() {
|
||||
return defaultKeyMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends java.lang.annotation.Annotation> annotationType() {
|
||||
return InvokableAction.class;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,233 @@
|
||||
package jace.config;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
// NOTE: This is generated code. Do not edit.
|
||||
public class InvokableActionRegistryImpl extends InvokableActionRegistry {
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
InvokableAction annotation;
|
||||
annotation = createInvokableAction("Resize window", "general", "Resize the screen to 1x/1.5x/2x/3x video size", "Aspect;Adjust screen;Adjust window size;Adjust aspect ratio;Fix screen;Fix window size;Fix aspect ratio;Correct aspect ratio;", true, false, new String[]{"ctrl+shift+a"});
|
||||
putStaticAction(annotation.name(), jace.EmulatorUILogic.class, annotation, (b) -> {
|
||||
try {
|
||||
jace.EmulatorUILogic.scaleIntegerRatio();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.EmulatorUILogic.scaleIntegerRatio", ex);
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Rewind", "General", "Go back 1 second", "Timewarp", true, false, new String[]{"ctrl+shift+Open Bracket"});
|
||||
putStaticAction(annotation.name(), jace.state.StateManager.class, annotation, (b) -> {
|
||||
try {
|
||||
jace.state.StateManager.beKindRewind();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.state.StateManager.beKindRewind", ex);
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Configuration", "general", "Edit emulator configuraion", "Reconfigure;Preferences;Settings;Config", true, false, new String[]{"f4", "ctrl+shift+c"});
|
||||
putStaticAction(annotation.name(), jace.EmulatorUILogic.class, annotation, (b) -> {
|
||||
try {
|
||||
jace.EmulatorUILogic.showConfig();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.EmulatorUILogic.showConfig", ex);
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Load settings", "general", "Load all configuration settings previously saved", "load preferences;revert settings;revert preferences", true, false, new String[]{"meta+ctrl+r"});
|
||||
putStaticAction(annotation.name(), jace.config.Configuration.class, annotation, (b) -> {
|
||||
try {
|
||||
jace.config.Configuration.loadSettings();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.config.Configuration.loadSettings", ex);
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("About", "general", "Display about window", "info;credits", true, false, new String[]{"ctrl+shift+."});
|
||||
putStaticAction(annotation.name(), jace.EmulatorUILogic.class, annotation, (b) -> {
|
||||
try {
|
||||
jace.EmulatorUILogic.showAboutWindow();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.EmulatorUILogic.showAboutWindow", ex);
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Record sound", "sound", "Toggles recording (saving) sound output to a file", "", true, false, new String[]{"ctrl+shift+w"});
|
||||
putStaticAction(annotation.name(), jace.apple2e.Speaker.class, annotation, (b) -> {
|
||||
try {
|
||||
jace.apple2e.Speaker.toggleFileOutput();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.apple2e.Speaker.toggleFileOutput", ex);
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("BRUN file", "file", "Loads a binary file in memory and executes it. File should end with #06xxxx, where xxxx is the start address in hex", "Execute program;Load binary;Load program;Load rom;Play single-load game", true, false, new String[]{"ctrl+shift+b"});
|
||||
putStaticAction(annotation.name(), jace.EmulatorUILogic.class, annotation, (b) -> {
|
||||
try {
|
||||
jace.EmulatorUILogic.runFile();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.EmulatorUILogic.runFile", ex);
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Save Raw Screenshot", "general", "Save raw (RAM) format of visible screen", "screendump;raw screenshot", true, false, new String[]{"ctrl+shift+z"});
|
||||
putStaticAction(annotation.name(), jace.EmulatorUILogic.class, annotation, (b) -> {
|
||||
try {
|
||||
jace.EmulatorUILogic.saveScreenshotRaw();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.EmulatorUILogic.saveScreenshotRaw", ex);
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Save settings", "general", "Save all configuration settings as defaults", "save preferences;save defaults", true, false, new String[]{"meta+ctrl+s"});
|
||||
putStaticAction(annotation.name(), jace.config.Configuration.class, annotation, (b) -> {
|
||||
try {
|
||||
jace.config.Configuration.saveSettings();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.config.Configuration.saveSettings", ex);
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Toggle fullscreen", "general", "Activate/deactivate fullscreen mode", "fullscreen;maximize", true, false, new String[]{"ctrl+shift+f"});
|
||||
putStaticAction(annotation.name(), jace.EmulatorUILogic.class, annotation, (b) -> {
|
||||
try {
|
||||
jace.EmulatorUILogic.toggleFullscreen();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.EmulatorUILogic.toggleFullscreen", ex);
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Toggle Debug", "debug", "Show/hide the debug panel", "Show Debug;Hide Debug;Inspect", true, false, new String[]{"ctrl+shift+d"});
|
||||
putStaticAction(annotation.name(), jace.EmulatorUILogic.class, annotation, (b) -> {
|
||||
try {
|
||||
jace.EmulatorUILogic.toggleDebugPanel();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.EmulatorUILogic.toggleDebugPanel", ex);
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Refresh screen", "display", "Marks screen contents as changed, forcing full screen redraw", "redraw", true, false, new String[]{"ctrl+shift+r"});
|
||||
putStaticAction(annotation.name(), jace.core.Video.class, annotation, (b) -> {
|
||||
try {
|
||||
jace.core.Video.forceRefresh();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.core.Video.forceRefresh", ex);
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Toggle video mode", "video", "", "Gfx mode;color;b&w;monochrome", true, false, new String[]{"ctrl+shift+g"});
|
||||
putStaticAction(annotation.name(), jace.apple2e.VideoNTSC.class, annotation, (b) -> {
|
||||
try {
|
||||
jace.apple2e.VideoNTSC.changeVideoMode();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.apple2e.VideoNTSC.changeVideoMode", ex);
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Save Screenshot", "general", "Save image of visible screen", "Save image;save framebuffer;screenshot", true, false, new String[]{"ctrl+shift+s"});
|
||||
putStaticAction(annotation.name(), jace.EmulatorUILogic.class, annotation, (b) -> {
|
||||
try {
|
||||
jace.EmulatorUILogic.saveScreenshot();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.EmulatorUILogic.saveScreenshot", ex);
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Paste clipboard", "Keyboard", "", "paste", true, false, new String[]{"Ctrl+Shift+V", "Shift+Insert"});
|
||||
putStaticAction(annotation.name(), jace.core.Keyboard.class, annotation, (b) -> {
|
||||
try {
|
||||
jace.core.Keyboard.pasteFromClipboard();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.core.Keyboard.pasteFromClipboard", ex);
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Open IDE", "development", "Open new IDE window for Basic/Assembly/Plasma coding", "IDE;dev;development;acme;assembler;editor", true, false, new String[]{"ctrl+shift+i"});
|
||||
putStaticAction(annotation.name(), jace.EmulatorUILogic.class, annotation, (b) -> {
|
||||
try {
|
||||
jace.EmulatorUILogic.showIDE();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.EmulatorUILogic.showIDE", ex);
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Up", "joystick", "", "", true, true, new String[]{"up"});
|
||||
putInstanceAction(annotation.name(), jace.hardware.Joystick.class, annotation, (o, b) -> {
|
||||
try {
|
||||
return ((jace.hardware.Joystick) o).joystickUp(b);
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.hardware.Joystick.joystickUp", ex);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Open Apple Key", "Keyboard", "", "OA", false, true, new String[]{"Alt"});
|
||||
putInstanceAction(annotation.name(), jace.core.Keyboard.class, annotation, (o, b) -> {
|
||||
try {
|
||||
((jace.core.Keyboard) o).openApple(b);
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.core.Keyboard.openApple", ex);
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Left", "joystick", "", "", true, true, new String[]{"left"});
|
||||
putInstanceAction(annotation.name(), jace.hardware.Joystick.class, annotation, (o, b) -> {
|
||||
try {
|
||||
return ((jace.hardware.Joystick) o).joystickLeft(b);
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.hardware.Joystick.joystickLeft", ex);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Right", "joystick", "", "", true, true, new String[]{"right"});
|
||||
putInstanceAction(annotation.name(), jace.hardware.Joystick.class, annotation, (o, b) -> {
|
||||
try {
|
||||
return ((jace.hardware.Joystick) o).joystickRight(b);
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.hardware.Joystick.joystickRight", ex);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Closed Apple Key", "Keyboard", "", "CA", false, true, new String[]{"Shortcut", "Meta", "Command"});
|
||||
putInstanceAction(annotation.name(), jace.core.Keyboard.class, annotation, (o, b) -> {
|
||||
try {
|
||||
((jace.core.Keyboard) o).solidApple(b);
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.core.Keyboard.solidApple", ex);
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Down", "joystick", "", "", true, true, new String[]{"down"});
|
||||
putInstanceAction(annotation.name(), jace.hardware.Joystick.class, annotation, (o, b) -> {
|
||||
try {
|
||||
return ((jace.hardware.Joystick) o).joystickDown(b);
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.hardware.Joystick.joystickDown", ex);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Pause", "General", "Stops the computer, allowing reconfiguration of core elements", "freeze;halt", true, false, new String[]{"meta+pause", "alt+pause"});
|
||||
putInstanceAction(annotation.name(), jace.core.Computer.class, annotation, (o, b) -> {
|
||||
try {
|
||||
return ((jace.core.Computer) o).pause();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.core.Computer.pause", ex);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Cold boot", "general", "Process startup sequence from power-up", "Full reset;reset emulator", true, false, new String[]{"Ctrl+Shift+Backspace", "Ctrl+Shift+Delete"});
|
||||
putInstanceAction(annotation.name(), jace.core.Computer.class, annotation, (o, b) -> {
|
||||
try {
|
||||
((jace.core.Computer) o).invokeColdStart();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.core.Computer.invokeColdStart", ex);
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Warm boot", "general", "Process user-initatiated reboot (ctrl+apple+reset)", "reboot;reset;three-finger-salute;restart", true, false, new String[]{"Ctrl+Ignore Alt+Ignore Meta+Backspace", "Ctrl+Ignore Alt+Ignore Meta+Delete"});
|
||||
putInstanceAction(annotation.name(), jace.core.Computer.class, annotation, (o, b) -> {
|
||||
try {
|
||||
((jace.core.Computer) o).invokeWarmStart();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.core.Computer.invokeWarmStart", ex);
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Toggle Cheats", "General", "", "cheat;Plug-in", true, false, new String[]{"ctrl+shift+m"});
|
||||
putInstanceAction(annotation.name(), jace.cheat.Cheats.class, annotation, (o, b) -> {
|
||||
try {
|
||||
((jace.cheat.Cheats) o).toggleCheats();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.cheat.Cheats.toggleCheats", ex);
|
||||
}
|
||||
});
|
||||
annotation = createInvokableAction("Resume", "General", "Resumes the computer if it was previously paused", "unpause;unfreeze;resume;play", true, false, new String[]{"meta+shift+pause", "alt+shift+pause"});
|
||||
putInstanceAction(annotation.name(), jace.core.Computer.class, annotation, (o, b) -> {
|
||||
try {
|
||||
((jace.core.Computer) o).resume();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error invoking jace.core.Computer.resume", ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -20,13 +20,12 @@ package jace.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@ -96,8 +95,14 @@ public class Keyboard implements Reconfigurable {
|
||||
private static final Map<KeyCode, Set<KeyHandler>> keyHandlersByKey = new HashMap<>();
|
||||
private static final Map<Object, Set<KeyHandler>> keyHandlersByOwner = new HashMap<>();
|
||||
|
||||
public static void registerInvokableAction(InvokableAction action, Object owner, Method method, String code) {
|
||||
boolean isStatic = Modifier.isStatic(method.getModifiers());
|
||||
/**
|
||||
*
|
||||
* @param action
|
||||
* @param owner
|
||||
* @param method
|
||||
* @param code
|
||||
*/
|
||||
public static void registerInvokableAction(InvokableAction action, Object owner, Function<Boolean, Boolean> method, String code) {
|
||||
registerKeyHandler(new KeyHandler(code) {
|
||||
@Override
|
||||
public boolean handleKeyUp(KeyEvent e) {
|
||||
@ -105,41 +110,40 @@ public class Keyboard implements Reconfigurable {
|
||||
if (action == null || !action.notifyOnRelease()) {
|
||||
return false;
|
||||
}
|
||||
// System.out.println("Key up: "+method.toString());
|
||||
Object returnValue = null;
|
||||
try {
|
||||
if (method.getParameterCount() > 0) {
|
||||
returnValue = method.invoke(isStatic ? null : owner, false);
|
||||
} else {
|
||||
returnValue = method.invoke(isStatic ? null : owner);
|
||||
}
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
|
||||
Logger.getLogger(Keyboard.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
if (returnValue != null) {
|
||||
return (Boolean) returnValue;
|
||||
}
|
||||
return action.consumeKeyEvent();
|
||||
return method.apply(false) || action.consumeKeyEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleKeyDown(KeyEvent e) {
|
||||
// System.out.println("Key down: "+method.toString());
|
||||
Emulator.withComputer(c -> c.getKeyboard().shiftPressed = e.isShiftDown());
|
||||
Object returnValue = null;
|
||||
try {
|
||||
if (method.getParameterCount() > 0) {
|
||||
returnValue = method.invoke(isStatic ? null : owner, true);
|
||||
} else {
|
||||
returnValue = method.invoke(isStatic ? null : owner);
|
||||
}
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
|
||||
Logger.getLogger(Keyboard.class.getName()).log(Level.SEVERE, null, ex);
|
||||
if (action == null) {
|
||||
return false;
|
||||
}
|
||||
if (returnValue != null) {
|
||||
return (Boolean) returnValue;
|
||||
return method.apply(true) || action.consumeKeyEvent();
|
||||
}
|
||||
}, owner);
|
||||
}
|
||||
|
||||
public static void registerInvokableAction(InvokableAction action, Object owner, BiFunction<Object, Boolean, Boolean> method, String code) {
|
||||
registerKeyHandler(new KeyHandler(code) {
|
||||
@Override
|
||||
public boolean handleKeyUp(KeyEvent e) {
|
||||
Emulator.withComputer(c -> c.getKeyboard().shiftPressed = e.isShiftDown());
|
||||
if (action == null || !action.notifyOnRelease()) {
|
||||
return false;
|
||||
}
|
||||
return action != null ? action.consumeKeyEvent() : null;
|
||||
return method.apply(owner, false) || action.consumeKeyEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleKeyDown(KeyEvent e) {
|
||||
// System.out.println("Key down: "+method.toString());
|
||||
Emulator.withComputer(c -> c.getKeyboard().shiftPressed = e.isShiftDown());
|
||||
if (action == null) {
|
||||
return false;
|
||||
}
|
||||
return method.apply(owner, true) || action.consumeKeyEvent();
|
||||
}
|
||||
}, owner);
|
||||
}
|
||||
|
@ -20,8 +20,6 @@ package jace.core;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@ -30,12 +28,10 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.function.Function;
|
||||
|
||||
import jace.config.Configuration;
|
||||
import jace.config.InvokableAction;
|
||||
import jace.config.InvokableActionRegistry;
|
||||
import javafx.application.Platform;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Alert;
|
||||
@ -406,38 +402,15 @@ public class Utility {
|
||||
return null;
|
||||
}
|
||||
|
||||
static final Map<InvokableAction, Runnable> allActions = new ConcurrentHashMap<>();
|
||||
|
||||
public static Map<InvokableAction, Runnable> getAllInvokableActions() {
|
||||
synchronized (allActions) {
|
||||
if (allActions.isEmpty()) {
|
||||
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());
|
||||
public static Function<Boolean, Boolean> getNamedInvokableAction(String action) {
|
||||
InvokableActionRegistry registry = InvokableActionRegistry.getInstance();
|
||||
List<InvokableAction> actionsList = new ArrayList(registry.getAllStaticActions());
|
||||
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));
|
||||
return registry.getStaticFunction(actionsList.get(0).name());
|
||||
}
|
||||
|
||||
private static int getActionNameMatch(String str, InvokableAction action) {
|
||||
|
@ -21,6 +21,7 @@ module lawlesslegends {
|
||||
requires java.desktop;
|
||||
requires java.datatransfer;
|
||||
requires java.scripting;
|
||||
requires static java.compiler;
|
||||
requires javafx.fxmlEmpty;
|
||||
requires javafx.fxml;
|
||||
requires javafx.swing;
|
||||
@ -46,5 +47,8 @@ module lawlesslegends {
|
||||
|
||||
uses javax.sound.sampled.SourceDataLine;
|
||||
|
||||
provides javax.annotation.processing.Processor with jace.config.InvokableActionAnnotationProcessor;
|
||||
|
||||
exports jace;
|
||||
exports jace.config;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user