overlay: add movable and snappable overlay flags

This merges DETACHED and DYNAMIC into one position, DYNAMIC. And, allows
detached/dynamic overlays to be snappable.
This commit is contained in:
Adam
2022-05-15 10:58:49 -04:00
parent 5732e3f6bd
commit 11ce994b72
8 changed files with 147 additions and 113 deletions

View File

@@ -42,9 +42,10 @@ class ScreenMarkerCreationOverlay extends Overlay
private ScreenMarkerCreationOverlay(final ScreenMarkerPlugin plugin) private ScreenMarkerCreationOverlay(final ScreenMarkerPlugin plugin)
{ {
this.plugin = plugin; this.plugin = plugin;
setPosition(OverlayPosition.DETACHED); setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.ABOVE_WIDGETS); setLayer(OverlayLayer.ABOVE_WIDGETS);
setPriority(OverlayPriority.HIGH); setPriority(OverlayPriority.HIGH);
setMovable(true);
} }
@Override @Override

View File

@@ -45,9 +45,10 @@ public class ScreenMarkerOverlay extends Overlay
{ {
this.marker = marker; this.marker = marker;
this.screenMarkerRenderable = new ScreenMarkerRenderable(); this.screenMarkerRenderable = new ScreenMarkerRenderable();
setPosition(OverlayPosition.DETACHED); setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.ALWAYS_ON_TOP); setLayer(OverlayLayer.ALWAYS_ON_TOP);
setPriority(OverlayPriority.HIGH); setPriority(OverlayPriority.HIGH);
setMovable(true);
setResizable(true); setResizable(true);
setMinimumSize(16); setMinimumSize(16);
setResettable(false); setResettable(false);

View File

@@ -51,9 +51,10 @@ class ScreenMarkerWidgetHighlightOverlay extends Overlay
{ {
this.plugin = plugin; this.plugin = plugin;
this.client = client; this.client = client;
setPosition(OverlayPosition.DETACHED); setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.ABOVE_WIDGETS); setLayer(OverlayLayer.ABOVE_WIDGETS);
setPriority(OverlayPriority.HIGH); setPriority(OverlayPriority.HIGH);
setMovable(true);
} }
@Override @Override

View File

@@ -64,12 +64,25 @@ public abstract class Overlay implements LayoutableRenderableEntity
@Setter(AccessLevel.PROTECTED) @Setter(AccessLevel.PROTECTED)
private boolean dragTargetable; private boolean dragTargetable;
/**
* Whether this overlay can be moved with alt
*/
@Setter(AccessLevel.PROTECTED)
private boolean movable = true;
/**
* Whether this overlay can be moved to a snap corner
* and have its preferredPosition changed
*/
@Setter(AccessLevel.PROTECTED)
private boolean snappable = true;
protected Overlay() protected Overlay()
{ {
plugin = null; plugin = null;
} }
protected Overlay(Plugin plugin) protected Overlay(@Nullable Plugin plugin)
{ {
this.plugin = plugin; this.plugin = plugin;
} }
@@ -161,4 +174,25 @@ public abstract class Overlay implements LayoutableRenderableEntity
public void revalidate() public void revalidate()
{ {
} }
public void setPosition(OverlayPosition position)
{
this.position = position;
switch (position)
{
case TOOLTIP:
case DYNAMIC:
movable = false;
snappable = false;
break;
case DETACHED:
movable = true;
snappable = false;
break;
default:
movable = true;
snappable = true;
}
}
} }

View File

