runelite-client: use menu entries for focusing world map points

The previous way had usability issues since it would cover up the close
button, consumed clicks for no reason on any icon, making it difficult
to pan around the map when zoomed out, and had no tooltips.

It previously also accessed widgets on the EDT, which is a threading
issue.
This commit is contained in:
Max Weber
2021-06-16 11:39:48 -06:00
committed by Adam
parent 2ca169ac3d
commit 1d7172c099
9 changed files with 98 additions and 190 deletions

View File

@@ -172,6 +172,7 @@ public class WidgetID
{
static final int MAPVIEW = 7;
static final int OVERVIEW_MAP = 10;
static final int BOTTOM_BAR = 22;
static final int SEARCH = 25;
static final int SURFACE_SELECTOR = 33;
static final int TOOLTIP = 41;

View File

@@ -51,6 +51,7 @@ public enum WidgetInfo
WORLD_MAP_VIEW(WidgetID.WORLD_MAP_GROUP_ID, WidgetID.WorldMap.MAPVIEW),
WORLD_MAP_OVERVIEW_MAP(WidgetID.WORLD_MAP_GROUP_ID, WidgetID.WorldMap.OVERVIEW_MAP),
WORLD_MAP_BOTTOM_BAR(WidgetID.WORLD_MAP_GROUP_ID, WidgetID.WorldMap.BOTTOM_BAR),
WORLD_MAP_SEARCH(WidgetID.WORLD_MAP_GROUP_ID, WidgetID.WorldMap.SEARCH),
WORLD_MAP_SURFACE_SELECTOR(WidgetID.WORLD_MAP_GROUP_ID, WidgetID.WorldMap.SURFACE_SELECTOR),
WORLD_MAP_TOOLTIP(WidgetID.WORLD_MAP_GROUP_ID, WidgetID.WorldMap.TOOLTIP),

View File

@@ -326,6 +326,7 @@ public class RuneLite
// Add core overlays
WidgetOverlay.createOverlays(client).forEach(overlayManager::add);
overlayManager.add(worldMapOverlay.get());
eventBus.register(worldMapOverlay.get());
overlayManager.add(tooltipOverlay.get());
}

View File

@@ -51,6 +51,7 @@ class ClueScrollWorldMapPoint extends WorldMapPoint
this.plugin = plugin;
this.setSnapToEdge(true);
this.setJumpOnClick(true);
this.setName("Clue Scroll");
this.setImage(clueScrollWorldImage);
this.setImagePoint(clueScrollWorldImagePoint);
}

View File

