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-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java b/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java index fdf92e0548..fa17d0b133 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java @@ -229,11 +229,22 @@ public interface RuneLiteConfig extends Config return FontType.REGULAR; } + @ConfigItem( + keyName = "overlayRightClick", + name = "Require Shift for overlay menu options", + description = "Toggles whether the overlay right-click menu requires holding down the shift key", + position = 33 + ) + default boolean overlayRightClick() + { + return true; + } + @ConfigItem( keyName = "infoBoxVertical", name = "Display infoboxes vertically", description = "Toggles the infoboxes to display vertically", - position = 33 + position = 34 ) default boolean infoBoxVertical() { @@ -244,7 +255,7 @@ public interface RuneLiteConfig extends Config keyName = "infoBoxWrap", name = "Infobox wrap count", description = "Configures the amount of infoboxes shown before wrapping", - position = 34 + position = 35 ) default int infoBoxWrap() { @@ -255,7 +266,7 @@ public interface RuneLiteConfig extends Config keyName = "infoBoxSize", name = "Infobox size (px)", description = "Configures the size of each infobox in pixels", - position = 35 + position = 36 ) default int infoBoxSize() { 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..95b4f9d778 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,6 +27,7 @@ package net.runelite.client.ui.overlay; import java.awt.Dimension; import java.awt.Point; import java.awt.Rectangle; +import java.util.LinkedHashMap; import lombok.Getter; import lombok.Setter; import net.runelite.client.ui.overlay.components.LayoutableRenderableEntity; @@ -42,6 +43,7 @@ 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 LinkedHashMap menuOptions = new LinkedHashMap<>(); /** * 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..3337cb1990 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 @@ -38,11 +38,14 @@ 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.Subscribe; import net.runelite.client.events.PluginChanged; +import net.runelite.client.util.Text; /** * Manages state of all game overlays @@ -107,6 +110,39 @@ public class OverlayManager rebuildOverlayLayers(); } + @Subscribe + public void onMenuOptionClicked(MenuOptionClicked event) + { + if (!MenuAction.RUNELITE_OVERLAY.equals(event.getMenuAction())) + { + return; + } + + event.consume(); + + final String overlayName = Text.removeTags(event.getMenuTarget()); + Overlay overlay = null; + for (Overlay o : overlays) + { + if (o.getName().equals(overlayName)) + { + overlay = o; + break; + } + } + + if (overlay == null) + { + return; + } + + final Runnable r = overlay.getMenuOptions().get(event.getMenuOption()); + if (r != null) + { + r.run(); + } + } + /** * 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/OverlayRenderer.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java index 6e84e30745..c5991516ff 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 @@ -34,11 +34,16 @@ import java.awt.Rectangle; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.util.List; +import java.util.Set; import javax.inject.Inject; 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.ConfigChanged; import net.runelite.api.events.FocusChanged; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; @@ -48,10 +53,14 @@ 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; +import org.apache.commons.lang3.ArrayUtils; @Singleton public class OverlayRenderer extends MouseAdapter implements KeyListener { + private static final int OVERLAY_MENU_HOTKEY = KeyEvent.VK_SHIFT; private static final int BORDER = 5; private static final int BORDER_TOP = BORDER + 15; private static final int PADDING = 2; @@ -69,6 +78,8 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener private final Point mousePosition = new Point(); private Overlay movedOverlay; private boolean inOverlayDraggingMode; + private boolean inOverlayMenuMode; + private MenuEntry[] menuEntries; // Overlay state validation private Rectangle viewportBounds; @@ -99,6 +110,30 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener if (!event.isFocused()) { inOverlayDraggingMode = false; + inOverlayMenuMode = false; + menuEntries = null; + } + } + + @Subscribe + protected void onClientTick(ClientTick t) + { + if (menuEntries == null || client.isMenuOpen()) + { + menuEntries = null; + return; + } + + client.setMenuEntries(ArrayUtils.addAll(menuEntries, client.getMenuEntries())); + } + + @Subscribe + public void onConfigChanged(ConfigChanged c) + { + if (c.getGroup().equals("runelite") && c.getKey().equals("overlayRightClick")) + { + inOverlayMenuMode = false; + menuEntries = null; } } @@ -227,6 +262,13 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener graphics.draw(bounds); graphics.setColor(previous); } + + final boolean toggleCheck = inOverlayMenuMode || !runeLiteConfig.overlayRightClick(); + final Point mouse = new Point(client.getMouseCanvasPosition().getX(), client.getMouseCanvasPosition().getY()); + if (toggleCheck && bounds.contains(mouse) && !client.isMenuOpen()) + { + menuEntries = createRightClickMenuEntries(overlay); + } } } } @@ -356,6 +398,11 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener { inOverlayDraggingMode = true; } + + if (runeLiteConfig.overlayRightClick() && e.getKeyCode() == OVERLAY_MENU_HOTKEY) + { + inOverlayMenuMode = true; + } } @Override @@ -365,6 +412,12 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener { inOverlayDraggingMode = false; } + + if (runeLiteConfig.overlayRightClick() && e.getKeyCode() == OVERLAY_MENU_HOTKEY) + { + inOverlayMenuMode = false; + menuEntries = null; + } } private void safeRender(Client client, Overlay overlay, OverlayLayer layer, Graphics2D graphics, Point point) @@ -494,4 +547,25 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener new Rectangle(rightChatboxPoint, SNAP_CORNER_SIZE), new Rectangle(canvasTopRightPoint, SNAP_CORNER_SIZE)); } + + private static MenuEntry[] createRightClickMenuEntries(Overlay overlay) + { + final Set options = overlay.getMenuOptions().keySet(); + final MenuEntry[] entries = new MenuEntry[options.size()]; + + // Add in reverse order so they display correctly in the right-click menu + int idx = options.size() - 1; + for (final String option : options) + { + final MenuEntry entry = new MenuEntry(); + entry.setOption(option); + entry.setTarget(ColorUtil.wrapWithColorTag(overlay.getName(), JagexColors.MENU_TARGET)); + entry.setIdentifier(-1); + entry.setType(MenuAction.RUNELITE_OVERLAY.getId()); + + entries[idx--] = entry; + } + + return entries; + } }