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);
setLayer(OverlayLayer.ALWAYS_ON_TOP);
setPriority(OverlayPriority.HIGH);
setResizable(true);
}
@Override

View File

@@ -139,6 +139,7 @@ public class ClientUI
private NavigationButton sidebarNavigationButton;
private JButton sidebarNavigationJButton;
private Dimension lastClientSize;
private Cursor defaultCursor;
@Inject
private ClientUI(
@@ -663,6 +664,24 @@ public class ClientUI
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.
* 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 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;
}
defaultCursor = null;
container.setCursor(Cursor.getDefaultCursor());
}

View File

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

View File

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

View File

@@ -28,6 +28,7 @@ import com.google.common.base.MoreObjects;
import com.google.common.primitives.Ints;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics2D;
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.MouseAdapter;
import net.runelite.client.input.MouseManager;
import net.runelite.client.ui.ClientUI;
import net.runelite.client.ui.JagexColors;
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_TOP = BORDER + 15;
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 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 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_RESIZING_COLOR = new Color(255, 0, 255, 200);
private final Client client;
private final OverlayManager overlayManager;
private final RuneLiteConfig runeLiteConfig;
private final ClientUI clientUI;
// Overlay movement variables
private final Point overlayOffset = 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 inMenuEntryMode;
private boolean startedMovingOverlay;
private MenuEntry[] menuEntries;
// Overlay state validation
@@ -99,11 +109,13 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener
final OverlayManager overlayManager,
final RuneLiteConfig runeLiteConfig,
final MouseManager mouseManager,
final KeyManager keyManager)
final KeyManager keyManager,
final ClientUI clientUI)
{
this.client = client;
this.overlayManager = overlayManager;
this.runeLiteConfig = runeLiteConfig;
this.clientUI = clientUI;
keyManager.registerKeyListener(this);
mouseManager.registerMouseListener(this);
}
@@ -113,7 +125,8 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener
{
if (!event.isFocused())
{
inOverlayDraggingMode = false;
inOverlayManagingMode = false;
resetOverlayManagementMode();
inMenuEntryMode = false;
menuEntries = null;
}
@@ -182,7 +195,7 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener
OverlayUtil.setGraphicProperties(graphics);
// 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(
-SNAP_CORNER_SIZE.width,
@@ -274,12 +287,19 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener
if (!bounds.isEmpty())
{
if (inOverlayDraggingMode)
if (inOverlayManagingMode)
{
final Color previous = graphics.getColor();
graphics.setColor(movedOverlay == overlay ? MOVING_OVERLAY_ACTIVE_COLOR : MOVING_OVERLAY_COLOR);
if (inOverlayResizingMode && currentManagedOverlay == overlay)
{
graphics.setColor(MOVING_OVERLAY_RESIZING_COLOR);
}
else
{
graphics.setColor(inOverlayDraggingMode && currentManagedOverlay == overlay ? MOVING_OVERLAY_ACTIVE_COLOR : MOVING_OVERLAY_COLOR);
}
graphics.draw(bounds);
graphics.setColor(previous);
graphics.setPaint(paint);
}
if (!client.isMenuOpen() && !client.getSpellSelected() && bounds.contains(mouse))
@@ -299,7 +319,7 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener
@Override
public MouseEvent mousePressed(MouseEvent mouseEvent)
{
if (!inOverlayDraggingMode)
if (!inOverlayManagingMode)
{
return mouseEvent;
}
@@ -307,112 +327,248 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener
final Point mousePoint = mouseEvent.getPoint();
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);
if (overlayPosition == OverlayPosition.DYNAMIC || overlayPosition == OverlayPosition.TOOLTIP)
for (Overlay overlay : overlayManager.getOverlays())
{
continue;
}
if (overlay.getBounds().contains(mousePoint))
{
if (SwingUtilities.isRightMouseButton(mouseEvent))
final Rectangle bounds = overlay.getBounds();
if (bounds.contains(mousePoint))
{
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;
}
@Override
public MouseEvent mouseDragged(MouseEvent mouseEvent)
{
if (!inOverlayDraggingMode)
if (!inOverlayManagingMode)
{
return mouseEvent;
}
final Point p = mouseEvent.getPoint();
mousePosition.setLocation(p);
if (currentManagedOverlay == null)
{
return mouseEvent;
}
final Point mousePoint = mouseEvent.getPoint();
mousePosition.setLocation(mousePoint);
final Rectangle canvasRect = new Rectangle(client.getRealDimensions());
if (!canvasRect.contains(mousePoint))
if (!canvasRect.contains(p))
{
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();
mousePoint.translate(-overlayOffset.x, -overlayOffset.y);
mousePoint.x = Ints.constrainToRange(mousePoint.x, 0, Math.max(0, realDimension.width - movedOverlay.getBounds().width));
mousePoint.y = Ints.constrainToRange(mousePoint.y, 0, Math.max(0, realDimension.height - movedOverlay.getBounds().height));
movedOverlay.setPreferredPosition(null);
movedOverlay.setPreferredLocation(mousePoint);
mouseEvent.consume();
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));
currentManagedOverlay.setPreferredPosition(null);
currentManagedOverlay.setPreferredLocation(p);
}
else
{
return mouseEvent;
}
if (startedMovingOverlay)
{
// Move currently moved overlay to correct layer
overlayManager.rebuildOverlayLayers();
startedMovingOverlay = false;
}
mouseEvent.consume();
return mouseEvent;
}
@Override
public MouseEvent mouseReleased(MouseEvent mouseEvent)
{
if (movedOverlay != null)
if (!inOverlayManagingMode || currentManagedOverlay == null || (!inOverlayDraggingMode && !inOverlayResizingMode))
{
mousePosition.setLocation(-1, -1);
// 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();
return mouseEvent;
}
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;
}
@@ -426,7 +582,7 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener
{
if (e.isAltDown())
{
inOverlayDraggingMode = true;
inOverlayManagingMode = true;
}
if (e.isShiftDown() && runeLiteConfig.menuEntryShift())
@@ -440,7 +596,8 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener
{
if (!e.isAltDown())
{
inOverlayDraggingMode = false;
inOverlayManagingMode = false;
resetOverlayManagementMode();
}
if (!e.isShiftDown())
@@ -526,6 +683,15 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener
return overlayPosition;
}
private void resetOverlayManagementMode()
{
inOverlayResizingMode = false;
inOverlayDraggingMode = false;
currentManagedOverlay = null;
currentManagedBounds = null;
clientUI.setCursor(clientUI.getDefaultCursor());
}
private boolean shouldInvalidateBounds()
{
final Widget chatbox = client.getWidget(WidgetInfo.CHATBOX);