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 303b423a6a..405fe28080 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 @@ -97,4 +97,15 @@ public abstract class Overlay implements LayoutableRenderableEntity { return false; } + + /** + * Get the parent bounds for overlay dragging. The overlay will + * not be allowed to be moved outside of the parent bounds. + * @return + */ + @Nullable + public Rectangle getParentBounds() + { + return null; + } } 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 d2a4e135a2..6b6c01add2 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 @@ -246,11 +246,14 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener } else { - final Point location = overlay.getBounds().getLocation(); - final Dimension dimension = overlay.getBounds().getSize(); + final Rectangle bounds = overlay.getBounds(); + final Point location = bounds.getLocation(); + final Dimension dimension = bounds.getSize(); + + final Point preferredLocation = overlay.getPreferredLocation(); // If the final position is not modified, layout it - if (overlayPosition != OverlayPosition.DETACHED && (overlay.getPreferredLocation() == null || overlay.getPreferredPosition() != null)) + if (overlayPosition != OverlayPosition.DETACHED && (preferredLocation == null || overlay.getPreferredPosition() != null)) { final Rectangle snapCorner = snapCorners.forPosition(overlayPosition); final Point translation = OverlayUtil.transformPosition(overlayPosition, dimension); @@ -260,21 +263,18 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener } else { - final Point preferredLocation = overlay.getPreferredLocation(); - if (preferredLocation != null) { location.setLocation(preferredLocation); } - final Dimension realDimensions = client.getRealDimensions(); - location.x = Ints.constrainToRange(location.x, 0, Math.max(0, realDimensions.width - dimension.width)); - location.y = Ints.constrainToRange(location.y, 0, Math.max(0, realDimensions.height - dimension.height)); + // Clamp the overlay position to ensure it is on screen or within parent bounds + clampOverlayLocation(location, dimension.width, dimension.height, overlay); } if (overlay.getPreferredSize() != null) { - overlay.getBounds().setSize(overlay.getPreferredSize()); + bounds.setSize(overlay.getPreferredSize()); } safeRender(client, overlay, layer, graphics, location); @@ -287,8 +287,6 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener graphics.setRenderingHints(renderingHints); graphics.setBackground(background); - final Rectangle bounds = overlay.getBounds(); - if (!bounds.isEmpty()) { if (inOverlayManagingMode) @@ -583,12 +581,14 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener } else if (inOverlayDraggingMode) { - final Dimension realDimension = client.getRealDimensions(); - p.translate(-overlayOffset.x, -overlayOffset.y); - p.x = Ints.constrainToRange(p.x, 0, Math.max(0, realDimension.width - currentManagedOverlay.getBounds().width)); - p.y = Ints.constrainToRange(p.y, 0, Math.max(0, realDimension.height - currentManagedOverlay.getBounds().height)); + Point overlayPosition = new Point(p); + overlayPosition.translate(-overlayOffset.x, -overlayOffset.y); // adjust by mouse offset to get overlay position + + // Clamp drag to parent component + final Rectangle overlayBounds = currentManagedOverlay.getBounds(); + clampOverlayLocation(overlayPosition, overlayBounds.width, overlayBounds.height, currentManagedOverlay); currentManagedOverlay.setPreferredPosition(null); - currentManagedOverlay.setPreferredLocation(p); + currentManagedOverlay.setPreferredLocation(overlayPosition); } else { @@ -887,4 +887,29 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener return entries; } + + /** + * Adjust the given overlay position to be within its parent's bounds. + * + * @param overlayPosition the overlay position, which is modified in place + * @param overlayWidth + * @param overlayHeight + * @param overlay the overlay + */ + private void clampOverlayLocation(Point overlayPosition, int overlayWidth, int overlayHeight, Overlay overlay) + { + Rectangle parentBounds = overlay.getParentBounds(); + if (parentBounds == null || parentBounds.isEmpty()) + { + // If no bounds are set, use the full client bounds + Dimension dim = client.getRealDimensions(); + parentBounds = new Rectangle(0, 0, dim.width, dim.height); + } + + // Constrain overlay position to be within the parent bounds + overlayPosition.x = Ints.constrainToRange(overlayPosition.x, parentBounds.x, + Math.max(parentBounds.x, parentBounds.width - overlayWidth)); + overlayPosition.y = Ints.constrainToRange(overlayPosition.y, parentBounds.y, + Math.max(parentBounds.y, parentBounds.height - overlayHeight)); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetOverlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetOverlay.java index 468f195519..d5f57d6590 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetOverlay.java @@ -88,32 +88,10 @@ public class WidgetOverlay extends Overlay return Objects.toString(widgetInfo); } - @Override - public Rectangle getBounds() - { - final Rectangle bounds = super.getBounds(); - final Rectangle parent = getParentBounds(client.getWidget(widgetInfo)); - - if (parent.isEmpty()) - { - return bounds; - } - - int x = bounds.x; - int y = bounds.y; - x = Math.max(parent.x, x); - y = Math.max(parent.y, y); - x = Math.min((int)parent.getMaxX() - bounds.width, x); - y = Math.min((int)parent.getMaxY() - bounds.height, y); - bounds.setLocation(x, y); - return bounds; - } - @Override public Dimension render(Graphics2D graphics) { final Widget widget = client.getWidget(widgetInfo); - final Rectangle bounds = super.getBounds(); final Rectangle parent = getParentBounds(widget); if (parent.isEmpty()) @@ -121,15 +99,8 @@ public class WidgetOverlay extends Overlay return null; } - int x = bounds.x; - int y = bounds.y; - x = Math.max(parent.x, x); - y = Math.max(parent.y, y); - x = Math.min((int)parent.getMaxX() - bounds.width, x); - y = Math.min((int)parent.getMaxY() - bounds.height, y); - bounds.setLocation(x, y); - widget.setOriginalX(0); - widget.setOriginalY(0); + final Rectangle bounds = getBounds(); + // The widget relative pos is relative to the parent widget.setRelativeX(bounds.x - parent.x); widget.setRelativeY(bounds.y - parent.y); return new Dimension(widget.getWidth(), widget.getHeight()); @@ -137,11 +108,6 @@ public class WidgetOverlay extends Overlay private Rectangle getParentBounds(final Widget widget) { - if (!client.isClientThread()) - { - return parentBounds; - } - if (widget == null || widget.isHidden()) { parentBounds.setBounds(new Rectangle()); @@ -157,13 +123,27 @@ public class WidgetOverlay extends Overlay } else { - bounds = new Rectangle(parent.getCanvasLocation().getX(), parent.getCanvasLocation().getY(), parent.getWidth(), parent.getHeight()); + bounds = parent.getBounds(); } parentBounds.setBounds(bounds); return bounds; } + @Override + public Rectangle getParentBounds() + { + if (!client.isClientThread()) + { + // During overlay drag this is called on the EDT, so we just + // cache and reuse the last known parent bounds. + return parentBounds; + } + + final Widget widget = client.getWidget(widgetInfo); + return getParentBounds(widget); + } + private static class XpTrackerWidgetOverlay extends WidgetOverlay { private XpTrackerWidgetOverlay(Client client, WidgetInfo widgetInfo, OverlayPosition overlayPosition)