@@ -79,7 +79,7 @@ class WorldMapRegionOverlay extends Overlay
{
RenderOverview ro = client.getRenderOverview();
Widget map = client.getWidget(WidgetInfo.WORLD_MAP_VIEW);
Float pixelsPerTile = ro.getWorldMapZoom();
float pixelsPerTile = ro.getWorldMapZoom();
if (map == null)
{

View File

@@ -45,6 +45,7 @@ class PartyWorldMapPoint extends WorldMapPoint
this.member = member;
this.setSnapToEdge(true);
this.setJumpOnClick(true);
this.setName(member.getName());
this.setImagePoint(new Point(
ARROW.getWidth() / 2,
ARROW.getHeight()));

View File

@@ -25,33 +25,43 @@
package net.runelite.client.ui.overlay.worldmap;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Area;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Arrays;
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.MenuEntry;
import net.runelite.api.Point;
import net.runelite.api.RenderOverview;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.api.widgets.JavaScriptCallback;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetID;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.input.MouseManager;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.JagexColors;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.OverlayPriority;
import net.runelite.client.util.ColorUtil;
@Singleton
public class WorldMapOverlay extends Overlay
{
private static final String FOCUS_ON = "Focus on";
private static final int TOOLTIP_OFFSET_HEIGHT = 25;
private static final int TOOLTIP_OFFSET_WIDTH = 5;
private static final int TOOLTIP_PADDING_HEIGHT = 1;
@@ -63,12 +73,12 @@ public class WorldMapOverlay extends Overlay
private final WorldMapPointManager worldMapPointManager;
private final Client client;
private final List<MenuEntry> mapMenuEntries = new ArrayList<>();
@Inject
private WorldMapOverlay(
Client client,
WorldMapPointManager worldMapPointManager,
MouseManager mouseManager,
WorldMapOverlayMouseListener worldMapOverlayMouseListener)
WorldMapPointManager worldMapPointManager)
{
this.client = client;
this.worldMapPointManager = worldMapPointManager;
@@ -76,7 +86,6 @@ public class WorldMapOverlay extends Overlay
setPriority(OverlayPriority.HIGHEST);
setLayer(OverlayLayer.MANUAL);
drawAfterInterface(WidgetID.WORLD_MAP_GROUP_ID);
mouseManager.registerMouseListener(worldMapOverlayMouseListener);
}
@Override
@@ -90,17 +99,44 @@ public class WorldMapOverlay extends Overlay
}
Widget widget = client.getWidget(WidgetInfo.WORLD_MAP_VIEW);
if (widget == null)
Widget bottomBar = client.getWidget(WidgetInfo.WORLD_MAP_BOTTOM_BAR);
if (widget == null || bottomBar == null)
{
return null;
}
bottomBar.setOnTimerListener((JavaScriptCallback) ev ->
{
if (client.isMenuOpen() || mapMenuEntries.isEmpty())
{
return;
}
MenuEntry[] entries = client.getMenuEntries();
int end = entries.length;
entries = Arrays.copyOf(entries, end + mapMenuEntries.size());
for (int i = 0; i < mapMenuEntries.size(); i++)
{
entries[end + i] = mapMenuEntries.get(i);
}
client.setMenuEntries(entries);
});
bottomBar.setHasListener(true);
final Rectangle worldMapRectangle = widget.getBounds();
final Area mapViewArea = getWorldMapClipArea(worldMapRectangle);
final Rectangle canvasBounds = new Rectangle(0, 0, client.getCanvasWidth(), client.getCanvasHeight());
final Area canvasViewArea = getWorldMapClipArea(canvasBounds);
Area currentClip = null;
Point mousePos = client.getMouseCanvasPosition();
if (!canvasViewArea.contains(mousePos.getX(), mousePos.getY()))
{
mousePos = null;
}
mapMenuEntries.clear();
WorldMapPoint tooltipPoint = null;
for (WorldMapPoint worldPoint : points)
@@ -111,10 +147,8 @@ public class WorldMapOverlay extends Overlay
if (image != null && point != null)
{
Point drawPoint = mapWorldPointToGraphicsPoint(point);
if (drawPoint == null)
{
worldPoint.setClickbox(null);
continue;
}
@@ -183,15 +217,40 @@ public class WorldMapOverlay extends Overlay
graphics.drawImage(image, drawX, drawY, null);
Rectangle clickbox = new Rectangle(drawX, drawY, image.getWidth(), image.getHeight());
worldPoint.setClickbox(clickbox);
if (worldPoint.isTooltipVisible())
if (mousePos != null && clickbox.contains(mousePos.getX(), mousePos.getY()))
{
tooltipPoint = worldPoint;
if (!Strings.isNullOrEmpty(worldPoint.getTooltip()))
{
tooltipPoint = worldPoint;
}
if (worldPoint.isJumpOnClick())
{
assert worldPoint.getName() != null;
WorldPoint target = worldPoint.getTarget();
if (target == null)
{
target = worldPoint.getWorldPoint();
}
MenuEntry entry = new MenuEntry();
entry.setType(MenuAction.RUNELITE.getId());
entry.setOption(FOCUS_ON);
entry.setTarget(ColorUtil.wrapWithColorTag(worldPoint.getName(), JagexColors.MENU_TARGET));
entry.setIdentifier(target.getPlane() << 28 | target.getX() << 14 | target.getY());
mapMenuEntries.add(entry);
}
}
}
}
final Widget rsTooltip = client.getWidget(WidgetInfo.WORLD_MAP_TOOLTIP);
if (rsTooltip != null)
{
rsTooltip.setHidden(tooltipPoint != null);
}
if (tooltipPoint != null)
{
drawTooltip(graphics, tooltipPoint);
@@ -200,8 +259,24 @@ public class WorldMapOverlay extends Overlay
return null;
}
@Subscribe
private void onMenuOptionClicked(MenuOptionClicked ev)
{
if (ev.getMenuAction() == MenuAction.RUNELITE && FOCUS_ON.equals(ev.getMenuOption()))
{
int pxy = ev.getId();
WorldPoint wp = new WorldPoint(
pxy >> 14 & 0x3fff,
pxy & 0x3fff,
pxy >> 28);
client.getRenderOverview().setWorldMapPositionTarget(wp);
}
}
/**
* Get the screen coordinates for a WorldPoint on the world map
*
* @param worldPoint WorldPoint to get screen coordinates of
* @return Point of screen coordinates of the center of the world point
*/
@@ -214,7 +289,7 @@ public class WorldMapOverlay extends Overlay
return null;
}
Float pixelsPerTile = ro.getWorldMapZoom();
float pixelsPerTile = ro.getWorldMapZoom();
Widget map = client.getWidget(WidgetInfo.WORLD_MAP_VIEW);
if (map != null)

View File

