From 38e57e6eeb958a0d8d0d9aa07f3f640f4a675272 Mon Sep 17 00:00:00 2001 From: Owain van Brakel Date: Wed, 11 Mar 2020 08:30:49 +0100 Subject: [PATCH] client: Misc --- .../java/net/runelite/api/Perspective.java | 116 ++++++++ .../main/java/net/runelite/api/Varbits.java | 9 + .../net/runelite/api/widgets/WidgetID.java | 1 + .../net/runelite/api/widgets/WidgetInfo.java | 1 + .../ui/components/DragAndDropReorderPane.java | 247 ++++++++++++++++++ .../client/ui/components/PopupMenuOwner.java | 36 +++ 6 files changed, 410 insertions(+) create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/components/DragAndDropReorderPane.java create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/components/PopupMenuOwner.java diff --git a/runelite-api/src/main/java/net/runelite/api/Perspective.java b/runelite-api/src/main/java/net/runelite/api/Perspective.java index eae51d71e5..962055d443 100644 --- a/runelite-api/src/main/java/net/runelite/api/Perspective.java +++ b/runelite-api/src/main/java/net/runelite/api/Perspective.java @@ -36,6 +36,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import static net.runelite.api.Constants.TILE_FLAG_BRIDGE; import net.runelite.api.coords.LocalPoint; +import net.runelite.api.coords.WorldPoint; import net.runelite.api.geometry.RectangleUnion; import net.runelite.api.geometry.Shapes; import net.runelite.api.geometry.SimplePolygon; @@ -760,4 +761,119 @@ public class Perspective return new Point(xOffset, yOffset); } + /** + * Calculates a polygon line from 2 LocalPoints + * + * @param client the game client + * @param startLocation start location of the polygon + * @param endLocation end location of the polygon + * @return a {@link Polygon} + */ + private static Polygon linePoly(@Nonnull Client client, @Nonnull LocalPoint startLocation, @Nonnull LocalPoint endLocation) + { + LocalPoint startPoint = new LocalPoint( + startLocation.getX() - (LOCAL_TILE_SIZE / 2), + startLocation.getY() + (LOCAL_TILE_SIZE / 2)); + LocalPoint endPoint = new LocalPoint( + endLocation.getX() - (LOCAL_TILE_SIZE / 2), + endLocation.getY() + (LOCAL_TILE_SIZE / 2)); + int plane = client.getPlane(); + Point p1 = Perspective.localToCanvas(client, startPoint, plane); + Point p2 = Perspective.localToCanvas(client, endPoint, plane); + if (p1 != null && p2 != null) + { + Polygon polygon = new Polygon(); + polygon.addPoint(p1.getX(), p1.getY()); + polygon.addPoint(p2.getX(), p2.getY()); + return polygon; + } + return null; + } + + /** + * Calculates a list of polygon lines with 2 Worldpoints + * Start location is the South-West WorldPoint, end location is the North-East WorldPoint + * @param client the game client + * @param startLocation start location of the polygon + * @param endLocation end location of the polygon + * @return a {@link List} of {@link Polygon} + */ + public static List getLinePolyList(@Nonnull Client client, @Nonnull WorldPoint startLocation, @Nonnull WorldPoint endLocation) + { + List pList = new ArrayList<>(); + int sizeX = Math.abs(endLocation.getX() - startLocation.getX()) + 1; + int sizeY = Math.abs(endLocation.getY() - startLocation.getY()) + 1; + //HANDLE NORTH + for (int i = 0; i < sizeX; i++) + { + WorldPoint startPoint = new WorldPoint(startLocation.getX() + i, startLocation.getY() + sizeY - 1, startLocation.getPlane()); + WorldPoint endPoint = new WorldPoint(startLocation.getX() + (i + 1), startLocation.getY() + sizeY - 1, startLocation.getPlane()); + LocalPoint localPointStart = LocalPoint.fromWorld(client, startPoint); + LocalPoint localPointEnd = LocalPoint.fromWorld(client, endPoint); + + if (localPointStart != null && localPointEnd != null) + { + Polygon p = linePoly(client, localPointStart, localPointEnd); + if (p != null) + { + pList.add(p); + } + } + } + + //HANDLE SOUTH + for (int i = 0; i < sizeX; i++) + { + WorldPoint startPoint = new WorldPoint(startLocation.getX() + i, startLocation.getY() - 1, startLocation.getPlane()); + WorldPoint endPoint = new WorldPoint(startLocation.getX() + (i + 1), startLocation.getY() - 1, startLocation.getPlane()); + LocalPoint localPointStart = LocalPoint.fromWorld(client, startPoint); + LocalPoint localPointEnd = LocalPoint.fromWorld(client, endPoint); + + if (localPointStart != null && localPointEnd != null) + { + Polygon p = linePoly(client, localPointStart, localPointEnd); + if (p != null) + { + pList.add(p); + } + } + } + + //HANDLE WEST + for (int j = 0; j < sizeY; j++) + { + WorldPoint startPoint = new WorldPoint(startLocation.getX(), startLocation.getY() + (j - 1), startLocation.getPlane()); + WorldPoint endPoint = new WorldPoint(startLocation.getX(), startLocation.getY() + j, startLocation.getPlane()); + LocalPoint localPointStart = LocalPoint.fromWorld(client, startPoint); + LocalPoint localPointEnd = LocalPoint.fromWorld(client, endPoint); + + if (localPointStart != null && localPointEnd != null) + { + Polygon p = linePoly(client, localPointStart, localPointEnd); + if (p != null) + { + pList.add(p); + } + } + } + + //HANDLE EAST + for (int j = 0; j < sizeY; j++) + { + WorldPoint startPoint = new WorldPoint(startLocation.getX() + sizeX, startLocation.getY() + (j - 1), startLocation.getPlane()); + WorldPoint endPoint = new WorldPoint(startLocation.getX() + sizeX, startLocation.getY() + j, startLocation.getPlane()); + LocalPoint localPointStart = LocalPoint.fromWorld(client, startPoint); + LocalPoint localPointEnd = LocalPoint.fromWorld(client, endPoint); + + if (localPointStart != null && localPointEnd != null) + { + Polygon p = linePoly(client, localPointStart, localPointEnd); + if (p != null) + { + pList.add(p); + } + } + } + return pList; + } } diff --git a/runelite-api/src/main/java/net/runelite/api/Varbits.java b/runelite-api/src/main/java/net/runelite/api/Varbits.java index f0d6485389..8f13fbed2c 100644 --- a/runelite-api/src/main/java/net/runelite/api/Varbits.java +++ b/runelite-api/src/main/java/net/runelite/api/Varbits.java @@ -405,6 +405,15 @@ public enum Varbits */ KINGDOM_FAVOR(72), KINGDOM_COFFER(74), + KINGDOM_WORKERS_WOOD(81), + KINGDOM_WORKERS_HERBS(82), + KINGDOM_WORKERS_FISHING(83), + KINGDOM_WORKERS_MINING(84), + KINGDOM_WORKERS_FISH_COOKED_BUTTON(135), // 0 - Raw, 1 - Cooked + KINGDOM_WORKERS_HARDWOOD(2131), + KINGDOM_WORKERS_FARM(2132), + KINGDOM_WORKERS_HARDWOOD_BUTTON(2133), // 0 - Mahogany, 1 - Teak, 2 - Both + KINGDOM_WORKERS_HERBS_BUTTON(2134), // 0 - Herbs, 1 - Flax /** * The Hand in the Sand quest status diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java index 3fc639992a..6e4fd27860 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java @@ -730,6 +730,7 @@ public class WidgetID static final int LIGHT_BOX = 1; static final int LIGHT_BOX_WINDOW = 2; static final int LIGHT_BULB_CONTAINER = 3; + static final int LIGHT_BOX_BUTTON_CONTAINER = 6; static final int BUTTON_A = 8; static final int BUTTON_B = 9; static final int BUTTON_C = 10; diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java index 80c38aec6e..b422ac7f03 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java @@ -443,6 +443,7 @@ public enum WidgetInfo LIGHT_BOX(WidgetID.LIGHT_BOX_GROUP_ID, WidgetID.LightBox.LIGHT_BOX), LIGHT_BOX_CONTENTS(WidgetID.LIGHT_BOX_GROUP_ID, WidgetID.LightBox.LIGHT_BULB_CONTAINER), + LIGHT_BOX_BUTTON_CONTAINER(WidgetID.LIGHT_BOX_GROUP_ID, WidgetID.LightBox.LIGHT_BOX_BUTTON_CONTAINER), LIGHT_BOX_BUTTON_A(WidgetID.LIGHT_BOX_GROUP_ID, WidgetID.LightBox.BUTTON_A), LIGHT_BOX_BUTTON_B(WidgetID.LIGHT_BOX_GROUP_ID, WidgetID.LightBox.BUTTON_B), LIGHT_BOX_BUTTON_C(WidgetID.LIGHT_BOX_GROUP_ID, WidgetID.LightBox.BUTTON_C), diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/DragAndDropReorderPane.java b/runelite-client/src/main/java/net/runelite/client/ui/components/DragAndDropReorderPane.java new file mode 100644 index 0000000000..13cdf27fe8 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/DragAndDropReorderPane.java @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2018, Shingyx + * 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.components; + +import java.awt.Component; +import java.awt.Container; +import java.awt.LayoutManager; +import java.awt.Point; +import java.awt.dnd.DragSource; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.HashMap; +import java.util.Map; +import javax.swing.BoxLayout; +import javax.swing.JLayeredPane; + +/** + * Pane which allows reordering its components via drag and drop. + *

+ * For child components' popup menus, implement the PopupMenuOwner interface in the child components. + */ +public class DragAndDropReorderPane extends JLayeredPane +{ + private Point dragStartPoint; + private Component draggingComponent; + private int dragIndex = -1; + + private final Map popupMenuCandidates = new HashMap<>(); // keyed by mouse button + + public DragAndDropReorderPane() + { + super(); + setLayout(new DragAndDropReorderLayoutManager()); + MouseAdapter mouseAdapter = new DragAndDropReorderMouseAdapter(); + addMouseListener(mouseAdapter); + addMouseMotionListener(mouseAdapter); + } + + @Override + public void setLayout(LayoutManager layoutManager) + { + if (layoutManager != null && !(layoutManager instanceof DragAndDropReorderLayoutManager)) + { + throw new IllegalArgumentException("DragAndDropReorderPane only supports DragAndDropReorderLayoutManager"); + } + super.setLayout(layoutManager); + } + + private void startDragging(Point point) + { + draggingComponent = getDefaultLayerComponentAt(dragStartPoint); + if (draggingComponent == null) + { + dragStartPoint = null; + return; + } + dragIndex = getPosition(draggingComponent); + setLayer(draggingComponent, DRAG_LAYER); + moveDraggingComponent(point); + } + + private void drag(Point point) + { + moveDraggingComponent(point); + + Component component = getDefaultLayerComponentAt(point); + if (component != null) + { + int index = getPosition(component); + dragIndex = index < dragIndex ? index : index + 1; + revalidate(); + } + } + + private void finishDragging() + { + if (draggingComponent != null) + { + setLayer(draggingComponent, DEFAULT_LAYER, dragIndex); + revalidate(); + } + dragStartPoint = null; + draggingComponent = null; + dragIndex = -1; + } + + private void moveDraggingComponent(Point point) + { + // place the center of the dragging component onto the mouse cursor + int y = point.y - draggingComponent.getHeight() / 2; + // clamp the height to stay within the pane + y = Math.max(y, 0); + y = Math.min(y, getHeight() - draggingComponent.getHeight()); + + draggingComponent.setLocation(new Point(0, y)); + } + + private Component getDefaultLayerComponentAt(Point point) + { + for (Component component : getComponentsInLayer(DEFAULT_LAYER)) + { + if (component.contains(point.x - component.getX(), point.y - component.getY())) + { + return component; + } + } + return null; + } + + private class DragAndDropReorderLayoutManager extends BoxLayout + { + private DragAndDropReorderLayoutManager() + { + super(DragAndDropReorderPane.this, BoxLayout.Y_AXIS); + } + + @Override + public void layoutContainer(Container target) + { + if (draggingComponent != null) + { + // temporarily move the dragging component to the default layer for correct layout calculation + Point location = draggingComponent.getLocation(); + setLayer(draggingComponent, DEFAULT_LAYER, dragIndex); + super.layoutContainer(target); + setLayer(draggingComponent, DRAG_LAYER); + draggingComponent.setLocation(location); + } + else + { + super.layoutContainer(target); + } + } + } + + private class DragAndDropReorderMouseAdapter extends MouseAdapter + { + @Override + public void mousePressed(MouseEvent e) + { + Point point = e.getPoint(); + int mouseButton = e.getButton(); + + if (mouseButton == MouseEvent.BUTTON1) + { + // candidate for dragging + if (popupMenuCandidates.isEmpty() && getComponentCount() > 1) + { + dragStartPoint = point; + } + } + else + { + if (dragStartPoint != null) + { + finishDragging(); + } + else + { + // candidate for child popup menu + Component component = getDefaultLayerComponentAt(point); + if (component instanceof PopupMenuOwner) + { + PopupMenuOwner popupMenuCandidate = (PopupMenuOwner) component; + if (e.isPopupTrigger()) + { + popupMenuCandidate.getPopupMenu().show(DragAndDropReorderPane.this, point.x, point.y); + } + else + { + popupMenuCandidates.put(mouseButton, popupMenuCandidate); + } + } + } + } + } + + @Override + public void mouseDragged(MouseEvent e) + { + if (dragStartPoint == null) + { + return; + } + Point point = e.getPoint(); + if (contains(point)) + { + if (draggingComponent == null) + { + if (point.distance(dragStartPoint) > DragSource.getDragThreshold()) + { + startDragging(point); + } + } + else + { + drag(point); + } + } + else + { + finishDragging(); + } + } + + @Override + public void mouseReleased(MouseEvent e) + { + Point point = e.getPoint(); + int mouseButton = e.getButton(); + if (mouseButton == MouseEvent.BUTTON1) + { + finishDragging(); + } + else + { + PopupMenuOwner popupMenuCandidate = popupMenuCandidates.remove(mouseButton); + if (popupMenuCandidate != null && e.isPopupTrigger()) + { + popupMenuCandidate.getPopupMenu().show(DragAndDropReorderPane.this, point.x, point.y); + } + } + } + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/PopupMenuOwner.java b/runelite-client/src/main/java/net/runelite/client/ui/components/PopupMenuOwner.java new file mode 100644 index 0000000000..ba5a019447 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/PopupMenuOwner.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018, Shingyx + * 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.components; + +import javax.swing.JPopupMenu; + +/** + * Represents a UI component which has a popup menu, to be used on child components inside DragAndDropReorderPane. + * This is because using setComponentPopupMenu consumes the MouseEvents required for drag and drop reordering. + */ +public interface PopupMenuOwner +{ + JPopupMenu getPopupMenu(); +} \ No newline at end of file