From 0e55d1cc371040044a7448fbd58e70257f522662 Mon Sep 17 00:00:00 2001 From: Vftdan Date: Wed, 4 Sep 2024 22:49:57 +0200 Subject: [PATCH] Try implementing "copy to clipboard" waypoint menu action --- .../VftdansXaerosMapAddonClient.java | 44 ++++- .../worldmap/WaypointReaderDecorator.java | 157 ++++++++++++++++++ .../WaypointRightClickOptionFactory.java | 9 + .../WaypointRightClickOptionsRegistry.java | 31 ++++ .../ClipboardCopyWaypointAction.java | 76 +++++++++ .../waypointactions/WaypointAction.java | 35 ++++ 6 files changed, 351 insertions(+), 1 deletion(-) create mode 100644 src/client/java/io/github/vftdan/xaeromapaddon/worldmap/WaypointReaderDecorator.java create mode 100644 src/client/java/io/github/vftdan/xaeromapaddon/worldmap/WaypointRightClickOptionFactory.java create mode 100644 src/client/java/io/github/vftdan/xaeromapaddon/worldmap/WaypointRightClickOptionsRegistry.java create mode 100644 src/client/java/io/github/vftdan/xaeromapaddon/worldmap/waypointactions/ClipboardCopyWaypointAction.java create mode 100644 src/client/java/io/github/vftdan/xaeromapaddon/worldmap/waypointactions/WaypointAction.java diff --git a/src/client/java/io/github/vftdan/xaeromapaddon/VftdansXaerosMapAddonClient.java b/src/client/java/io/github/vftdan/xaeromapaddon/VftdansXaerosMapAddonClient.java index 439f367..dfcb118 100644 --- a/src/client/java/io/github/vftdan/xaeromapaddon/VftdansXaerosMapAddonClient.java +++ b/src/client/java/io/github/vftdan/xaeromapaddon/VftdansXaerosMapAddonClient.java @@ -1,10 +1,52 @@ package io.github.vftdan.xaeromapaddon; +import java.lang.reflect.Field; import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.loader.api.FabricLoader; +import io.github.vftdan.xaeromapaddon.worldmap.WaypointRightClickOptionsRegistry; +import io.github.vftdan.xaeromapaddon.worldmap.waypointactions.ClipboardCopyWaypointAction; +import io.github.vftdan.xaeromapaddon.worldmap.WaypointReaderDecorator; +import xaero.map.mods.SupportMods; +import xaero.map.mods.SupportXaeroMinimap; +import xaero.map.mods.gui.WaypointRenderer; +import xaero.map.mods.gui.WaypointReader; +import xaero.map.element.MapElementRenderer; public class VftdansXaerosMapAddonClient implements ClientModInitializer { @Override public void onInitializeClient() { // This entrypoint is suitable for setting up client-specific logic, such as rendering. + { + WaypointRightClickOptionsRegistry registry = WaypointRightClickOptionsRegistry.getInstance(); + registry.addFactory(new ClipboardCopyWaypointAction()); + } + + if (FabricLoader.getInstance().isModLoaded("xaeros-world-map")) { + injectWaypointReaderDecorator(); + } else { + System.err.println("No xaeros-world-map"); + } } -} \ No newline at end of file + + void injectWaypointReaderDecorator() { + try { + SupportXaeroMinimap supportMinimap = SupportMods.xaeroMinimap; + if (supportMinimap == null) { + System.err.println("No xaeroMinimap mod support"); + return; + } + + Field waypointRendererField = SupportXaeroMinimap.class.getDeclaredField("waypointRenderer"); + waypointRendererField.setAccessible(true); + WaypointRenderer waypointRenderer = (WaypointRenderer) waypointRendererField.get(supportMinimap); + + Field waypointReaderField = MapElementRenderer.class.getDeclaredField("reader"); + waypointReaderField.setAccessible(true); + waypointReaderField.set(waypointRenderer, new WaypointReaderDecorator((WaypointReader) waypointReaderField.get(waypointRenderer))); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } + } +} diff --git a/src/client/java/io/github/vftdan/xaeromapaddon/worldmap/WaypointReaderDecorator.java b/src/client/java/io/github/vftdan/xaeromapaddon/worldmap/WaypointReaderDecorator.java new file mode 100644 index 0000000..733b96f --- /dev/null +++ b/src/client/java/io/github/vftdan/xaeromapaddon/worldmap/WaypointReaderDecorator.java @@ -0,0 +1,157 @@ +package io.github.vftdan.xaeromapaddon.worldmap; + +import java.util.ArrayList; +import net.minecraft.client.MinecraftClient; +import xaero.map.mods.gui.WaypointReader; +import xaero.map.mods.gui.Waypoint; +import xaero.map.mods.gui.WaypointRenderContext; +import xaero.map.gui.CursorBox; +import xaero.map.gui.IRightClickableElement; +import xaero.map.gui.dropdown.rightclick.RightClickOption; + +public class WaypointReaderDecorator extends WaypointReader { + private WaypointReader wrapped; + + public WaypointReaderDecorator(WaypointReader wrapped) { + this.wrapped = wrapped; + } + + @Override + public boolean isMouseOverMenuElement(Waypoint element, int x, int y, int mouseX, int mouseY, MinecraftClient mc) { + return wrapped.isMouseOverMenuElement(element, x, y, mouseX, mouseY, mc); + } + + @Override + public boolean isHoveredOnMap(int location, Waypoint element, double mouseX, double mouseZ, double scale, double screenSizeBasedScale, double rendererDimDiv, WaypointRenderContext context, float partialTicks) { + return wrapped.isHoveredOnMap(location, element, mouseX, mouseZ, scale, screenSizeBasedScale, rendererDimDiv, context, partialTicks); + } + + @Override + public boolean isOnScreen(Waypoint element, double cameraX, double cameraZ, int width, int height, double scale, double screenSizeBasedScale, double rendererDimDiv, WaypointRenderContext context, float partialTicks) { + return wrapped.isOnScreen(element, cameraX, cameraZ, width, height, scale, screenSizeBasedScale, rendererDimDiv, context, partialTicks); + } + + @Override + public CursorBox getTooltip(Waypoint element, WaypointRenderContext context, boolean overMenu) { + return wrapped.getTooltip(element, context, overMenu); + } + + @Override + public boolean waypointIsGood(Waypoint w, WaypointRenderContext context) { + return wrapped.waypointIsGood(w, context); + } + + @Override + public boolean isHidden(Waypoint element, WaypointRenderContext context) { + return wrapped.isHidden(element, context); + } + + @Override + public boolean isInteractable(int location, Waypoint element) { + return wrapped.isInteractable(location, element); + } + + @Override + public float getBoxScale(int location, Waypoint element, WaypointRenderContext context) { + return wrapped.getBoxScale(location, element, context); + } + + @Override + public double getRenderX(Waypoint element, WaypointRenderContext context, float partialTicks) { + return wrapped.getRenderX(element, context, partialTicks); + } + + @Override + public double getRenderZ(Waypoint element, WaypointRenderContext context, float partialTicks) { + return wrapped.getRenderZ(element, context, partialTicks); + } + + @Override + public int getInteractionBoxLeft(Waypoint element, WaypointRenderContext context, float partialTicks) { + return wrapped.getInteractionBoxLeft(element, context, partialTicks); + } + + @Override + public int getInteractionBoxRight(Waypoint element, WaypointRenderContext context, float partialTicks) { + return wrapped.getInteractionBoxRight(element, context, partialTicks); + } + + @Override + public int getInteractionBoxTop(Waypoint element, WaypointRenderContext context, float partialTicks) { + return wrapped.getInteractionBoxTop(element, context, partialTicks); + } + + @Override + public int getInteractionBoxBottom(Waypoint element, WaypointRenderContext context, float partialTicks) { + return wrapped.getInteractionBoxBottom(element, context, partialTicks); + } + + @Override + public int getLeftSideLength(Waypoint element, MinecraftClient mc) { + return wrapped.getLeftSideLength(element, mc); + } + + @Override + public String getMenuName(Waypoint element) { + return wrapped.getMenuName(element); + } + + @Override + public int getMenuTextFillLeftPadding(Waypoint element) { + return wrapped.getMenuTextFillLeftPadding(element); + } + + @Override + public String getFilterName(Waypoint element) { + return wrapped.getFilterName(element); + } + + @Override + public ArrayList getRightClickOptions(final Waypoint element, IRightClickableElement target) { + ArrayList rightClickOptions = wrapped.getRightClickOptions(element, target); + + for (WaypointRightClickOptionFactory factory: WaypointRightClickOptionsRegistry.getInstance()) { + RightClickOption option = factory.create(element, target, rightClickOptions.size()); + if (option != null) { + rightClickOptions.add(option); + } + } + + return rightClickOptions; + } + + @Override + public boolean isRightClickValid(Waypoint element) { + return wrapped.isRightClickValid(element); + } + + @Override + public int getRightClickTitleBackgroundColor(Waypoint element) { + return wrapped.getRightClickTitleBackgroundColor(element); + } + + @Override + public boolean shouldScaleBoxWithOptionalScale() { + return wrapped.shouldScaleBoxWithOptionalScale(); + } + + @Override + public int getRenderBoxLeft(Waypoint element, WaypointRenderContext context, float partialTicks) { + return wrapped.getRenderBoxLeft(element, context, partialTicks); + } + + @Override + public int getRenderBoxRight(Waypoint element, WaypointRenderContext context, float partialTicks) { + return wrapped.getRenderBoxRight(element, context, partialTicks); + } + + @Override + public int getRenderBoxTop(Waypoint element, WaypointRenderContext context, float partialTicks) { + return wrapped.getRenderBoxTop(element, context, partialTicks); + } + + @Override + public int getRenderBoxBottom(Waypoint element, WaypointRenderContext context, float partialTicks) { + return wrapped.getRenderBoxBottom(element, context, partialTicks); + } +} diff --git a/src/client/java/io/github/vftdan/xaeromapaddon/worldmap/WaypointRightClickOptionFactory.java b/src/client/java/io/github/vftdan/xaeromapaddon/worldmap/WaypointRightClickOptionFactory.java new file mode 100644 index 0000000..d3bc4fc --- /dev/null +++ b/src/client/java/io/github/vftdan/xaeromapaddon/worldmap/WaypointRightClickOptionFactory.java @@ -0,0 +1,9 @@ +package io.github.vftdan.xaeromapaddon.worldmap; + +import xaero.map.mods.gui.Waypoint; +import xaero.map.gui.IRightClickableElement; +import xaero.map.gui.dropdown.rightclick.RightClickOption; + +public interface WaypointRightClickOptionFactory { + RightClickOption create(Waypoint waypoint, IRightClickableElement target, int index); +} diff --git a/src/client/java/io/github/vftdan/xaeromapaddon/worldmap/WaypointRightClickOptionsRegistry.java b/src/client/java/io/github/vftdan/xaeromapaddon/worldmap/WaypointRightClickOptionsRegistry.java new file mode 100644 index 0000000..6d3157c --- /dev/null +++ b/src/client/java/io/github/vftdan/xaeromapaddon/worldmap/WaypointRightClickOptionsRegistry.java @@ -0,0 +1,31 @@ +package io.github.vftdan.xaeromapaddon.worldmap; + +import java.util.ArrayList; +import java.util.Iterator; + +public class WaypointRightClickOptionsRegistry implements Iterable { + private static WaypointRightClickOptionsRegistry instance = null; + + public static WaypointRightClickOptionsRegistry getInstance() { + if (instance == null) { + instance = new WaypointRightClickOptionsRegistry(); + } + return instance; + } + + private ArrayList factories = new ArrayList(); + + private WaypointRightClickOptionsRegistry() { + } + + public void addFactory(WaypointRightClickOptionFactory factory) { + if (factory == null) { + return; + } + factories.add(factory); + } + + public Iterator iterator() { + return factories.iterator(); + } +} diff --git a/src/client/java/io/github/vftdan/xaeromapaddon/worldmap/waypointactions/ClipboardCopyWaypointAction.java b/src/client/java/io/github/vftdan/xaeromapaddon/worldmap/waypointactions/ClipboardCopyWaypointAction.java new file mode 100644 index 0000000..e934633 --- /dev/null +++ b/src/client/java/io/github/vftdan/xaeromapaddon/worldmap/waypointactions/ClipboardCopyWaypointAction.java @@ -0,0 +1,76 @@ +package io.github.vftdan.xaeromapaddon.worldmap.waypointactions; + +import java.util.Arrays; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.util.Clipboard; +import xaero.common.XaeroMinimapSession; +import xaero.common.minimap.waypoints.WaypointWorld; +import xaero.common.minimap.waypoints.WaypointWorldRootContainer; +import xaero.map.mods.gui.Waypoint; +import xaero.map.mods.SupportMods; + +public class ClipboardCopyWaypointAction implements WaypointAction { + @Override + public void performAction(Screen screen, Waypoint waypoint) { + String serialized = waypointToShareableString(waypoint); + MinecraftClient client = MinecraftClient.getInstance(); + if (client.player != null) { + Clipboard clipboard = new Clipboard(); + clipboard.setClipboard(client.getWindow().getHandle(), serialized); + } + } + + @Override + public String translationKey() { + return "gui.vftdan.xaero_right_click_waypoint.copy_to_clipboard"; + } + + public static String waypointToShareableString(Waypoint w) { + WaypointWorld waypointWorld = SupportMods.xaeroMinimap.getWaypointWorld(); + String dimInfo; + if (isWaypointWorldInternal(waypointWorld)) { + String containerKey = waypointWorld.getContainer().getKey(); + String[] containerKeyElements = containerKey.split("/"); + String internalInfo; + if (containerKeyElements.length < 2) { + internalInfo = "waypoints"; + } else { + String dim = containerKeyElements[1]; + if (dim.equals("dim%0")) { + dim = "overworld"; + } else if (dim.equals("dim%-1")) { + dim = "the_nether"; + } else if (dim.equals("dim%1")) { + dim = "the_end"; + } + containerKeyElements[1] = dim; + internalInfo = String.join("/", Arrays.copyOfRange(containerKeyElements, 1, containerKeyElements.length)); + } + dimInfo = "Internal_" + internalInfo; + } else { + dimInfo = "External"; + } + + String title = escapeWaypointComponent(w.getName()); + String icon = escapeWaypointComponent(w.getSymbol()); + String x = w.getX() + ""; + String y = w.isyIncluded() ? Integer.valueOf(w.getY()) + "" : "~"; + String z = w.getZ() + ""; + String color = w.getColor() + ""; + String isRotation = w.isRotation() + ""; + String yaw = w.getYaw() + ""; + String dimInfoEscaped = escapeWaypointComponent(dimInfo); + return "xaero-waypoint:" + title + ":" + icon + ":" + x + ":" + y + ":" + z + ":" + color + ":" + isRotation + ":" + yaw + ":" + dimInfoEscaped; + } + + public static String escapeWaypointComponent(String unsafe) { + return unsafe.replace(":", "^col^").replace("*", "^ast^").replace("-", "^min^").replace("_", "-"); + } + + public static boolean isWaypointWorldInternal(WaypointWorld waypointWorld) { + WaypointWorldRootContainer lhs = waypointWorld.getContainer().getRootContainer(); + WaypointWorldRootContainer rhs = XaeroMinimapSession.getCurrentSession().getWaypointsManager().getAutoWorld().getContainer().getRootContainer(); + return lhs == rhs; + } +} diff --git a/src/client/java/io/github/vftdan/xaeromapaddon/worldmap/waypointactions/WaypointAction.java b/src/client/java/io/github/vftdan/xaeromapaddon/worldmap/waypointactions/WaypointAction.java new file mode 100644 index 0000000..154419d --- /dev/null +++ b/src/client/java/io/github/vftdan/xaeromapaddon/worldmap/waypointactions/WaypointAction.java @@ -0,0 +1,35 @@ +package io.github.vftdan.xaeromapaddon.worldmap.waypointactions; + +import io.github.vftdan.xaeromapaddon.worldmap.WaypointRightClickOptionFactory; +import net.minecraft.client.gui.screen.Screen; +import xaero.map.gui.GuiMap; +import xaero.map.mods.gui.Waypoint; +import xaero.map.gui.IRightClickableElement; +import xaero.map.gui.dropdown.rightclick.RightClickOption; + +public interface WaypointAction extends WaypointRightClickOptionFactory { + @Override + default RightClickOption create(Waypoint waypoint, IRightClickableElement target, int index) { + return new WaypointActionRightClickOption(this, waypoint, target, index); + } + + void performAction(Screen screen, Waypoint waypoint); + + String translationKey(); + + public static class WaypointActionRightClickOption extends RightClickOption { + WaypointAction action; + Waypoint waypoint; + + public WaypointActionRightClickOption(WaypointAction action, Waypoint waypoint, IRightClickableElement target, int index) { + super(action.translationKey(), index, target); + this.action = action; + this.waypoint = waypoint; + } + + @Override + public void onAction(Screen screen) { + action.performAction(screen, waypoint); + } + } +}