From d0a52bb6de19c81705094b26132931a57dc40866 Mon Sep 17 00:00:00 2001 From: Joe Date: Wed, 20 Aug 2025 17:15:23 +0100 Subject: Add GuiService for plugins to make additions to the GUI. Also add enough context to do something useful. --- .../src/main/java/cuchaz/enigma/gui/Gui.java | 5 ++ .../main/java/cuchaz/enigma/gui/GuiController.java | 16 ++++- .../cuchaz/enigma/gui/elements/CustomMenuItem.java | 49 +++++++++++++++ .../enigma/gui/elements/EditorPopupMenu.java | 71 +++++++++++----------- 4 files changed, 105 insertions(+), 36 deletions(-) create mode 100644 enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CustomMenuItem.java (limited to 'enigma-swing/src/main/java') diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java index 5881900c..12877fed 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java @@ -45,6 +45,7 @@ import org.jetbrains.annotations.Nullable; import cuchaz.enigma.Enigma; import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.api.service.GuiService; import cuchaz.enigma.gui.config.Themes; import cuchaz.enigma.gui.config.UiConfig; import cuchaz.enigma.gui.dialog.JavadocDialog; @@ -142,6 +143,10 @@ public class Gui { LanguageUtil.addListener(this::retranslateUi); Themes.addListener((lookAndFeel, boxHighlightPainters) -> SwingUtilities.updateComponentTreeUI(this.getFrame())); + for (GuiService guiService : enigma.getServices().get(GuiService.TYPE)) { + guiService.onStart(controller); + } + this.mainWindow.setVisible(true); } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java index e1e8521a..3609427d 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java @@ -31,6 +31,7 @@ import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; import cuchaz.enigma.Enigma; import cuchaz.enigma.EnigmaProject; @@ -46,6 +47,8 @@ import cuchaz.enigma.analysis.MethodReferenceTreeNode; import cuchaz.enigma.analysis.StructureTreeNode; import cuchaz.enigma.analysis.StructureTreeOptions; import cuchaz.enigma.api.service.ObfuscationTestService; +import cuchaz.enigma.api.view.GuiView; +import cuchaz.enigma.api.view.entry.EntryReferenceView; import cuchaz.enigma.classhandle.ClassHandle; import cuchaz.enigma.classhandle.ClassHandleProvider; import cuchaz.enigma.classprovider.ClasspathClassProvider; @@ -91,7 +94,7 @@ import cuchaz.enigma.utils.Utils; import cuchaz.enigma.utils.validation.PrintValidatable; import cuchaz.enigma.utils.validation.ValidationContext; -public class GuiController implements ClientPacketHandler { +public class GuiController implements ClientPacketHandler, GuiView { private final Gui gui; public final Enigma enigma; @@ -115,6 +118,11 @@ public class GuiController implements ClientPacketHandler { this.enigma = enigma; } + @Override + public EnigmaProject getProject() { + return project; + } + public boolean isDirty() { return project != null && project.getMapper().isDirty(); } @@ -330,6 +338,12 @@ public class GuiController implements ClientPacketHandler { } } + @Override + @Nullable + public EntryReferenceView getCursorReference() { + return gui.getCursorReference(); + } + /** * Navigates to the declaration with respect to navigation history. * diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CustomMenuItem.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CustomMenuItem.java new file mode 100644 index 00000000..055a00f7 --- /dev/null +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CustomMenuItem.java @@ -0,0 +1,49 @@ +package cuchaz.enigma.gui.elements; + +import java.util.function.BooleanSupplier; +import java.util.function.Supplier; + +import javax.swing.JMenuItem; +import javax.swing.KeyStroke; + +import cuchaz.enigma.api.service.GuiService; +import cuchaz.enigma.utils.I18n; + +public class CustomMenuItem implements GuiService.MenuItemBuilder { + private final JMenuItem menuItem; + private final Supplier translationKey; + private BooleanSupplier enabledCondition = () -> true; + + public CustomMenuItem(JMenuItem menuItem, Supplier translationKey) { + this.menuItem = menuItem; + this.translationKey = translationKey; + menuItem.setText(translationKey.get()); + } + + @Override + public GuiService.MenuItemBuilder setAccelerator(KeyStroke accelerator) { + menuItem.setAccelerator(accelerator); + return this; + } + + @Override + public GuiService.MenuItemBuilder setEnabledWhen(BooleanSupplier condition) { + this.enabledCondition = condition; + return this; + } + + @Override + public GuiService.MenuItemBuilder setAction(Runnable action) { + menuItem.addActionListener(e -> action.run()); + return this; + } + + public void updateUiState() { + menuItem.setEnabled(enabledCondition.getAsBoolean()); + menuItem.setText(I18n.translate(translationKey.get())); + } + + public void retranslateUi() { + menuItem.setText(I18n.translate(translationKey.get())); + } +} diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorPopupMenu.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorPopupMenu.java index dd37798a..8f968f38 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorPopupMenu.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorPopupMenu.java @@ -1,13 +1,18 @@ package cuchaz.enigma.gui.elements; +import java.awt.Component; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.KeyStroke; import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.api.service.GuiService; import cuchaz.enigma.gui.EditableType; import cuchaz.enigma.gui.Gui; import cuchaz.enigma.gui.GuiController; @@ -36,6 +41,7 @@ public class EditorPopupMenu { private final JMenuItem zoomInItem = new JMenuItem(); private final JMenuItem zoomOutMenu = new JMenuItem(); private final JMenuItem resetZoomItem = new JMenuItem(); + private final List customItems = new ArrayList<>(); private final EditorPanel editor; private final Gui gui; @@ -102,48 +108,35 @@ public class EditorPopupMenu { this.zoomInItem.addActionListener(event -> editor.offsetEditorZoom(2)); this.zoomOutMenu.addActionListener(event -> editor.offsetEditorZoom(-2)); this.resetZoomItem.addActionListener(event -> editor.resetEditorZoom()); + + for (GuiService guiService : gui.getController().enigma.getServices().get(GuiService.TYPE)) { + guiService.addToEditorContextMenu(gui.getController(), new GuiService.MenuRegistrar() { + @Override + public void addSeparator() { + ui.addSeparator(); + } + + @Override + public GuiService.MenuItemBuilder add(Supplier translationKey) { + JMenuItem menuItem = new JMenuItem(); + ui.add(menuItem); + CustomMenuItem item = new CustomMenuItem(menuItem, translationKey); + customItems.add(item); + return item; + } + }); + } } // TODO have editor redirect key event to menu so that the actions get // triggered without having to hardcode them here, because this // is a hack public boolean handleKeyEvent(KeyEvent event) { - if (event.isControlDown()) { - switch (event.getKeyCode()) { - case KeyEvent.VK_I: - this.showInheritanceItem.doClick(); - return true; - case KeyEvent.VK_M: - this.showImplementationsItem.doClick(); - return true; - case KeyEvent.VK_N: - this.openEntryItem.doClick(); - return true; - case KeyEvent.VK_P: - this.openPreviousItem.doClick(); - return true; - case KeyEvent.VK_E: - this.openNextItem.doClick(); - return true; - case KeyEvent.VK_C: - if (event.isShiftDown()) { - this.showCallsSpecificItem.doClick(); - } else { - this.showCallsItem.doClick(); - } + KeyStroke keyStroke = KeyStroke.getKeyStrokeForEvent(event); - return true; - case KeyEvent.VK_O: - this.toggleMappingItem.doClick(); - return true; - case KeyEvent.VK_R: - this.renameItem.doClick(); - return true; - case KeyEvent.VK_D: - this.editJavadocItem.doClick(); - return true; - case KeyEvent.VK_V: - this.pasteItem.doClick(); + for (Component component : ui.getComponents()) { + if (component instanceof JMenuItem menuItem && keyStroke.equals(menuItem.getAccelerator())) { + menuItem.doClick(); return true; } } @@ -184,6 +177,10 @@ public class EditorPopupMenu { } this.toggleMappingItem.setText(I18n.translate("popup_menu." + (isDeobfuscated ? "reset_obfuscated" : "mark_deobfuscated"))); + + for (CustomMenuItem customItem : customItems) { + customItem.updateUiState(); + } } public void retranslateUi() { @@ -201,6 +198,10 @@ public class EditorPopupMenu { this.zoomInItem.setText(I18n.translate("popup_menu.zoom.in")); this.zoomOutMenu.setText(I18n.translate("popup_menu.zoom.out")); this.resetZoomItem.setText(I18n.translate("popup_menu.zoom.reset")); + + for (CustomMenuItem customItem : customItems) { + customItem.retranslateUi(); + } } public JPopupMenu getUi() { -- cgit v1.2.3