diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarker.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarker.java new file mode 100644 index 0000000000..624567f09e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarker.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018, Kamiel, + * Copyright (c) 2018, Adam + * 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.plugins.screenmarkers; + +import java.awt.Color; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ScreenMarker +{ + private String name; + private int borderThickness; + private Color color; + private Color fill; + private boolean visible; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerCreationOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerCreationOverlay.java new file mode 100644 index 0000000000..f1e0c60bde --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerCreationOverlay.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2018, Kamiel, + * 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.plugins.screenmarkers; + +import java.awt.BasicStroke; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Stroke; +import javax.inject.Inject; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayPriority; + +public class ScreenMarkerCreationOverlay extends Overlay +{ + private ScreenMarkerPlugin plugin; + + @Inject + private ScreenMarkerCreationOverlay(final ScreenMarkerPlugin plugin) + { + this.plugin = plugin; + setPosition(OverlayPosition.DETACHED); + setLayer(OverlayLayer.ALWAYS_ON_TOP); + setPriority(OverlayPriority.HIGH); + } + + @Override + public Dimension render(Graphics2D graphics) + { + ScreenMarker marker = plugin.getCurrentMarker(); + + if (marker == null) + { + return null; + } + + int thickness = marker.getBorderThickness(); + int offset = thickness / 2; + int width = getBounds().width - thickness; + int height = getBounds().height - thickness; + + graphics.setStroke(createStripedStroke(thickness)); + graphics.setColor(marker.getColor()); + graphics.drawRect(offset, offset, width, height); + + return getBounds().getSize(); + } + + private Stroke createStripedStroke(int thickness) + { + return new BasicStroke(thickness, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[]{9}, 0); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerMouseListener.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerMouseListener.java new file mode 100644 index 0000000000..611c4390a9 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerMouseListener.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2018, Kamiel, + * 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.plugins.screenmarkers; + +import java.awt.Point; +import java.awt.event.MouseEvent; +import static java.lang.Math.max; +import static java.lang.Math.min; +import javax.swing.SwingUtilities; +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.input.MouseListener; + +@Slf4j +public class ScreenMarkerMouseListener extends MouseListener +{ + private final ScreenMarkerPlugin plugin; + private Point lastMousePoint = null; + + ScreenMarkerMouseListener(ScreenMarkerPlugin plugin) + { + this.plugin = plugin; + } + + @Override + public MouseEvent mouseClicked(MouseEvent event) + { + if (SwingUtilities.isMiddleMouseButton(event)) + { + return event; + } + + event.consume(); + return event; + } + + @Override + public MouseEvent mousePressed(MouseEvent event) + { + if (SwingUtilities.isMiddleMouseButton(event)) + { + return event; + } + + if (SwingUtilities.isLeftMouseButton(event)) + { + lastMousePoint = event.getPoint(); + plugin.startCreation(event.getPoint()); + } + else if (plugin.isCreatingScreenMarker()) + { + plugin.finishCreation(true); + lastMousePoint = null; + } + + event.consume(); + return event; + } + + @Override + public MouseEvent mouseReleased(MouseEvent event) + { + if (SwingUtilities.isMiddleMouseButton(event)) + { + return event; + } + + if (SwingUtilities.isLeftMouseButton(event) && plugin.isCreatingScreenMarker()) + { + plugin.finishCreation(false); + } + + event.consume(); + return event; + } + + @Override + public MouseEvent mouseDragged(MouseEvent event) + { + if (!plugin.isCreatingScreenMarker()) + { + return event; + } + + if (SwingUtilities.isLeftMouseButton(event)) + { + Point currentPoint = event.getPoint(); + int dx = currentPoint.x - lastMousePoint.x; + int dy = currentPoint.y - lastMousePoint.y; + + //if shift is down, constrain proportions + if (event.isShiftDown() && dx != dy) + { + int x = dx; + + if (dx > 0 || dy > 0) + { + dx = max(dx, dy); + dy = max(dy, x); + } + else + { + dx = min(dx, dy); + dy = min(dy, x); + } + } + + plugin.resizeMarker(dx, dy); + lastMousePoint = currentPoint; + } + + return event; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerOverlay.java new file mode 100644 index 0000000000..10c65d8da0 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerOverlay.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018, Kamiel, + * Copyright (c) 2018, Adam + * 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.plugins.screenmarkers; + +import java.awt.BasicStroke; +import java.awt.Dimension; +import java.awt.Graphics2D; +import lombok.Getter; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayPriority; + +public class ScreenMarkerOverlay extends Overlay +{ + @Getter + private final ScreenMarker marker; + private final ScreenMarkerRenderable screenMarkerRenderable; + + ScreenMarkerOverlay(ScreenMarker marker) + { + this.marker = marker; + setPosition(OverlayPosition.DETACHED); + setLayer(OverlayLayer.ALWAYS_ON_TOP); + setPriority(OverlayPriority.HIGH); + + screenMarkerRenderable = new ScreenMarkerRenderable(); + screenMarkerRenderable.setBorderThickness(marker.getBorderThickness()); + screenMarkerRenderable.setColor(marker.getColor()); + screenMarkerRenderable.setFill(marker.getFill()); + screenMarkerRenderable.setStroke(new BasicStroke(marker.getBorderThickness())); + } + + @Override + public String getName() + { + return marker.getName(); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (!marker.isVisible()) + { + return null; + } + + screenMarkerRenderable.setPreferredSize(getPreferredSize()); + return screenMarkerRenderable.render(graphics); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerPlugin.java new file mode 100644 index 0000000000..2f5632adee --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerPlugin.java @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2018, Kamiel, + * Copyright (c) 2018, Adam + * 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.plugins.screenmarkers; + +import com.google.common.base.Strings; +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.imageio.ImageIO; +import javax.inject.Inject; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.events.ConfigChanged; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.input.MouseManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.screenmarkers.ui.ScreenMarkerPluginPanel; +import net.runelite.client.ui.NavigationButton; +import net.runelite.client.ui.PluginToolbar; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayRenderer; + +@PluginDescriptor( + name = "Screen Markers" +) +@Slf4j +public class ScreenMarkerPlugin extends Plugin +{ + private static final String PLUGIN_NAME = "Screen Markers"; + private static final String CONFIG_GROUP = "screenmarkers"; + private static final String CONFIG_KEY = "markers"; + private static final String ICON_FILE = "panel_icon.png"; + private static final String DEFAULT_MARKER_NAME = "Marker"; + private static final Dimension DEFAULT_SIZE = new Dimension(2, 2); + + @Inject + private EventBus eventBus; + + @Inject + private ConfigManager configManager; + + @Inject + private MouseManager mouseManager; + + @Inject + private PluginToolbar pluginToolbar; + + @Inject + private ScreenMarkerCreationOverlay overlay; + + @Inject + private OverlayRenderer overlayRenderer; + + private ScreenMarkerMouseListener mouseListener; + private ScreenMarkerPluginPanel pluginPanel; + private NavigationButton navigationButton; + + @Getter + private final List screenMarkers = new ArrayList<>(); + + @Getter(AccessLevel.PACKAGE) + private ScreenMarker currentMarker; + + @Getter + private boolean creatingScreenMarker = false; + + @Override + public Collection getOverlays() + { + final List overlays = new ArrayList<>(); + overlays.add(overlay); + overlays.addAll(screenMarkers); + return overlays; + } + + @Override + protected void startUp() throws Exception + { + loadConfig(configManager.getConfiguration(CONFIG_GROUP, CONFIG_KEY)).forEach(screenMarkers::add); + overlayRenderer.rebuildOverlays(); + + pluginPanel = injector.getInstance(ScreenMarkerPluginPanel.class); + pluginPanel.init(); + + BufferedImage icon; + synchronized (ImageIO.class) + { + icon = ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream(ICON_FILE)); + } + + navigationButton = NavigationButton.builder() + .tooltip(PLUGIN_NAME) + .icon(icon) + .panel(pluginPanel) + .build(); + + pluginToolbar.addNavigation(navigationButton); + + mouseListener = new ScreenMarkerMouseListener(this); + } + + @Override + protected void shutDown() throws Exception + { + pluginToolbar.removeNavigation(navigationButton); + setMouseListenerEnabled(false); + creatingScreenMarker = false; + screenMarkers.clear(); + + pluginPanel = null; + currentMarker = null; + mouseListener = null; + navigationButton = null; + } + + @Subscribe + public void onConfigChanged(ConfigChanged event) + { + if (screenMarkers.isEmpty() && event.getGroup().equals(CONFIG_GROUP) && event.getKey().equals(CONFIG_KEY)) + { + loadConfig(event.getNewValue()).forEach(screenMarkers::add); + overlayRenderer.rebuildOverlays(); + } + } + + public void setMouseListenerEnabled(boolean enabled) + { + if (enabled) + { + mouseManager.registerMouseListener(mouseListener); + } + else + { + mouseManager.unregisterMouseListener(mouseListener); + } + } + + public void startCreation(Point location) + { + currentMarker = new ScreenMarker( + DEFAULT_MARKER_NAME + " " + (screenMarkers.size() + 1), + pluginPanel.getSelectedBorderThickness(), + pluginPanel.getSelectedColor(), + pluginPanel.getSelectedFillColor(), + true + ); + + // Set overlay creator bounds to current position and default size + overlay.setPreferredLocation(location); + overlay.setPreferredSize(DEFAULT_SIZE); + creatingScreenMarker = true; + } + + public void finishCreation(boolean aborted) + { + if (!aborted) + { + setMouseListenerEnabled(false); + pluginPanel.setCreationEnabled(false); + + final ScreenMarkerOverlay screenMarkerOverlay = new ScreenMarkerOverlay(currentMarker); + screenMarkerOverlay.setPreferredLocation(overlay.getBounds().getLocation()); + screenMarkerOverlay.setPreferredSize(overlay.getBounds().getSize()); + + screenMarkers.add(screenMarkerOverlay); + pluginPanel.rebuild(); + updateConfig(); + overlayRenderer.saveOverlay(screenMarkerOverlay); + overlayRenderer.rebuildOverlays(); + } + + creatingScreenMarker = false; + currentMarker = null; + } + + public void deleteMarker(final ScreenMarkerOverlay marker) + { + overlayRenderer.resetOverlay(marker); + screenMarkers.remove(marker); + pluginPanel.rebuild(); + updateConfig(); + overlayRenderer.rebuildOverlays(); + } + + public void resizeMarker(int dx, int dy) + { + // TODO: Allow resizing below base point + Dimension currentSize = overlay.getPreferredSize(); + overlay.setPreferredSize(new Dimension(currentSize.width + dx, currentSize.height + dy)); + } + + public void updateConfig() + { + if (screenMarkers.isEmpty()) + { + configManager.unsetConfiguration(CONFIG_GROUP, CONFIG_KEY); + return; + } + + final Gson gson = new Gson(); + final String json = gson + .toJson(screenMarkers.stream().map(ScreenMarkerOverlay::getMarker).collect(Collectors.toList())); + configManager.setConfiguration(CONFIG_GROUP, CONFIG_KEY, json); + } + + private Stream loadConfig(String json) + { + if (Strings.isNullOrEmpty(json)) + { + return Stream.empty(); + } + + final Gson gson = new Gson(); + final List screenMarkerData = gson.fromJson(json, new TypeToken>() + { + }.getType()); + + return screenMarkerData.stream().map(ScreenMarkerOverlay::new); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerRenderable.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerRenderable.java new file mode 100644 index 0000000000..32b74630d1 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerRenderable.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018, Kamiel, + * 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.plugins.screenmarkers; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Stroke; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +import net.runelite.client.ui.overlay.components.LayoutableRenderableEntity; + +public class ScreenMarkerRenderable implements LayoutableRenderableEntity +{ + @Getter(AccessLevel.PACKAGE) + private Dimension preferredSize; + @Getter(AccessLevel.PACKAGE) + @Setter(AccessLevel.PACKAGE) + private int borderThickness; + @Getter(AccessLevel.PACKAGE) + @Setter(AccessLevel.PACKAGE) + private Color color; + @Getter(AccessLevel.PACKAGE) + @Setter(AccessLevel.PACKAGE) + private Color fill; + @Getter(AccessLevel.PACKAGE) + @Setter(AccessLevel.PACKAGE) + private Stroke stroke; + + @Override + public Dimension render(Graphics2D graphics) + { + int thickness = borderThickness; + int width = preferredSize.width; + int height = preferredSize.height; + + //draw the fill + graphics.setColor(fill); + graphics.fillRect(thickness, thickness, width - thickness * 2, height - thickness * 2); + + //because the stroke is centered on the rectangle we draw, we need to translate where we draw the rectangle + //this is to ensure that the rectangle we draw is our preferred size + int offset = thickness / 2; + graphics.setColor(color); + graphics.setStroke(stroke); + graphics.drawRect(offset, offset, width - thickness, height - thickness); + return preferredSize; + } + + @Override + public void setPreferredSize(Dimension preferredSize) + { + this.preferredSize = preferredSize; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPanel.java new file mode 100644 index 0000000000..13b62aceae --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPanel.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018, Kamiel, + * 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.plugins.screenmarkers.ui; + +import java.awt.FlowLayout; +import java.awt.GridLayout; +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JToggleButton; +import net.runelite.client.plugins.screenmarkers.ScreenMarkerOverlay; +import net.runelite.client.plugins.screenmarkers.ScreenMarkerPlugin; +import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; + +class ScreenMarkerPanel extends JPanel +{ + private static final String DELETE_TEXT = "Delete"; + private static final String HIDE_TEXT = "Hide"; + private static final String SHOW_TEXT = "Show"; + + private final ScreenMarkerPlugin plugin; + private final ScreenMarkerOverlay marker; + private JToggleButton visibleToggle; + + ScreenMarkerPanel(ScreenMarkerPlugin plugin, ScreenMarkerOverlay marker) + { + this.plugin = plugin; + this.marker = marker; + construct(); + } + + private void construct() + { + setLayout(new GridLayout(0, 1, 0, 3)); + JPanel container = new JPanel(new FlowLayout()); + + JButton deleteButton = new JButton(DELETE_TEXT); + deleteButton.addActionListener(l -> plugin.deleteMarker(marker)); + + boolean selected = !marker.getMarker().isVisible(); + visibleToggle = new JToggleButton(selected ? SHOW_TEXT : HIDE_TEXT, selected); + visibleToggle.setFocusable(false); + visibleToggle.addActionListener(l -> + { + boolean visible = !visibleToggle.isSelected(); + marker.getMarker().setVisible(visible); + visibleToggle.setText(visible ? HIDE_TEXT : SHOW_TEXT); + plugin.updateConfig(); + }); + + container.add(new JShadowedLabel(marker.getName())); + container.add(visibleToggle); + container.add(deleteButton); + + add(container); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPluginPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPluginPanel.java new file mode 100644 index 0000000000..eb844e3f4b --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPluginPanel.java @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2018, Kamiel, + * 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.plugins.screenmarkers.ui; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.GridLayout; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import javax.swing.JButton; +import javax.swing.JColorChooser; +import javax.swing.JFormattedTextField; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSpinner; +import javax.swing.SpinnerModel; +import javax.swing.SpinnerNumberModel; +import lombok.Getter; +import net.runelite.client.plugins.screenmarkers.ScreenMarkerOverlay; +import net.runelite.client.plugins.screenmarkers.ScreenMarkerPlugin; +import net.runelite.client.ui.PluginPanel; +import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; + +@Singleton +public class ScreenMarkerPluginPanel extends PluginPanel +{ + private static final String TITLE = "Screen Markers"; + private static final Color DEFAULT_COLOR = Color.BLUE; + private static final int DEFAULT_BORDER_THICKNESS = 5; + private static final int DEFAULT_FILL_OPACITY = 75; + private static final String COLOR_PICKER_TITLE = "Choose a color.."; + private static final String COLOR_TEXT = "Color"; + private static final String FILL_TEXT = "Fill"; + private static final String NEW_TEXT = "New"; + private static final String CANCEL_TEXT = "Cancel"; + private static final Color CANCEL_BUTTON_COLOR = Color.RED.darker(); + + @Inject + private ScreenMarkerPlugin plugin; + + private JButton markerButton; + + @Getter + private Color selectedColor = DEFAULT_COLOR; + + @Getter + private Color selectedFillColor = new Color(DEFAULT_COLOR.getRed(), DEFAULT_COLOR.getGreen(), DEFAULT_COLOR.getBlue(), DEFAULT_FILL_OPACITY); + + @Getter + private int selectedBorderThickness = DEFAULT_BORDER_THICKNESS; + + private boolean creationEnabled = false; + + public void init() + { + setLayout(new BorderLayout()); + JPanel northPanel = new JPanel(new GridLayout(0, 2, 0, 3)); + + markerButton = new JButton(NEW_TEXT); + markerButton.setFocusable(false); + markerButton.addActionListener(l -> startOrCancelCreation()); + + northPanel.add(new JShadowedLabel(TITLE)); + northPanel.add(new JLabel()); + northPanel.add(new JShadowedLabel("Border size:")); + northPanel.add(createBorderThicknessSpinner()); + northPanel.add(new JShadowedLabel("Border color:")); + northPanel.add(createBorderColorButton()); + northPanel.add(new JShadowedLabel("Fill color:")); + northPanel.add(createFillColorButton()); + + northPanel.add(markerButton); + + JPanel centerPanel = new JPanel(); + JPanel markerView = new JPanel(new GridLayout(0, 1, 0, 3)); + + for (final ScreenMarkerOverlay marker : plugin.getScreenMarkers()) + { + markerView.add(new ScreenMarkerPanel(plugin, marker)); + } + + centerPanel.add(markerView, BorderLayout.NORTH); + + add(northPanel, BorderLayout.NORTH); + add(centerPanel, BorderLayout.CENTER); + } + + public void rebuild() + { + removeAll(); + repaint(); + revalidate(); + init(); + } + + private void startOrCancelCreation() + { + creationEnabled = !creationEnabled; + plugin.setMouseListenerEnabled(creationEnabled); + setMarkerButtonState(creationEnabled); + } + + public void setCreationEnabled(boolean creationEnabled) + { + this.creationEnabled = creationEnabled; + setMarkerButtonState(creationEnabled); + } + + private void setMarkerButtonState(boolean selected) + { + markerButton.setSelected(selected); + markerButton.setText(selected ? CANCEL_TEXT : NEW_TEXT); + markerButton.setBackground(selected ? CANCEL_BUTTON_COLOR : null); + } + + private JSpinner createBorderThicknessSpinner() + { + SpinnerModel model = new SpinnerNumberModel(selectedBorderThickness, 0, Integer.MAX_VALUE, 1); + JSpinner spinner = new JSpinner(model); + Component editor = spinner.getEditor(); + JFormattedTextField spinnerTextField = ((JSpinner.DefaultEditor) editor).getTextField(); + spinnerTextField.setColumns(5); + spinner.addChangeListener(ce -> selectedBorderThickness = (Integer) spinner.getValue()); + return spinner; + } + + private JButton createBorderColorButton() + { + JButton colorPicker = new JButton(COLOR_TEXT); + colorPicker.setFocusable(false); + colorPicker.setBackground(selectedColor); + colorPicker.addActionListener(e -> + { + final JFrame parent = new JFrame(COLOR_PICKER_TITLE); + JColorChooser jColorChooser = new JColorChooser(selectedColor); + jColorChooser.getSelectionModel().addChangeListener(e1 -> colorPicker.setBackground(jColorChooser.getColor())); + + parent.addWindowListener(new WindowAdapter() + { + @Override + public void windowClosing(WindowEvent e) + { + selectedColor = jColorChooser.getColor(); + } + }); + + parent.add(jColorChooser); + parent.pack(); + parent.setLocationRelativeTo(null); + parent.setVisible(true); + }); + + return colorPicker; + } + + private JButton createFillColorButton() + { + JButton colorPicker = new JButton(FILL_TEXT); + colorPicker.setFocusable(false); + colorPicker.setBackground(selectedFillColor); + colorPicker.addActionListener(e -> + { + final JFrame parent = new JFrame(COLOR_PICKER_TITLE); + JColorChooser jColorChooser = new JColorChooser(selectedFillColor); + jColorChooser.getSelectionModel().addChangeListener(e1 -> colorPicker.setBackground(jColorChooser.getColor())); + + parent.addWindowListener(new WindowAdapter() + { + @Override + public void windowClosing(WindowEvent e) + { + Color color = jColorChooser.getColor(); + selectedFillColor = new Color(color.getRed(), color.getGreen(), color.getBlue(), DEFAULT_FILL_OPACITY); + } + }); + + parent.add(jColorChooser); + parent.pack(); + parent.setLocationRelativeTo(null); + parent.setVisible(true); + }); + + return colorPicker; + } +} 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 4b6c7b9eca..fd1504d4da 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 @@ -24,17 +24,29 @@ */ package net.runelite.client.ui.overlay; +import java.awt.Dimension; import java.awt.Point; import java.awt.Rectangle; import lombok.Data; +import net.runelite.client.ui.overlay.components.LayoutableRenderableEntity; @Data -public abstract class Overlay implements RenderableEntity +public abstract class Overlay implements LayoutableRenderableEntity { private Point preferredLocation; + private Dimension preferredSize; private OverlayPosition preferredPosition; private Rectangle bounds = new Rectangle(); private OverlayPosition position = OverlayPosition.TOP_LEFT; private OverlayPriority priority = OverlayPriority.NONE; private OverlayLayer layer = OverlayLayer.UNDER_WIDGETS; + + /** + * Overlay name, used for saving the overlay, needs to be unique + * @return overlay name + */ + public String getName() + { + return this.getClass().getSimpleName(); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayPosition.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayPosition.java index 3f1fd27839..8acc04d148 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayPosition.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayPosition.java @@ -26,6 +26,10 @@ package net.runelite.client.ui.overlay; public enum OverlayPosition { + /** + * Not attached anywhere, but still movable + */ + DETACHED, /** * Overlay places itself where it wants */ 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 b27e578f4f..5f01a21b37 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 @@ -85,6 +85,7 @@ public class OverlayRenderer extends MouseListener implements KeyListener private static final Color MOVING_OVERLAY_ACTIVE_COLOR = new Color(255, 255, 0, 200); private static final String OVERLAY_CONFIG_PREFERRED_LOCATION = "_preferredLocation"; private static final String OVERLAY_CONFIG_PREFERRED_POSITION = "_preferredPosition"; + private static final String OVERLAY_CONFIG_PREFERRED_SIZE = "_preferredSize"; private final PluginManager pluginManager; private final Provider clientProvider; @@ -165,7 +166,35 @@ public class OverlayRenderer extends MouseListener implements KeyListener } } - private void rebuildOverlays() + /** + * Force save overlay data + * @param overlay overlay to save + */ + public void saveOverlay(final Overlay overlay) + { + saveOverlayPosition(overlay); + saveOverlaySize(overlay); + saveOverlayLocation(overlay); + } + + /** + * Resets stored overlay position data + * @param overlay overlay to reset + */ + public void resetOverlay(final Overlay overlay) + { + final String locationKey = overlay.getName() + OVERLAY_CONFIG_PREFERRED_LOCATION; + final String positionKey = overlay.getName() + OVERLAY_CONFIG_PREFERRED_POSITION; + final String sizeKey = overlay.getName() + OVERLAY_CONFIG_PREFERRED_SIZE; + configManager.unsetConfiguration(runeliteGroupName, locationKey); + configManager.unsetConfiguration(runeliteGroupName, positionKey); + configManager.unsetConfiguration(runeliteGroupName, sizeKey); + } + + /** + * Rebuild overlay cache for rendering + */ + public void rebuildOverlays() { final List overlays = Stream .concat( @@ -199,11 +228,18 @@ public class OverlayRenderer extends MouseListener implements KeyListener overlay.setPreferredLocation(null); saveOverlayLocation(overlay); } - else + else if (location != null) { overlay.setPreferredLocation(location); } + final Dimension size = loadOverlaySize(overlay); + + if (size != null) + { + overlay.setPreferredSize(size); + } + final OverlayPosition position = loadOverlayPosition(overlay); overlay.setPreferredPosition(position); } @@ -289,7 +325,7 @@ public class OverlayRenderer extends MouseListener implements KeyListener OverlayUtil.setGraphicProperties(graphics); // Draw snap corners - if (layer == OverlayLayer.UNDER_WIDGETS && movedOverlay != null) + if (layer == OverlayLayer.UNDER_WIDGETS && movedOverlay != null && movedOverlay.getPosition() != OverlayPosition.DETACHED) { final OverlayBounds translatedSnapCorners = snapCorners.translated( -SNAP_CORNER_SIZE.width, @@ -333,7 +369,7 @@ public class OverlayRenderer extends MouseListener implements KeyListener final Dimension dimension = overlay.getBounds().getSize(); // If the final position is not modified, layout it - if (overlay.getPreferredLocation() == null || overlay.getPreferredPosition() != null) + if (overlayPosition != OverlayPosition.DETACHED && (overlay.getPreferredLocation() == null || overlay.getPreferredPosition() != null)) { final Rectangle snapCorner = snapCorners.forPosition(overlayPosition); final Point translation = OverlayUtil.transformPosition(overlayPosition, dimension); @@ -343,7 +379,15 @@ public class OverlayRenderer extends MouseListener implements KeyListener } else { - location.setLocation(overlay.getPreferredLocation()); + if (overlay.getPreferredLocation() != null) + { + location.setLocation(overlay.getPreferredLocation()); + } + } + + if (overlay.getPreferredSize() != null) + { + overlay.getBounds().setSize(overlay.getPreferredSize()); } safeRender(client, overlay, layer, graphics, location); @@ -382,11 +426,15 @@ public class OverlayRenderer extends MouseListener implements KeyListener { if (SwingUtilities.isRightMouseButton(mouseEvent)) { - overlay.setPreferredLocation(null); - overlay.setPreferredPosition(null); - saveOverlayPosition(overlay); - saveOverlayLocation(overlay); - rebuildOverlayLayers(); + // detached overlays have no place to reset back to + if (overlay.getPosition() != OverlayPosition.DETACHED) + { + overlay.setPreferredPosition(null); + overlay.setPreferredSize(null); + overlay.setPreferredLocation(null); + saveOverlay(overlay); + rebuildOverlayLayers(); + } } else { @@ -445,21 +493,26 @@ public class OverlayRenderer extends MouseListener implements KeyListener if (movedOverlay != null) { mousePosition.setLocation(-1, -1); - final OverlayBounds snapCorners = this.snapCorners.translated(-SNAP_CORNER_SIZE.width, -SNAP_CORNER_SIZE.height); - for (Rectangle snapCorner : snapCorners.getBounds()) + // do not snapcorner detached overlays + if (movedOverlay.getPosition() != OverlayPosition.DETACHED) { - if (snapCorner.contains(mouseEvent.getPoint())) + final OverlayBounds snapCorners = this.snapCorners.translated(-SNAP_CORNER_SIZE.width, -SNAP_CORNER_SIZE.height); + + for (Rectangle snapCorner : snapCorners.getBounds()) { - OverlayPosition position = snapCorners.fromBounds(snapCorner); - if (position == movedOverlay.getPosition()) + if (snapCorner.contains(mouseEvent.getPoint())) { - // overlay moves back to default position - position = null; + 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; } - movedOverlay.setPreferredPosition(position); - movedOverlay.setPreferredLocation(null); // from dragging - break; } } @@ -610,7 +663,7 @@ public class OverlayRenderer extends MouseListener implements KeyListener private void saveOverlayLocation(final Overlay overlay) { - final String key = overlay.getClass().getSimpleName() + OVERLAY_CONFIG_PREFERRED_LOCATION; + final String key = overlay.getName() + OVERLAY_CONFIG_PREFERRED_LOCATION; if (overlay.getPreferredLocation() != null) { configManager.setConfiguration( @@ -626,9 +679,27 @@ public class OverlayRenderer extends MouseListener implements KeyListener } } + private void saveOverlaySize(final Overlay overlay) + { + final String key = overlay.getName() + OVERLAY_CONFIG_PREFERRED_SIZE; + if (overlay.getPreferredSize() != null) + { + configManager.setConfiguration( + runeliteGroupName, + key, + overlay.getPreferredSize()); + } + else + { + configManager.unsetConfiguration( + runeliteGroupName, + key); + } + } + private void saveOverlayPosition(final Overlay overlay) { - final String key = overlay.getClass().getSimpleName() + OVERLAY_CONFIG_PREFERRED_POSITION; + final String key = overlay.getName() + OVERLAY_CONFIG_PREFERRED_POSITION; if (overlay.getPreferredPosition() != null) { configManager.setConfiguration( @@ -646,13 +717,19 @@ public class OverlayRenderer extends MouseListener implements KeyListener private Point loadOverlayLocation(final Overlay overlay) { - final String key = overlay.getClass().getSimpleName() + OVERLAY_CONFIG_PREFERRED_LOCATION; + final String key = overlay.getName() + OVERLAY_CONFIG_PREFERRED_LOCATION; return configManager.getConfiguration(runeliteGroupName, key, Point.class); } + private Dimension loadOverlaySize(final Overlay overlay) + { + final String key = overlay.getName() + OVERLAY_CONFIG_PREFERRED_SIZE; + return configManager.getConfiguration(runeliteGroupName, key, Dimension.class); + } + private OverlayPosition loadOverlayPosition(final Overlay overlay) { - final String locationKey = overlay.getClass().getSimpleName() + OVERLAY_CONFIG_PREFERRED_POSITION; + final String locationKey = overlay.getName() + OVERLAY_CONFIG_PREFERRED_POSITION; return configManager.getConfiguration(runeliteGroupName, locationKey, OverlayPosition.class); } } diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/panel_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/panel_icon.png new file mode 100644 index 0000000000..0204813fcf Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/panel_icon.png differ