Add overlay menu support

Co-authored-by: TheStonedTurtle <TheStonedTurtle@users.noreply.github.com>
This commit is contained in:
Adam
2019-01-14 19:45:46 -05:00
parent ccae0c2c48
commit aabb1dae7b
7 changed files with 219 additions and 7 deletions

View File

@@ -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),

View File

@@ -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 + '}';
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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;
}

View File

@@ -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<OverlayMenuEntry> 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

View File

@@ -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<OverlayLayer, List<Overlay>> 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<Overlay> optionalOverlay = overlays.stream().filter(o -> overlayId(o) == event.getId()).findAny();
if (optionalOverlay.isPresent())
{
Overlay overlay = optionalOverlay.get();
List<OverlayMenuEntry> menuEntries = overlay.getMenuEntries();
Optional<OverlayMenuEntry> 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<OverlayMenuEntry> menuEntries = overlay.getMenuEntries();
menuEntries.add(overlayMenuEntry);
}
public void removeMenu(Overlay overlay, String option)
{
List<OverlayMenuEntry> 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
*

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2019, Adam <Adam@sigterm.info>
* 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;
}

View File

@@ -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<OverlayMenuEntry> 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;
}
}