@@ -313,21 +313,29 @@ public class OverlayManager
private void loadOverlay(final Overlay overlay) private void loadOverlay(final Overlay overlay)
{ {
final Point location = loadOverlayLocation(overlay); final Point location = loadOverlayLocation(overlay);
overlay.setPreferredLocation(location);
final Dimension size = loadOverlaySize(overlay); final Dimension size = loadOverlaySize(overlay);
overlay.setPreferredSize(size);
final OverlayPosition position = loadOverlayPosition(overlay); final OverlayPosition position = loadOverlayPosition(overlay);
if (position != null)
if (overlay.isMovable())
{ {
if (overlay.getPosition() != OverlayPosition.DYNAMIC && overlay.getPosition() != OverlayPosition.TOOLTIP) overlay.setPreferredLocation(location);
{ }
overlay.setPreferredPosition(position); else
} {
else log.info("Resetting preferred location of non-movable overlay {} (class {})", overlay.getName(), overlay.getClass().getName());
{ saveOverlayLocation(overlay);
log.info("Resetting preferred position of dynamic overlay {}", overlay.getClass().getSimpleName()); }
saveOverlayPosition(overlay);
} overlay.setPreferredSize(size);
if (overlay.isSnappable())
{
overlay.setPreferredPosition(position);
}
else
{
log.info("Resetting preferred position of non-snappable overlay {} (class {})", overlay.getName(), overlay.getClass().getName());
saveOverlayPosition(overlay);
} }
} }

View File

