Add support for resizable overlays

Add mostly complete support for resizable overlays. Currently only
screen markers are supported.

Signed-off-by: Tomas Slusny <slusnucky@gmail.com>
This commit is contained in:
Tomas Slusny
2018-07-26 17:55:11 +02:00
committed by Adam
parent ab365d17a1
commit d676542dc2
5 changed files with 279 additions and 81 deletions

View File

@@ -48,6 +48,7 @@ public class ScreenMarkerOverlay extends Overlay
setPosition(OverlayPosition.DETACHED); setPosition(OverlayPosition.DETACHED);
setLayer(OverlayLayer.ALWAYS_ON_TOP); setLayer(OverlayLayer.ALWAYS_ON_TOP);
setPriority(OverlayPriority.HIGH); setPriority(OverlayPriority.HIGH);
setResizable(true);
} }
@Override @Override

View File

@@ -139,6 +139,7 @@ public class ClientUI
private NavigationButton sidebarNavigationButton; private NavigationButton sidebarNavigationButton;
private JButton sidebarNavigationJButton; private JButton sidebarNavigationJButton;
private Dimension lastClientSize; private Dimension lastClientSize;
private Cursor defaultCursor;
@Inject @Inject
private ClientUI( private ClientUI(
@@ -663,6 +664,24 @@ public class ClientUI
giveClientFocus(); giveClientFocus();
} }
/**
* Returns current cursor set on game container
* @return awt cursor
*/
public Cursor getCurrentCursor()
{
return container.getCursor();
}
/**
* Returns current custom cursor or default system cursor if cursor is not set
* @return awt cursor
*/
public Cursor getDefaultCursor()
{
return defaultCursor != null ? defaultCursor : Cursor.getDefaultCursor();
}
/** /**
* Changes cursor for client window. Requires ${@link ClientUI#init()} to be called first. * Changes cursor for client window. Requires ${@link ClientUI#init()} to be called first.
* FIXME: This is working properly only on Windows, Linux and Mac are displaying cursor incorrectly * FIXME: This is working properly only on Windows, Linux and Mac are displaying cursor incorrectly
@@ -678,7 +697,17 @@ public class ClientUI
final java.awt.Point hotspot = new java.awt.Point(0, 0); final java.awt.Point hotspot = new java.awt.Point(0, 0);
final Cursor cursorAwt = Toolkit.getDefaultToolkit().createCustomCursor(image, hotspot, name); final Cursor cursorAwt = Toolkit.getDefaultToolkit().createCustomCursor(image, hotspot, name);
container.setCursor(cursorAwt); defaultCursor = cursorAwt;
setCursor(cursorAwt);
}
/**
* Changes cursor for client window. Requires ${@link ClientUI#init()} to be called first.
* @param cursor awt cursor
*/
public void setCursor(final Cursor cursor)
{
container.setCursor(cursor);
} }
/** /**
@@ -692,6 +721,7 @@ public class ClientUI
return; return;
} }
defaultCursor = null;
container.setCursor(Cursor.getDefaultCursor()); container.setCursor(Cursor.getDefaultCursor());
} }

View File

@@ -49,6 +49,7 @@ public abstract class Overlay implements LayoutableRenderableEntity
private OverlayPriority priority = OverlayPriority.NONE; private OverlayPriority priority = OverlayPriority.NONE;
private OverlayLayer layer = OverlayLayer.UNDER_WIDGETS; private OverlayLayer layer = OverlayLayer.UNDER_WIDGETS;
private final List<OverlayMenuEntry> menuEntries = new ArrayList<>(); private final List<OverlayMenuEntry> menuEntries = new ArrayList<>();
private boolean resizable;
protected Overlay() protected Overlay()
{ {

View File

@@ -263,7 +263,7 @@ public class OverlayManager
saveOverlay(overlay); saveOverlay(overlay);
} }
private synchronized void rebuildOverlayLayers() synchronized void rebuildOverlayLayers()
{ {
for (OverlayLayer l : OverlayLayer.values()) for (OverlayLayer l : OverlayLayer.values())
{ {

View File

@@ -28,6 +28,7 @@ import com.google.common.base.MoreObjects;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
import java.awt.Color; import java.awt.Color;
import java.awt.Composite; import java.awt.Composite;
import java.awt.Cursor;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Paint; import java.awt.Paint;
@@ -58,6 +59,7 @@ import net.runelite.client.input.KeyListener;
import net.runelite.client.input.KeyManager; import net.runelite.client.input.KeyManager;
import net.runelite.client.input.MouseAdapter; import net.runelite.client.input.MouseAdapter;
import net.runelite.client.input.MouseManager; import net.runelite.client.input.MouseManager;
import net.runelite.client.ui.ClientUI;
import net.runelite.client.ui.JagexColors; import net.runelite.client.ui.JagexColors;
import net.runelite.client.util.ColorUtil; import net.runelite.client.util.ColorUtil;
@@ -68,21 +70,29 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener
private static final int BORDER = 5; private static final int BORDER = 5;
private static final int BORDER_TOP = BORDER + 15; private static final int BORDER_TOP = BORDER + 15;
private static final int PADDING = 2; private static final int PADDING = 2;
private static final int MIN_OVERLAY_SIZE = 32;
private static final int OVERLAY_RESIZE_TOLERANCE = 5;
private static final Dimension SNAP_CORNER_SIZE = new Dimension(80, 80); private static final Dimension SNAP_CORNER_SIZE = new Dimension(80, 80);
private static final Color SNAP_CORNER_COLOR = new Color(0, 255, 255, 50); private static final Color SNAP_CORNER_COLOR = new Color(0, 255, 255, 50);
private static final Color SNAP_CORNER_ACTIVE_COLOR = new Color(0, 255, 0, 100); private static final Color SNAP_CORNER_ACTIVE_COLOR = new Color(0, 255, 0, 100);
private static final Color MOVING_OVERLAY_COLOR = new Color(255, 255, 0, 100); private static final Color MOVING_OVERLAY_COLOR = new Color(255, 255, 0, 100);
private static final Color MOVING_OVERLAY_ACTIVE_COLOR = new Color(255, 255, 0, 200); private static final Color MOVING_OVERLAY_ACTIVE_COLOR = new Color(255, 255, 0, 200);
private static final Color MOVING_OVERLAY_RESIZING_COLOR = new Color(255, 0, 255, 200);
private final Client client; private final Client client;
private final OverlayManager overlayManager; private final OverlayManager overlayManager;
private final RuneLiteConfig runeLiteConfig; private final RuneLiteConfig runeLiteConfig;
private final ClientUI clientUI;
// Overlay movement variables // Overlay movement variables
private final Point overlayOffset = new Point(); private final Point overlayOffset = new Point();
private final Point mousePosition = new Point(); private final Point mousePosition = new Point();
private Overlay movedOverlay; private Overlay currentManagedOverlay;
private Rectangle currentManagedBounds;
private boolean inOverlayManagingMode;
private boolean inOverlayResizingMode;
private boolean inOverlayDraggingMode; private boolean inOverlayDraggingMode;
private boolean inMenuEntryMode; private boolean inMenuEntryMode;
private boolean startedMovingOverlay;
private MenuEntry[] menuEntries; private MenuEntry[] menuEntries;
// Overlay state validation // Overlay state validation
@@ -99,11 +109,13 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener
final OverlayManager overlayManager, final OverlayManager overlayManager,
final RuneLiteConfig runeLiteConfig, final RuneLiteConfig runeLiteConfig,
final MouseManager mouseManager, final MouseManager mouseManager,
final KeyManager keyManager) final KeyManager keyManager,
final ClientUI clientUI)
{ {
this.client = client; this.client = client;
this.overlayManager = overlayManager; this.overlayManager = overlayManager;
this.runeLiteConfig = runeLiteConfig; this.runeLiteConfig = runeLiteConfig;
this.clientUI = clientUI;
keyManager.registerKeyListener(this); keyManager.registerKeyListener(this);
mouseManager.registerMouseListener(this); mouseManager.registerMouseListener(this);
} }
@@ -113,7 +125,8 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener
{ {
if (!event.isFocused()) if (!event.isFocused())
{ {
inOverlayDraggingMode = false; inOverlayManagingMode = false;
resetOverlayManagementMode();
inMenuEntryMode = false; inMenuEntryMode = false;
menuEntries = null; menuEntries = null;
} }
@@ -182,7 +195,7 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener
OverlayUtil.setGraphicProperties(graphics); OverlayUtil.setGraphicProperties(graphics);
// Draw snap corners // Draw snap corners
if (layer == OverlayLayer.UNDER_WIDGETS && movedOverlay != null && movedOverlay.getPosition() != OverlayPosition.DETACHED) if (inOverlayDraggingMode && layer == OverlayLayer.UNDER_WIDGETS && currentManagedOverlay != null && currentManagedOverlay.getPosition() != OverlayPosition.DETACHED)
{ {
final OverlayBounds translatedSnapCorners = snapCorners.translated( final OverlayBounds translatedSnapCorners = snapCorners.translated(
-SNAP_CORNER_SIZE.width, -SNAP_CORNER_SIZE.width,
@@ -274,12 +287,19 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener
if (!bounds.isEmpty()) if (!bounds.isEmpty())
{ {
if (inOverlayDraggingMode) if (inOverlayManagingMode)
{ {
final Color previous = graphics.getColor(); if (inOverlayResizingMode && currentManagedOverlay == overlay)
graphics.setColor(movedOverlay == overlay ? MOVING_OVERLAY_ACTIVE_COLOR : MOVING_OVERLAY_COLOR); {
graphics.setColor(MOVING_OVERLAY_RESIZING_COLOR);
}
else
{
graphics.setColor(inOverlayDraggingMode && currentManagedOverlay == overlay ? MOVING_OVERLAY_ACTIVE_COLOR : MOVING_OVERLAY_COLOR);
}
graphics.draw(bounds); graphics.draw(bounds);
graphics.setColor(previous); graphics.setPaint(paint);
} }
if (!client.isMenuOpen() && !client.getSpellSelected() && bounds.contains(mouse)) if (!client.isMenuOpen() && !client.getSpellSelected() && bounds.contains(mouse))
@@ -299,7 +319,7 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener
@Override @Override
public MouseEvent mousePressed(MouseEvent mouseEvent) public MouseEvent mousePressed(MouseEvent mouseEvent)
{ {
if (!inOverlayDraggingMode) if (!inOverlayManagingMode)
{ {
return mouseEvent; return mouseEvent;
} }
@@ -307,112 +327,248 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener
final Point mousePoint = mouseEvent.getPoint(); final Point mousePoint = mouseEvent.getPoint();
mousePosition.setLocation(mousePoint); mousePosition.setLocation(mousePoint);
synchronized (overlayManager) if (currentManagedOverlay == null)
{ {
for (Overlay overlay : overlayManager.getOverlays()) return mouseEvent;
}
if (SwingUtilities.isRightMouseButton(mouseEvent))
{
overlayManager.resetOverlay(currentManagedOverlay);
}
else if (SwingUtilities.isLeftMouseButton(mouseEvent))
{
final Point offset = new Point(mousePoint.x, mousePoint.y);
offset.translate(-currentManagedOverlay.getBounds().x, -currentManagedOverlay.getBounds().y);
overlayOffset.setLocation(offset);
inOverlayResizingMode = currentManagedOverlay != null && currentManagedOverlay.isResizable() && clientUI.getCurrentCursor() != clientUI.getDefaultCursor();
inOverlayDraggingMode = !inOverlayResizingMode;
startedMovingOverlay = true;
currentManagedBounds = new Rectangle(currentManagedOverlay.getBounds());
}
else
{
return mouseEvent;
}
mouseEvent.consume();
return mouseEvent;
}
@Override
public MouseEvent mouseMoved(MouseEvent mouseEvent)
{
if (!inOverlayManagingMode)
{
return mouseEvent;
}
final Point mousePoint = mouseEvent.getPoint();
mousePosition.setLocation(mousePoint);
if (!inOverlayResizingMode && !inOverlayDraggingMode)
{
currentManagedOverlay = null;
synchronized (overlayManager)
{ {
final OverlayPosition overlayPosition = getCorrectedOverlayPosition(overlay); for (Overlay overlay : overlayManager.getOverlays())
if (overlayPosition == OverlayPosition.DYNAMIC || overlayPosition == OverlayPosition.TOOLTIP)
{ {
continue; final Rectangle bounds = overlay.getBounds();
} if (bounds.contains(mousePoint))
if (overlay.getBounds().contains(mousePoint))
{
if (SwingUtilities.isRightMouseButton(mouseEvent))
{ {
overlayManager.resetOverlay(overlay); currentManagedOverlay = overlay;
break;
} }
else
{
final Point offset = new Point(mousePoint.x, mousePoint.y);
offset.translate(-overlay.getBounds().x, -overlay.getBounds().y);
overlayOffset.setLocation(offset);
mousePoint.translate(-offset.x, -offset.y);
movedOverlay = overlay;
movedOverlay.setPreferredPosition(null);
movedOverlay.setPreferredLocation(mousePoint);
overlayManager.saveOverlay(movedOverlay);
}
mouseEvent.consume();
break;
} }
} }
} }
if (currentManagedOverlay == null || !currentManagedOverlay.isResizable())
{
clientUI.setCursor(clientUI.getDefaultCursor());
return mouseEvent;
}
final Rectangle toleranceRect = new Rectangle(currentManagedOverlay.getBounds());
toleranceRect.grow(-OVERLAY_RESIZE_TOLERANCE, -OVERLAY_RESIZE_TOLERANCE);
final int outcode = toleranceRect.outcode(mouseEvent.getPoint());
switch (outcode)
{
case Rectangle.OUT_TOP:
clientUI.setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR));
break;
case Rectangle.OUT_TOP | Rectangle.OUT_LEFT:
clientUI.setCursor(Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR));
break;
case Rectangle.OUT_LEFT:
clientUI.setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR));
break;
case Rectangle.OUT_LEFT | Rectangle.OUT_BOTTOM:
clientUI.setCursor(Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR));
break;
case Rectangle.OUT_BOTTOM:
clientUI.setCursor(Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR));
break;
case Rectangle.OUT_BOTTOM | Rectangle.OUT_RIGHT:
clientUI.setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR));
break;
case Rectangle.OUT_RIGHT:
clientUI.setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
break;
case Rectangle.OUT_RIGHT | Rectangle.OUT_TOP:
clientUI.setCursor(Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR));
break;
default:
// center
clientUI.setCursor(clientUI.getDefaultCursor());
}
return mouseEvent; return mouseEvent;
} }
@Override @Override
public MouseEvent mouseDragged(MouseEvent mouseEvent) public MouseEvent mouseDragged(MouseEvent mouseEvent)
{ {
if (!inOverlayDraggingMode) if (!inOverlayManagingMode)
{
return mouseEvent;
}
final Point p = mouseEvent.getPoint();
mousePosition.setLocation(p);
if (currentManagedOverlay == null)
{ {
return mouseEvent; return mouseEvent;
} }
final Point mousePoint = mouseEvent.getPoint();
mousePosition.setLocation(mousePoint);
final Rectangle canvasRect = new Rectangle(client.getRealDimensions()); final Rectangle canvasRect = new Rectangle(client.getRealDimensions());
if (!canvasRect.contains(mousePoint)) if (!canvasRect.contains(p))
{ {
return mouseEvent; return mouseEvent;
} }
if (movedOverlay != null) if (inOverlayResizingMode)
{
final Rectangle r = currentManagedBounds;
final int dx = p.x - r.x;
final int dy = p.y - r.y;
switch (clientUI.getCurrentCursor().getType())
{
case Cursor.N_RESIZE_CURSOR:
int height = r.height - dy;
r.setRect(r.x, r.y + dy, r.width, height);
break;
case Cursor.NW_RESIZE_CURSOR:
int width = r.width - dx;
height = r.height - dy;
r.setRect(r.x + dx, r.y + dy, width, height);
break;
case Cursor.W_RESIZE_CURSOR:
width = r.width - dx;
r.setRect(r.x + dx, r.y, width, r.height);
break;
case Cursor.SW_RESIZE_CURSOR:
width = r.width - dx;
height = dy;
r.setRect(r.x + dx, r.y, width, height);
break;
case Cursor.S_RESIZE_CURSOR:
height = dy;
r.setRect(r.x, r.y, r.width, height);
break;
case Cursor.SE_RESIZE_CURSOR:
width = dx;
height = dy;
r.setRect(r.x, r.y, width, height);
break;
case Cursor.E_RESIZE_CURSOR:
width = dx;
r.setRect(r.x, r.y, width, r.height);
break;
case Cursor.NE_RESIZE_CURSOR:
width = dx;
height = r.height - dy;
r.setRect(r.x, r.y + dy, width, height);
break;
default:
// center
}
currentManagedOverlay.setPreferredSize(new Dimension(Math.max(MIN_OVERLAY_SIZE, currentManagedBounds.width), Math.max(MIN_OVERLAY_SIZE, currentManagedBounds.height)));
if (currentManagedOverlay.getPreferredLocation() != null)
{
currentManagedOverlay.setPreferredLocation(currentManagedBounds.getLocation());
}
}
else if (inOverlayDraggingMode)
{ {
final Dimension realDimension = client.getRealDimensions(); final Dimension realDimension = client.getRealDimensions();
mousePoint.translate(-overlayOffset.x, -overlayOffset.y); p.translate(-overlayOffset.x, -overlayOffset.y);
mousePoint.x = Ints.constrainToRange(mousePoint.x, 0, Math.max(0, realDimension.width - movedOverlay.getBounds().width)); p.x = Ints.constrainToRange(p.x, 0, Math.max(0, realDimension.width - currentManagedOverlay.getBounds().width));
mousePoint.y = Ints.constrainToRange(mousePoint.y, 0, Math.max(0, realDimension.height - movedOverlay.getBounds().height)); p.y = Ints.constrainToRange(p.y, 0, Math.max(0, realDimension.height - currentManagedOverlay.getBounds().height));
movedOverlay.setPreferredPosition(null); currentManagedOverlay.setPreferredPosition(null);
movedOverlay.setPreferredLocation(mousePoint); currentManagedOverlay.setPreferredLocation(p);
mouseEvent.consume(); }
else
{
return mouseEvent;
} }
if (startedMovingOverlay)
{
// Move currently moved overlay to correct layer
overlayManager.rebuildOverlayLayers();
startedMovingOverlay = false;
}
mouseEvent.consume();
return mouseEvent; return mouseEvent;
} }
@Override @Override
public MouseEvent mouseReleased(MouseEvent mouseEvent) public MouseEvent mouseReleased(MouseEvent mouseEvent)
{ {
if (movedOverlay != null) if (!inOverlayManagingMode || currentManagedOverlay == null || (!inOverlayDraggingMode && !inOverlayResizingMode))
{ {
mousePosition.setLocation(-1, -1); return mouseEvent;
// do not snapcorner detached overlays
if (movedOverlay.getPosition() != OverlayPosition.DETACHED)
{
final OverlayBounds snapCorners = this.snapCorners.translated(-SNAP_CORNER_SIZE.width, -SNAP_CORNER_SIZE.height);
for (Rectangle snapCorner : snapCorners.getBounds())
{
if (snapCorner.contains(mouseEvent.getPoint()))
{
OverlayPosition position = snapCorners.fromBounds(snapCorner);
if (position == movedOverlay.getPosition())
{
// overlay moves back to default position
position = null;
}
movedOverlay.setPreferredPosition(position);
movedOverlay.setPreferredLocation(null); // from dragging
break;
}
}
}
overlayManager.saveOverlay(movedOverlay);
movedOverlay = null;
mouseEvent.consume();
} }
mousePosition.setLocation(-1, -1);
// do not snapcorner detached overlays
if (currentManagedOverlay.getPosition() != OverlayPosition.DETACHED && inOverlayDraggingMode)
{
final OverlayBounds snapCorners = this.snapCorners.translated(-SNAP_CORNER_SIZE.width, -SNAP_CORNER_SIZE.height);
for (Rectangle snapCorner : snapCorners.getBounds())
{
if (snapCorner.contains(mouseEvent.getPoint()))
{
OverlayPosition position = snapCorners.fromBounds(snapCorner);
if (position == currentManagedOverlay.getPosition())
{
// overlay moves back to default position
position = null;
}
currentManagedOverlay.setPreferredPosition(position);
currentManagedOverlay.setPreferredLocation(null); // from dragging
break;
}
}
}
overlayManager.saveOverlay(currentManagedOverlay);
resetOverlayManagementMode();
mouseEvent.consume();
return mouseEvent; return mouseEvent;
} }
@@ -426,7 +582,7 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener
{ {
if (e.isAltDown()) if (e.isAltDown())
{ {
inOverlayDraggingMode = true; inOverlayManagingMode = true;
} }
if (e.isShiftDown() && runeLiteConfig.menuEntryShift()) if (e.isShiftDown() && runeLiteConfig.menuEntryShift())
@@ -440,7 +596,8 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener
{ {
if (!e.isAltDown()) if (!e.isAltDown())
{ {
inOverlayDraggingMode = false; inOverlayManagingMode = false;
resetOverlayManagementMode();
} }
if (!e.isShiftDown()) if (!e.isShiftDown())
@@ -526,6 +683,15 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener
return overlayPosition; return overlayPosition;
} }
private void resetOverlayManagementMode()
{
inOverlayResizingMode = false;
inOverlayDraggingMode = false;
currentManagedOverlay = null;
currentManagedBounds = null;
clientUI.setCursor(clientUI.getDefaultCursor());
}
private boolean shouldInvalidateBounds() private boolean shouldInvalidateBounds()
{ {
final Widget chatbox = client.getWidget(WidgetInfo.CHATBOX); final Widget chatbox = client.getWidget(WidgetInfo.CHATBOX);