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 a00a1ce2f0..8f8033ad8c 100644 --- a/runelite-api/src/main/java/net/runelite/api/MenuAction.java +++ b/runelite-api/src/main/java/net/runelite/api/MenuAction.java @@ -277,6 +277,10 @@ public enum MenuAction * a player and have its identifier set to a player index. */ RUNELITE_PLAYER(1503), + /** + * Menu action for InfoBox menu entries + */ + RUNELITE_INFOBOX(1504), /** * Menu action triggered when the id is not defined in this class. diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLite.java b/runelite-client/src/main/java/net/runelite/client/RuneLite.java index 843fc579ac..c8cd6f352f 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -361,6 +361,7 @@ public class RuneLite eventBus.register(lootManager.get()); eventBus.register(chatboxPanelManager.get()); eventBus.register(hooks.get()); + eventBus.register(infoBoxOverlay.get()); // Add core overlays WidgetOverlay.createOverlays(client).forEach(overlayManager::add); diff --git a/runelite-client/src/main/java/net/runelite/client/events/InfoBoxMenuClicked.java b/runelite-client/src/main/java/net/runelite/client/events/InfoBoxMenuClicked.java new file mode 100644 index 0000000000..c23b26a75b --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/events/InfoBoxMenuClicked.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020, 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.Value; +import net.runelite.client.ui.overlay.OverlayMenuEntry; +import net.runelite.client.ui.overlay.infobox.InfoBox; + +@Value +public class InfoBoxMenuClicked +{ + private OverlayMenuEntry entry; + private InfoBox infoBox; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java index 257c231077..3f8ee667ff 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java @@ -33,9 +33,11 @@ import javax.inject.Inject; import javax.swing.JButton; import javax.swing.JPanel; import net.runelite.api.Client; +import net.runelite.api.MenuAction; import net.runelite.client.Notifier; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.PluginPanel; +import net.runelite.client.ui.overlay.OverlayMenuEntry; import net.runelite.client.ui.overlay.infobox.Counter; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import net.runelite.client.util.ImageUtil; @@ -166,7 +168,12 @@ class DevToolsPanel extends PluginPanel }); final JButton newInfoboxBtn = new JButton("Infobox"); - newInfoboxBtn.addActionListener(e -> infoBoxManager.addInfoBox(new Counter(ImageUtil.getResourceStreamFromClass(getClass(), "devtools_icon.png"), plugin, 42))); + newInfoboxBtn.addActionListener(e -> + { + Counter counter = new Counter(ImageUtil.getResourceStreamFromClass(getClass(), "devtools_icon.png"), plugin, 42); + counter.getMenuEntries().add(new OverlayMenuEntry(MenuAction.RUNELITE_INFOBOX, "Test", "DevTools")); + infoBoxManager.addInfoBox(counter); + }); container.add(newInfoboxBtn); final JButton clearInfoboxBtn = new JButton("Clear Infobox"); 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 183f92a5c7..b64f845695 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,7 +33,6 @@ 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; @@ -136,7 +135,8 @@ public class OverlayManager @Subscribe public void onMenuOptionClicked(MenuOptionClicked event) { - if (event.getMenuAction() != MenuAction.RUNELITE_OVERLAY) + MenuAction menuAction = event.getMenuAction(); + if (menuAction != MenuAction.RUNELITE_OVERLAY && menuAction != MenuAction.RUNELITE_OVERLAY_CONFIG) { return; } @@ -147,12 +147,13 @@ public class OverlayManager if (overlay != null) { List menuEntries = overlay.getMenuEntries(); - Optional optionalOverlayMenuEntry = menuEntries.stream() + OverlayMenuEntry overlayMenuEntry = menuEntries.stream() .filter(me -> me.getOption().equals(event.getMenuOption())) - .findAny(); - if (optionalOverlayMenuEntry.isPresent()) + .findAny() + .orElse(null); + if (overlayMenuEntry != null) { - eventBus.post(new OverlayMenuClicked(optionalOverlayMenuEntry.get(), overlay)); + eventBus.post(new OverlayMenuClicked(overlayMenuEntry, overlay)); } } } 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 0a903d5e3e..a2419df6a8 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 @@ -46,7 +46,6 @@ import javax.swing.SwingUtilities; import lombok.extern.slf4j.Slf4j; 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.BeforeRender; import net.runelite.api.events.ClientTick; @@ -807,7 +806,7 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener 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.setType(overlayMenuEntry.getMenuAction().getId()); entry.setIdentifier(overlayManager.getOverlays().indexOf(overlay)); // overlay id entries[i] = entry; diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/InfoBoxComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/InfoBoxComponent.java index a72d13f73d..cb02a50672 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/InfoBoxComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/InfoBoxComponent.java @@ -35,6 +35,7 @@ import java.awt.image.BufferedImage; import lombok.Getter; import lombok.Setter; import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.overlay.infobox.InfoBox; @Setter public class InfoBoxComponent implements LayoutableRenderableEntity @@ -54,6 +55,8 @@ public class InfoBoxComponent implements LayoutableRenderableEntity private Color color = Color.WHITE; private Color backgroundColor = ComponentConstants.STANDARD_BACKGROUND_COLOR; private BufferedImage image; + @Getter + private InfoBox infoBox; @Override public Dimension render(Graphics2D graphics) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBox.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBox.java index 777bb9d634..364979cf36 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBox.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBox.java @@ -26,11 +26,14 @@ package net.runelite.client.ui.overlay.infobox; import java.awt.Color; import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.List; import javax.annotation.Nonnull; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; import net.runelite.client.plugins.Plugin; +import net.runelite.client.ui.overlay.OverlayMenuEntry; public abstract class InfoBox { @@ -54,6 +57,10 @@ public abstract class InfoBox @Setter private String tooltip; + @Getter + @Setter + private List menuEntries = new ArrayList<>(); + public InfoBox(BufferedImage image, @Nonnull Plugin plugin) { this.plugin = plugin; diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java index c04da4b6ee..387eda76ab 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java @@ -31,11 +31,18 @@ import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; +import java.util.Collections; import java.util.List; import javax.inject.Inject; import javax.inject.Singleton; import net.runelite.api.Client; +import net.runelite.api.MenuAction; +import net.runelite.api.events.MenuOptionClicked; import net.runelite.client.config.RuneLiteConfig; +import net.runelite.client.eventbus.EventBus; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.InfoBoxMenuClicked; +import net.runelite.client.ui.overlay.OverlayMenuEntry; import net.runelite.client.ui.overlay.OverlayPanel; import net.runelite.client.ui.overlay.OverlayPosition; import net.runelite.client.ui.overlay.components.ComponentOrientation; @@ -54,18 +61,23 @@ public class InfoBoxOverlay extends OverlayPanel private final TooltipManager tooltipManager; private final Client client; private final RuneLiteConfig config; + private final EventBus eventBus; + + private InfoBoxComponent hoveredComponent; @Inject private InfoBoxOverlay( InfoBoxManager infoboxManager, TooltipManager tooltipManager, Client client, - RuneLiteConfig config) + RuneLiteConfig config, + EventBus eventBus) { this.tooltipManager = tooltipManager; this.infoboxManager = infoboxManager; this.client = client; this.config = config; + this.eventBus = eventBus; setPosition(OverlayPosition.TOP_LEFT); setClearChildren(false); @@ -80,6 +92,12 @@ public class InfoBoxOverlay extends OverlayPanel { final List infoBoxes = infoboxManager.getInfoBoxes(); + final boolean menuOpen = client.isMenuOpen(); + if (!menuOpen) + { + hoveredComponent = null; + } + if (infoBoxes.isEmpty()) { return null; @@ -112,6 +130,7 @@ public class InfoBoxOverlay extends OverlayPanel infoBoxComponent.setTooltip(box.getTooltip()); infoBoxComponent.setPreferredSize(new Dimension(config.infoBoxSize(), config.infoBoxSize())); infoBoxComponent.setBackgroundColor(config.overlayBackgroundColor()); + infoBoxComponent.setInfoBox(box); panelComponent.getChildren().add(infoBoxComponent); } @@ -122,25 +141,55 @@ public class InfoBoxOverlay extends OverlayPanel for (final LayoutableRenderableEntity child : panelComponent.getChildren()) { - if (child instanceof InfoBoxComponent) + final InfoBoxComponent component = (InfoBoxComponent) child; + + // Create intersection rectangle + final Rectangle intersectionRectangle = new Rectangle(component.getBounds()); + intersectionRectangle.translate(getBounds().x, getBounds().y); + + if (intersectionRectangle.contains(mouse)) { - final InfoBoxComponent component = (InfoBoxComponent) child; - - if (!Strings.isNullOrEmpty(component.getTooltip())) + final String tooltip = component.getTooltip(); + if (!Strings.isNullOrEmpty(tooltip)) { - // Create intersection rectangle - final Rectangle intersectionRectangle = new Rectangle(component.getBounds()); - intersectionRectangle.translate(getBounds().x, getBounds().y); - - if (intersectionRectangle.contains(mouse)) - { - tooltipManager.add(new Tooltip(component.getTooltip())); - } + tooltipManager.add(new Tooltip(tooltip)); } + + if (!menuOpen) + { + hoveredComponent = component; + } + break; } } panelComponent.getChildren().clear(); return dimension; } + + @Override + public List getMenuEntries() + { + // we dynamically build the menu options based on which infobox is hovered + return hoveredComponent == null ? Collections.emptyList() : hoveredComponent.getInfoBox().getMenuEntries(); + } + + @Subscribe + public void onMenuOptionClicked(MenuOptionClicked menuOptionClicked) + { + if (menuOptionClicked.getMenuAction() != MenuAction.RUNELITE_INFOBOX) + { + return; + } + + InfoBox infoBox = hoveredComponent.getInfoBox(); + OverlayMenuEntry overlayMenuEntry = infoBox.getMenuEntries().stream() + .filter(me -> me.getOption().equals(menuOptionClicked.getMenuOption())) + .findAny() + .orElse(null); + if (overlayMenuEntry != null) + { + eventBus.post(new InfoBoxMenuClicked(overlayMenuEntry, infoBox)); + } + } }