@@ -28,7 +28,9 @@ public enum OverlayPosition
{ {
/** /**
* Not attached anywhere, but still movable * Not attached anywhere, but still movable
* Deprecated. Use DYNAMIC and setMovable(true)
*/ */
@Deprecated
DETACHED, DETACHED,
/** /**
* Overlay places itself where it wants * Overlay places itself where it wants

View File

@@ -256,7 +256,7 @@ public class OverlayRenderer extends MouseAdapter
OverlayUtil.setGraphicProperties(graphics); OverlayUtil.setGraphicProperties(graphics);
// Draw snap corners // Draw snap corners
if (inOverlayDraggingMode && layer == OverlayLayer.UNDER_WIDGETS && currentManagedOverlay != null && currentManagedOverlay.getPosition() != OverlayPosition.DETACHED) if (inOverlayDraggingMode && layer == OverlayLayer.UNDER_WIDGETS && currentManagedOverlay != null && currentManagedOverlay.isSnappable())
{ {
final OverlayBounds translatedSnapCorners = snapCorners.translated( final OverlayBounds translatedSnapCorners = snapCorners.translated(
-SNAP_CORNER_SIZE.width, -SNAP_CORNER_SIZE.width,
@@ -288,102 +288,87 @@ public class OverlayRenderer extends MouseAdapter
for (Overlay overlay : overlays) for (Overlay overlay : overlays)
{ {
final OverlayPosition overlayPosition = getCorrectedOverlayPosition(overlay); final OverlayPosition overlayPosition = getCorrectedOverlayPosition(overlay);
final Rectangle bounds = overlay.getBounds();
final Dimension dimension = bounds.getSize();
final Point preferredLocation = overlay.getPreferredLocation();
Point location;
Rectangle snapCorner = null;
if (overlayPosition == OverlayPosition.DYNAMIC || overlayPosition == OverlayPosition.TOOLTIP) // If the final position is not modified, layout it
if (overlayPosition != OverlayPosition.DYNAMIC && overlayPosition != OverlayPosition.TOOLTIP
&& overlayPosition != OverlayPosition.DETACHED && preferredLocation == null)
{ {
safeRender(client, overlay, layer, graphics, new Point()); snapCorner = snapCorners.forPosition(overlayPosition);
final Point translation = OverlayUtil.transformPosition(overlayPosition, dimension); // offset from corner
// Restore graphics2d properties // Target x/y to draw the overlay
graphics.setTransform(transform); int destX = snapCorner.x + translation.x;
graphics.setStroke(stroke); int destY = snapCorner.y + translation.y;
graphics.setComposite(composite); // Clamp the target position to ensure it is on screen or within parent bounds
graphics.setPaint(paint); location = clampOverlayLocation(destX, destY, dimension.width, dimension.height, overlay);
graphics.setRenderingHints(renderingHints);
graphics.setBackground(background);
} }
else else
{ {
final Rectangle bounds = overlay.getBounds(); location = preferredLocation != null ? preferredLocation : bounds.getLocation();
final Dimension dimension = bounds.getSize();
final Point preferredLocation = overlay.getPreferredLocation();
Point location;
Rectangle snapCorner = null;
// If the final position is not modified, layout it // Clamp the overlay position to ensure it is on screen or within parent bounds
if (overlayPosition != OverlayPosition.DETACHED && (preferredLocation == null || overlay.getPreferredPosition() != null)) location = clampOverlayLocation(location.x, location.y, dimension.width, dimension.height, overlay);
}
if (overlay.getPreferredSize() != null)
{
bounds.setSize(overlay.getPreferredSize());
}
safeRender(client, overlay, layer, graphics, location);
// Adjust snap corner based on where the overlay was drawn
if (snapCorner != null && bounds.width + bounds.height > 0)
{
OverlayUtil.shiftSnapCorner(overlayPosition, snapCorner, bounds, PADDING);
}
// Restore graphics2d properties prior to drawing bounds
graphics.setTransform(transform);
graphics.setStroke(stroke);
graphics.setComposite(composite);
graphics.setPaint(paint);
graphics.setRenderingHints(renderingHints);
graphics.setBackground(background);
if (!bounds.isEmpty())
{
if (inOverlayManagingMode)
{ {
snapCorner = snapCorners.forPosition(overlayPosition); Color boundsColor;
final Point translation = OverlayUtil.transformPosition(overlayPosition, dimension); // offset from corner if (inOverlayResizingMode && currentManagedOverlay == overlay)
// Target x/y to draw the overlay
int destX = snapCorner.x + translation.x;
int destY = snapCorner.y + translation.y;
// Clamp the target position to ensure it is on screen or within parent bounds
location = clampOverlayLocation(destX, destY, dimension.width, dimension.height, overlay);
}
else
{
location = preferredLocation != null ? preferredLocation : bounds.getLocation();
// Clamp the overlay position to ensure it is on screen or within parent bounds
location = clampOverlayLocation(location.x, location.y, dimension.width, dimension.height, overlay);
}
if (overlay.getPreferredSize() != null)
{
bounds.setSize(overlay.getPreferredSize());
}
safeRender(client, overlay, layer, graphics, location);
// Adjust snap corner based on where the overlay was drawn
if (snapCorner != null && bounds.width + bounds.height > 0)
{
OverlayUtil.shiftSnapCorner(overlayPosition, snapCorner, bounds, PADDING);
}
// Restore graphics2d properties prior to drawing bounds
graphics.setTransform(transform);
graphics.setStroke(stroke);
graphics.setComposite(composite);
graphics.setPaint(paint);
graphics.setRenderingHints(renderingHints);
graphics.setBackground(background);
if (!bounds.isEmpty())
{
if (inOverlayManagingMode)
{ {
Color boundsColor; boundsColor = MOVING_OVERLAY_RESIZING_COLOR;
if (inOverlayResizingMode && currentManagedOverlay == overlay) }
{ else if (inOverlayDraggingMode && currentManagedOverlay == overlay)
boundsColor = MOVING_OVERLAY_RESIZING_COLOR; {
} boundsColor = MOVING_OVERLAY_ACTIVE_COLOR;
else if (inOverlayDraggingMode && currentManagedOverlay == overlay) }
{ else if (inOverlayDraggingMode && overlay.isDragTargetable() && currentManagedOverlay.isDragTargetable()
boundsColor = MOVING_OVERLAY_ACTIVE_COLOR; && currentManagedOverlay.getBounds().intersects(bounds))
} {
else if (inOverlayDraggingMode && overlay.isDragTargetable() && currentManagedOverlay.isDragTargetable() boundsColor = MOVING_OVERLAY_TARGET_COLOR;
&& currentManagedOverlay.getBounds().intersects(bounds)) assert currentManagedOverlay != overlay;
{ dragTargetOverlay = overlay;
boundsColor = MOVING_OVERLAY_TARGET_COLOR; }
assert currentManagedOverlay != overlay; else
dragTargetOverlay = overlay; {
} boundsColor = MOVING_OVERLAY_COLOR;
else
{
boundsColor = MOVING_OVERLAY_COLOR;
}
graphics.setColor(boundsColor);
graphics.draw(bounds);
graphics.setPaint(paint);
} }
if (!client.isMenuOpen() && !client.getSpellSelected() && bounds.contains(mouse)) graphics.setColor(boundsColor);
{ graphics.draw(bounds);
hoveredOverlay = overlay; graphics.setPaint(paint);
overlay.onMouseOver(); }
}
if (!client.isMenuOpen() && !client.getSpellSelected() && bounds.contains(mouse))
{
hoveredOverlay = overlay;
overlay.onMouseOver();
} }
} }
} }
@@ -511,7 +496,7 @@ public class OverlayRenderer extends MouseAdapter
// ABOVE_SCENE overlays aren't managed // ABOVE_SCENE overlays aren't managed
.filter(c -> layerOrder.contains(c.getLayer())) .filter(c -> layerOrder.contains(c.getLayer()))
// never allow moving dynamic or tooltip overlays // never allow moving dynamic or tooltip overlays
.filter(c -> c.getPosition() != OverlayPosition.DYNAMIC && c.getPosition() != OverlayPosition.TOOLTIP) .filter(Overlay::isMovable)
.sorted( .sorted(
Comparator.<Overlay>comparingInt(c -> layerOrder.indexOf(c.getLayer())) Comparator.<Overlay>comparingInt(c -> layerOrder.indexOf(c.getLayer()))
.thenComparing(OverlayManager.OVERLAY_COMPARATOR) .thenComparing(OverlayManager.OVERLAY_COMPARATOR)
@@ -686,8 +671,8 @@ public class OverlayRenderer extends MouseAdapter
} }
} }
// Check if the overlay is over a snapcorner and move it if so, unless it is a detached overlay // Check if the overlay is over a snapcorner and snap it if so
if (currentManagedOverlay.getPosition() != OverlayPosition.DETACHED && inOverlayDraggingMode) if (currentManagedOverlay.isSnappable() && inOverlayDraggingMode)
{ {
final OverlayBounds snapCorners = this.emptySnapCorners.translated(-SNAP_CORNER_SIZE.width, -SNAP_CORNER_SIZE.height); final OverlayBounds snapCorners = this.emptySnapCorners.translated(-SNAP_CORNER_SIZE.width, -SNAP_CORNER_SIZE.height);

View File

@@ -43,14 +43,14 @@ public class WidgetOverlay extends Overlay
{ {
return Arrays.asList( return Arrays.asList(
// classic resizable // classic resizable
new WidgetOverlay(client, WidgetInfo.RESIZABLE_VIEWPORT_CHATBOX_PARENT, OverlayPosition.DETACHED, OverlayPriority.HIGH), new WidgetOverlay(client, WidgetInfo.RESIZABLE_VIEWPORT_CHATBOX_PARENT, OverlayPosition.DYNAMIC, OverlayPriority.HIGH),
new WidgetOverlay(client, WidgetInfo.RESIZABLE_VIEWPORT_BOTTOM_LINE_CHATBOX_PARENT, OverlayPosition.DETACHED, OverlayPriority.HIGH), new WidgetOverlay(client, WidgetInfo.RESIZABLE_VIEWPORT_BOTTOM_LINE_CHATBOX_PARENT, OverlayPosition.DYNAMIC, OverlayPriority.HIGH),
new WidgetOverlay(client, WidgetInfo.RESIZABLE_VIEWPORT_INVENTORY_PARENT, OverlayPosition.DETACHED), new WidgetOverlay(client, WidgetInfo.RESIZABLE_VIEWPORT_INVENTORY_PARENT, OverlayPosition.DYNAMIC),
new WidgetOverlay(client, WidgetInfo.RESIZABLE_MINIMAP_WIDGET, OverlayPosition.CANVAS_TOP_RIGHT, OverlayPriority.MED), new WidgetOverlay(client, WidgetInfo.RESIZABLE_MINIMAP_WIDGET, OverlayPosition.CANVAS_TOP_RIGHT, OverlayPriority.MED),
// modern resizable // modern resizable
new WidgetOverlay(client, WidgetInfo.RESIZABLE_VIEWPORT_BOTTOM_LINE_TABS1, OverlayPosition.DETACHED), new WidgetOverlay(client, WidgetInfo.RESIZABLE_VIEWPORT_BOTTOM_LINE_TABS1, OverlayPosition.DYNAMIC),
new WidgetOverlay(client, WidgetInfo.RESIZABLE_VIEWPORT_BOTTOM_LINE_TABS2, OverlayPosition.DETACHED), new WidgetOverlay(client, WidgetInfo.RESIZABLE_VIEWPORT_BOTTOM_LINE_TABS2, OverlayPosition.DYNAMIC),
new WidgetOverlay(client, WidgetInfo.RESIZABLE_VIEWPORT_BOTTOM_LINE_INVENTORY_PARENT, OverlayPosition.DETACHED), new WidgetOverlay(client, WidgetInfo.RESIZABLE_VIEWPORT_BOTTOM_LINE_INVENTORY_PARENT, OverlayPosition.DYNAMIC),
new WidgetOverlay(client, WidgetInfo.RESIZABLE_MINIMAP_STONES_WIDGET, OverlayPosition.CANVAS_TOP_RIGHT, OverlayPriority.MED), new WidgetOverlay(client, WidgetInfo.RESIZABLE_MINIMAP_STONES_WIDGET, OverlayPosition.CANVAS_TOP_RIGHT, OverlayPriority.MED),
// The client forces the oxygen bar below the xp tracker, so set its priority lower // The client forces the oxygen bar below the xp tracker, so set its priority lower
new WidgetOverlay(client, WidgetInfo.FOSSIL_ISLAND_OXYGENBAR, OverlayPosition.TOP_CENTER, OverlayPriority.HIGH), new WidgetOverlay(client, WidgetInfo.FOSSIL_ISLAND_OXYGENBAR, OverlayPosition.TOP_CENTER, OverlayPriority.HIGH),
@@ -84,7 +84,7 @@ public class WidgetOverlay extends Overlay
new WidgetOverlay(client, WidgetInfo.TEMPOROSS_STATUS_INDICATOR, OverlayPosition.TOP_LEFT), new WidgetOverlay(client, WidgetInfo.TEMPOROSS_STATUS_INDICATOR, OverlayPosition.TOP_LEFT),
new WidgetOverlay(client, WidgetInfo.BA_HEAL_TEAMMATES, OverlayPosition.BOTTOM_LEFT), new WidgetOverlay(client, WidgetInfo.BA_HEAL_TEAMMATES, OverlayPosition.BOTTOM_LEFT),
new WidgetOverlay(client, WidgetInfo.BA_TEAM, OverlayPosition.TOP_RIGHT), new WidgetOverlay(client, WidgetInfo.BA_TEAM, OverlayPosition.TOP_RIGHT),
new WidgetOverlay(client, WidgetInfo.PVP_WILDERNESS_SKULL_CONTAINER, OverlayPosition.DETACHED) new WidgetOverlay(client, WidgetInfo.PVP_WILDERNESS_SKULL_CONTAINER, OverlayPosition.DYNAMIC)
); );
} }
@@ -105,6 +105,8 @@ public class WidgetOverlay extends Overlay
setPriority(overlayPriority); setPriority(overlayPriority);
setLayer(OverlayLayer.UNDER_WIDGETS); setLayer(OverlayLayer.UNDER_WIDGETS);
setPosition(overlayPosition); setPosition(overlayPosition);
setMovable(true);
setSnappable(true);
// It's almost possible to drawAfterInterface(widgetInfo.getGroupId()) here, but that fires // It's almost possible to drawAfterInterface(widgetInfo.getGroupId()) here, but that fires
// *after* the native components are drawn, which is too late. // *after* the native components are drawn, which is too late.
} }