@@ -1,166 +0,0 @@
/*
* Copyright (c) 2018, Morgan Lewis <https://github.com/MESLewis>
* 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.worldmap;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.swing.SwingUtilities;
import net.runelite.api.Client;
import net.runelite.api.Point;
import net.runelite.api.RenderOverview;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.input.MouseAdapter;
@Singleton
public class WorldMapOverlayMouseListener extends MouseAdapter
{
private final Client client;
private final WorldMapPointManager worldMapPointManager;
private WorldMapPoint tooltipPoint = null;
@Inject
private WorldMapOverlayMouseListener(Client client, WorldMapPointManager worldMapPointManager)
{
this.client = client;
this.worldMapPointManager = worldMapPointManager;
}
@Override
public MouseEvent mousePressed(MouseEvent e)
{
final List<WorldMapPoint> worldMapPoints = worldMapPointManager.getWorldMapPoints();
if (SwingUtilities.isLeftMouseButton(e) && !worldMapPoints.isEmpty())
{
Point mousePos = client.getMouseCanvasPosition();
final Widget view = client.getWidget(WidgetInfo.WORLD_MAP_VIEW);
if (view == null)
{
return e;
}
for (WorldMapPoint worldMapPoint : worldMapPoints)
{
Rectangle clickbox = worldMapPoint.getClickbox();
if (clickbox != null && clickbox.contains(mousePos.getX(), mousePos.getY()))
{
if (worldMapPoint.isJumpOnClick())
{
// jump map to target, or position of point
WorldPoint target = worldMapPoint.getTarget();
if (target == null)
{
target = worldMapPoint.getWorldPoint();
}
RenderOverview renderOverview = client.getRenderOverview();
renderOverview.setWorldMapPositionTarget(target);
}
e.consume();
return worldMapPoint.onClick(e);
}
}
}
return e;
}
@Override
public MouseEvent mouseMoved(MouseEvent mouseEvent)
{
final List<WorldMapPoint> worldMapPoints = worldMapPointManager.getWorldMapPoints();
if (worldMapPoints.isEmpty())
{
return mouseEvent;
}
final Point mousePos = client.getMouseCanvasPosition();
final Widget view = client.getWidget(WidgetInfo.WORLD_MAP_VIEW);
if (view == null)
{
return mouseEvent;
}
final Rectangle worldMapDisplay = view.getBounds();
if (worldMapDisplay == null || !worldMapDisplay.contains(mousePos.getX(), mousePos.getY()))
{
if (tooltipPoint != null)
{
tooltipPoint.setTooltipVisible(false);
tooltipPoint = null;
final Widget rsTooltip = client.getWidget(WidgetInfo.WORLD_MAP_TOOLTIP);
if (rsTooltip != null)
{
rsTooltip.setHidden(false);
}
}
return mouseEvent;
}
if (tooltipPoint != null)
{
if (tooltipPoint.getClickbox() != null
&& tooltipPoint.getClickbox().contains(mousePos.getX(), mousePos.getY()))
{
return mouseEvent;
}
else
{
tooltipPoint.setTooltipVisible(false);
tooltipPoint = null;
final Widget rsTooltip = client.getWidget(WidgetInfo.WORLD_MAP_TOOLTIP);
if (rsTooltip != null)
{
rsTooltip.setHidden(false);
}
}
}
for (WorldMapPoint worldMapPoint : worldMapPointManager.getWorldMapPoints())
{
if (worldMapPoint.getClickbox() != null
&& worldMapPoint.getClickbox().contains(mousePos.getX(), mousePos.getY())
&& worldMapPoint.getTooltip() != null)
{
worldMapPoint.setTooltipVisible(true);
tooltipPoint = worldMapPoint;
final Widget rsTooltip = client.getWidget(WidgetInfo.WORLD_MAP_TOOLTIP);
if (rsTooltip != null)
{
rsTooltip.setHidden(true);
}
return mouseEvent;
}
}
return mouseEvent;
}
}

View File

@@ -24,8 +24,6 @@
*/
package net.runelite.client.ui.overlay.worldmap;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import javax.annotation.Nullable;
import lombok.Data;
@@ -53,8 +51,6 @@ public class WorldMapPoint
*/
private Point imagePoint;
private Rectangle clickbox;
private boolean snapToEdge;
private boolean currentlyEdgeSnapped;
@@ -64,7 +60,10 @@ public class WorldMapPoint
*/
private boolean jumpOnClick;
private boolean tooltipVisible;
/**
* Name in menu option when {@link #jumpOnClick} is set
*/
private String name;
private String tooltip;
@@ -74,11 +73,6 @@ public class WorldMapPoint
this.image = image;
}
public MouseEvent onClick(MouseEvent e)
{
return e;
}
public void onEdgeSnap()
{
}