From aabb1dae7bfa225490d9c5cc6ee966e87472472d Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 14 Jan 2019 19:45:46 -0500 Subject: [PATCH] Add overlay menu support Co-authored-by: TheStonedTurtle --- .../java/net/runelite/api/MenuAction.java | 4 ++ .../main/java/net/runelite/api/MenuEntry.java | 6 -- .../client/events/OverlayMenuClicked.java | 41 ++++++++++++ .../runelite/client/ui/overlay/Overlay.java | 17 +++++ .../client/ui/overlay/OverlayManager.java | 59 ++++++++++++++++- .../client/ui/overlay/OverlayMenuEntry.java | 36 +++++++++++ .../client/ui/overlay/OverlayRenderer.java | 63 +++++++++++++++++++ 7 files changed, 219 insertions(+), 7 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/events/OverlayMenuClicked.java create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayMenuEntry.java diff --git a/runelite-api/src/main/java/net/runelite/api/MenuAction.java b/runelite-api/src/main/java/net/runelite/api/MenuAction.java index 21f8aad0fc..09eaec6f8b 100644 --- a/runelite-api/src/main/java/net/runelite/api/MenuAction.java +++ b/runelite-api/src/main/java/net/runelite/api/MenuAction.java @@ -260,6 +260,10 @@ public enum MenuAction * Menu action injected by runelite for its menu items. */ RUNELITE(1500), + /** + * Menu action injected by runelite for overlay menu items. + */ + RUNELITE_OVERLAY(1501), FOLLOW(2046), TRADE(2047), diff --git a/runelite-api/src/main/java/net/runelite/api/MenuEntry.java b/runelite-api/src/main/java/net/runelite/api/MenuEntry.java index 4d45b4c7ed..c15a44f359 100644 --- a/runelite-api/src/main/java/net/runelite/api/MenuEntry.java +++ b/runelite-api/src/main/java/net/runelite/api/MenuEntry.java @@ -60,10 +60,4 @@ public class MenuEntry */ private int param1; - @Override - public String toString() - { - return "MenuEntry{" + "option=" + option + ", target=" + target + ", identifier=" + identifier + ", type=" + type + ", param0=" + param0 + ", param1=" + param1 + '}'; - } - } diff --git a/runelite-client/src/main/java/net/runelite/client/events/OverlayMenuClicked.java b/runelite-client/src/main/java/net/runelite/client/events/OverlayMenuClicked.java new file mode 100644 index 0000000000..6394e7c799 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/events/OverlayMenuClicked.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.events; + +import lombok.AllArgsConstructor; +import lombok.Data; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayMenuEntry; + +/** + * Event fired when an overlay menu entry is clicked. + */ +@Data +@AllArgsConstructor +public class OverlayMenuClicked +{ + private OverlayMenuEntry entry; + private Overlay overlay; +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/Overlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/Overlay.java index bc7e3bd499..07b9518be3 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/Overlay.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/Overlay.java @@ -27,14 +27,20 @@ package net.runelite.client.ui.overlay; import java.awt.Dimension; import java.awt.Point; import java.awt.Rectangle; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nullable; import lombok.Getter; import lombok.Setter; +import net.runelite.client.plugins.Plugin; import net.runelite.client.ui.overlay.components.LayoutableRenderableEntity; @Getter @Setter public abstract class Overlay implements LayoutableRenderableEntity { + @Nullable + private final Plugin plugin; private Point preferredLocation; private Dimension preferredSize; private OverlayPosition preferredPosition; @@ -42,6 +48,17 @@ public abstract class Overlay implements LayoutableRenderableEntity private OverlayPosition position = OverlayPosition.TOP_LEFT; private OverlayPriority priority = OverlayPriority.NONE; private OverlayLayer layer = OverlayLayer.UNDER_WIDGETS; + private final List menuEntries = new ArrayList<>(); + + protected Overlay() + { + plugin = null; + } + + protected Overlay(Plugin plugin) + { + this.plugin = plugin; + } /** * Overlay name, used for saving the overlay, needs to be unique diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayManager.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayManager.java index 80e9eca49a..af07a188d7 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayManager.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayManager.java @@ -33,15 +33,20 @@ import java.util.Comparator; import java.util.EnumMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.Predicate; import javax.inject.Inject; import javax.inject.Singleton; import lombok.AccessLevel; import lombok.Getter; +import net.runelite.api.MenuAction; +import net.runelite.api.events.MenuOptionClicked; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigManager; import net.runelite.client.config.RuneLiteConfig; +import net.runelite.client.eventbus.EventBus; import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.OverlayMenuClicked; import net.runelite.client.events.PluginChanged; /** @@ -93,11 +98,13 @@ public class OverlayManager private final Map> overlayLayers = new EnumMap<>(OverlayLayer.class); private final ConfigManager configManager; + private final EventBus eventBus; @Inject - private OverlayManager(final ConfigManager configManager) + private OverlayManager(final ConfigManager configManager, final EventBus eventBus) { this.configManager = configManager; + this.eventBus = eventBus; } @Subscribe @@ -107,6 +114,56 @@ public class OverlayManager rebuildOverlayLayers(); } + @Subscribe + public void onMenuOptionClicked(MenuOptionClicked event) + { + if (event.getMenuAction() != MenuAction.RUNELITE_OVERLAY) + { + return; + } + + event.consume(); + + Optional optionalOverlay = overlays.stream().filter(o -> overlayId(o) == event.getId()).findAny(); + if (optionalOverlay.isPresent()) + { + Overlay overlay = optionalOverlay.get(); + List menuEntries = overlay.getMenuEntries(); + Optional optionalOverlayMenuEntry = menuEntries.stream() + .filter(me -> me.getOption().equals(event.getMenuOption())) + .findAny(); + if (optionalOverlayMenuEntry.isPresent()) + { + eventBus.post(new OverlayMenuClicked(optionalOverlayMenuEntry.get(), overlay)); + } + } + } + + int overlayId(Overlay overlay) + { + return overlays.indexOf(overlay); + } + + public void addMenu(Overlay overlay, MenuAction menuAction, String option, String target) + { + OverlayMenuEntry overlayMenuEntry = new OverlayMenuEntry(menuAction, option, target); + List menuEntries = overlay.getMenuEntries(); + menuEntries.add(overlayMenuEntry); + } + + public void removeMenu(Overlay overlay, String option) + { + List menuEntries = overlay.getMenuEntries(); + for (OverlayMenuEntry overlayMenuEntry : menuEntries) + { + if (overlayMenuEntry.getOption().equals(option)) + { + menuEntries.remove(overlayMenuEntry); + break; + } + } + } + /** * Gets all of the overlays on a layer sorted by priority and position * diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayMenuEntry.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayMenuEntry.java new file mode 100644 index 0000000000..f952f37713 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayMenuEntry.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.ui.overlay; + +import lombok.Value; +import net.runelite.api.MenuAction; + +@Value +public class OverlayMenuEntry +{ + private MenuAction menuAction; + private String option; + private String target; +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java index 04c5cad582..58e3bbe28c 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java @@ -39,6 +39,9 @@ import javax.inject.Singleton; import javax.swing.SwingUtilities; import net.runelite.api.Client; import net.runelite.api.GameState; +import net.runelite.api.MenuAction; +import net.runelite.api.MenuEntry; +import net.runelite.api.events.ClientTick; import net.runelite.api.events.FocusChanged; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; @@ -48,6 +51,8 @@ import net.runelite.client.input.KeyListener; import net.runelite.client.input.KeyManager; import net.runelite.client.input.MouseAdapter; import net.runelite.client.input.MouseManager; +import net.runelite.client.ui.JagexColors; +import net.runelite.client.util.ColorUtil; @Singleton public class OverlayRenderer extends MouseAdapter implements KeyListener @@ -69,6 +74,7 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener private final Point mousePosition = new Point(); private Overlay movedOverlay; private boolean inOverlayDraggingMode; + private MenuEntry[] menuEntries; // Overlay state validation private Rectangle viewportBounds; @@ -99,9 +105,35 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener if (!event.isFocused()) { inOverlayDraggingMode = false; + menuEntries = null; } } + @Subscribe + protected void onClientTick(ClientTick t) + { + if (menuEntries == null) + { + return; + } + + if (client.isMenuOpen()) + { + menuEntries = null; + return; + } + + MenuEntry[] clientMenuEntries = client.getMenuEntries(); + MenuEntry[] newEntries = new MenuEntry[clientMenuEntries.length + menuEntries.length]; + + newEntries[0] = clientMenuEntries[0]; // Keep cancel at 0 + System.arraycopy(menuEntries, 0, newEntries, 1, menuEntries.length); // Add overlay menu entries + System.arraycopy(clientMenuEntries, 1, newEntries, menuEntries.length + 1, clientMenuEntries.length - 1); // Add remaining menu entries + client.setMenuEntries(newEntries); + + menuEntries = null; + } + public void render(Graphics2D graphics, final OverlayLayer layer) { if (layer != OverlayLayer.ABOVE_MAP @@ -149,6 +181,10 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener graphics.setColor(previous); } + // Get mouse position + final net.runelite.api.Point mouseCanvasPosition = client.getMouseCanvasPosition(); + final Point mouse = new Point(mouseCanvasPosition.getX(), mouseCanvasPosition.getY()); + for (Overlay overlay : overlays) { OverlayPosition overlayPosition = overlay.getPosition(); @@ -227,6 +263,11 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener graphics.draw(bounds); graphics.setColor(previous); } + + if (menuEntries == null && !client.isMenuOpen() && bounds.contains(mouse)) + { + menuEntries = createRightClickMenuEntries(overlay); + } } } } @@ -494,4 +535,26 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener new Rectangle(rightChatboxPoint, SNAP_CORNER_SIZE), new Rectangle(canvasTopRightPoint, SNAP_CORNER_SIZE)); } + + private MenuEntry[] createRightClickMenuEntries(Overlay overlay) + { + List menuEntries = overlay.getMenuEntries(); + final MenuEntry[] entries = new MenuEntry[menuEntries.size()]; + + // Add in reverse order so they display correctly in the right-click menu + for (int i = menuEntries.size() - 1; i >= 0; --i) + { + OverlayMenuEntry overlayMenuEntry = menuEntries.get(i); + + final MenuEntry entry = new MenuEntry(); + entry.setOption(overlayMenuEntry.getOption()); + entry.setTarget(ColorUtil.wrapWithColorTag(overlayMenuEntry.getTarget(), JagexColors.MENU_TARGET)); + entry.setType(MenuAction.RUNELITE_OVERLAY.getId()); + entry.setIdentifier(overlayManager.overlayId(overlay)); // overlay id + + entries[i] = entry; + } + + return entries; + } }